kopia lustrzana https://gitlab.com/eliggett/wfview
Add audio resampler
rodzic
998381ac00
commit
a0f4a4deeb
223
audiohandler.cpp
223
audiohandler.cpp
|
@ -750,9 +750,13 @@ audioHandler::~audioHandler()
|
||||||
if (audioInput != Q_NULLPTR) {
|
if (audioInput != Q_NULLPTR) {
|
||||||
delete audioInput;
|
delete audioInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resampler) {
|
||||||
|
speex_resampler_destroy(resampler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, QString port)
|
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, QString port, quint8 resampleQuality)
|
||||||
{
|
{
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -760,7 +764,7 @@ bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16
|
||||||
/* Always use 16 bit 48K samples internally*/
|
/* Always use 16 bit 48K samples internally*/
|
||||||
format.setSampleSize(16);
|
format.setSampleSize(16);
|
||||||
format.setChannelCount(channels);
|
format.setChannelCount(channels);
|
||||||
format.setSampleRate(48000);
|
format.setSampleRate(INTERNAL_SAMPLE_RATE);
|
||||||
format.setCodec("audio/pcm");
|
format.setCodec("audio/pcm");
|
||||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||||
format.setSampleType(QAudioFormat::SignedInt);
|
format.setSampleType(QAudioFormat::SignedInt);
|
||||||
|
@ -770,7 +774,27 @@ bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16
|
||||||
this->isInput = isinput;
|
this->isInput = isinput;
|
||||||
this->radioSampleBits = bits;
|
this->radioSampleBits = bits;
|
||||||
this->radioSampleRate = samplerate;
|
this->radioSampleRate = samplerate;
|
||||||
this->chunkSize = this->radioSampleBits * 120;
|
this->radioChannels = channels;
|
||||||
|
|
||||||
|
//this->chunkSize = (INTERNAL_SAMPLE_RATE / 25) * (radioSampleBits / 8)/2;
|
||||||
|
|
||||||
|
this->chunkSize = 1920*radioChannels;
|
||||||
|
|
||||||
|
qDebug(logAudio()) << "Audio chunkSize: " << this->chunkSize;
|
||||||
|
|
||||||
|
int resample_error=0;
|
||||||
|
if (isinput) {
|
||||||
|
resampler = wf_resampler_init(radioChannels, INTERNAL_SAMPLE_RATE, samplerate, resampleQuality, &resample_error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resampler = wf_resampler_init(radioChannels, samplerate, INTERNAL_SAMPLE_RATE, resampleQuality, &resample_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
|
||||||
|
|
||||||
|
qDebug(logAudio()) << "wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen << " input " << isinput;
|
||||||
|
|
||||||
qDebug(logAudio()) << "Got audio port name: " << port;
|
qDebug(logAudio()) << "Got audio port name: " << port;
|
||||||
|
|
||||||
|
@ -869,8 +893,8 @@ void audioHandler::reinit()
|
||||||
delete audioOutput;
|
delete audioOutput;
|
||||||
audioOutput = Q_NULLPTR;
|
audioOutput = Q_NULLPTR;
|
||||||
audioOutput = new QAudioOutput(deviceInfo, format, this);
|
audioOutput = new QAudioOutput(deviceInfo, format, this);
|
||||||
audioOutput->setBufferSize((radioSampleRate/25)*(radioSampleBits/8)*2);
|
audioOutput->setBufferSize((radioSampleRate / 25) * (radioSampleBits / 8) * 2);
|
||||||
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
|
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
|
||||||
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,11 +953,18 @@ void audioHandler::stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="maxlen"></param>
|
||||||
|
/// <returns></returns>
|
||||||
qint64 audioHandler::readData(char* data, qint64 maxlen)
|
qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||||
{
|
{
|
||||||
// Calculate output length, always full samples
|
// Calculate output length, always full samples
|
||||||
int sentlen = 0;
|
int sentlen = 0;
|
||||||
|
|
||||||
|
|
||||||
//qDebug(logAudio()) << "Looking for: " << maxlen << " bytes";
|
//qDebug(logAudio()) << "Looking for: " << maxlen << " bytes";
|
||||||
|
|
||||||
// We must lock the mutex for the entire time that the buffer may be modified.
|
// We must lock the mutex for the entire time that the buffer may be modified.
|
||||||
|
@ -942,10 +973,7 @@ qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||||
if (!audioBuffer.isEmpty())
|
if (!audioBuffer.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Output buffer is ALWAYS 16 bit.
|
// Output buffer is ALWAYS 16 bit.
|
||||||
int divisor = 16 / radioSampleBits;
|
|
||||||
|
|
||||||
auto packet = audioBuffer.begin();
|
auto packet = audioBuffer.begin();
|
||||||
while (packet != audioBuffer.end() && sentlen < maxlen)
|
while (packet != audioBuffer.end() && sentlen < maxlen)
|
||||||
{
|
{
|
||||||
|
@ -956,44 +984,18 @@ qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||||
}
|
}
|
||||||
else if (packet->seq == lastSeq+1 || packet->seq <= lastSeq)
|
else if (packet->seq == lastSeq+1 || packet->seq <= lastSeq)
|
||||||
{
|
{
|
||||||
|
int send = qMin((int)maxlen-sentlen, packet->dataout.length() - packet->sent);
|
||||||
lastSeq = packet->seq;
|
lastSeq = packet->seq;
|
||||||
//qDebug(logAudio()) << "Packet " << hex << packet->seq << " arrived on time " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
|
//qDebug(logAudio()) << "Packet " << hex << packet->seq << " arrived on time " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
|
||||||
// Will this packet fit in the current buffer?
|
|
||||||
int send = qMin((int)((maxlen/divisor) - (sentlen/divisor)), packet->data.length() - packet->sent);
|
|
||||||
|
|
||||||
if (divisor == 2)
|
memcpy(data + sentlen, packet->dataout.constData() + packet->sent, send);
|
||||||
{
|
|
||||||
// Input buffer is 8bit and output buffer is 16bit
|
|
||||||
for (int f = 0; f < send; f++)
|
|
||||||
{
|
|
||||||
if (isUlaw)
|
|
||||||
qToLittleEndian<qint16>(ulaw_decode[(quint8)packet->data[f+packet->sent]], data + (f * 2 + sentlen));
|
|
||||||
else
|
|
||||||
qToLittleEndian<qint16>((qint16)(packet->data[f+packet->sent] << 8) - 32640, data + (f * 2 + sentlen));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (divisor == 1)
|
|
||||||
{
|
|
||||||
// 16 bit audio so just copy it in place.
|
|
||||||
//qDebug(logAudio()) << "Adding packet to buffer:" << (*packet).seq << ": " << (*packet).data.length()-(*packet).sent;
|
|
||||||
memcpy(data+sentlen, packet->data.constData()+packet->sent, send);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//qDebug(logAudio()) << "Invalid number of bits in audio " << radioSampleBits;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sentlen = sentlen + (send * divisor);
|
sentlen = sentlen + send;
|
||||||
|
|
||||||
if (send == packet->data.length())
|
if (send == packet->dataout.length())
|
||||||
{
|
{
|
||||||
lastSeq = packet->seq;
|
//qDebug(logAudio()) << "Get next packet";
|
||||||
packet = audioBuffer.erase(packet); // returns next packet
|
packet = audioBuffer.erase(packet); // returns next packet
|
||||||
if (maxlen - sentlen == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (send == 0)
|
else if (send == 0)
|
||||||
{
|
{
|
||||||
|
@ -1019,9 +1021,7 @@ qint64 audioHandler::readData(char* data, qint64 maxlen)
|
||||||
|
|
||||||
qint64 audioHandler::writeData(const char* data, qint64 len)
|
qint64 audioHandler::writeData(const char* data, qint64 len)
|
||||||
{
|
{
|
||||||
int multiplier = (int)16 / radioSampleBits;
|
|
||||||
qint64 sentlen = 0;
|
qint64 sentlen = 0;
|
||||||
int tosend = 0;
|
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
audioPacket *current;
|
audioPacket *current;
|
||||||
|
|
||||||
|
@ -1041,36 +1041,15 @@ qint64 audioHandler::writeData(const char* data, qint64 len)
|
||||||
}
|
}
|
||||||
current = &audioBuffer.last();
|
current = &audioBuffer.last();
|
||||||
|
|
||||||
tosend = qMin((int)((len - sentlen)/multiplier), (int)chunkSize-current->sent);
|
int send = qMin((int)(len - sentlen), (int)chunkSize-current->sent);
|
||||||
|
|
||||||
if (radioSampleBits == 8) {
|
current->datain.append(QByteArray::fromRawData(data + sentlen, send ));
|
||||||
int f = 0;
|
|
||||||
while (f < tosend)
|
sentlen = sentlen + send;
|
||||||
{
|
|
||||||
quint8 outdata=0;
|
|
||||||
if (isUlaw) {
|
|
||||||
qint16 enc = qFromLittleEndian<quint16>(data + ((f * multiplier) + sentlen));
|
|
||||||
if (enc >= 0)
|
|
||||||
outdata=ulaw_encode[enc];
|
|
||||||
else
|
|
||||||
outdata=0x7f & ulaw_encode[-enc];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
outdata = (quint8)(((qFromLittleEndian<qint16>((data + ((f * multiplier) + sentlen))) >> 8) ^ 0x80) & 0xff);
|
|
||||||
}
|
|
||||||
current->data.append((char)outdata);
|
|
||||||
f++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (radioSampleBits == 16)
|
|
||||||
{
|
|
||||||
current->data.append(QByteArray::fromRawData(data + sentlen, tosend ));
|
|
||||||
}
|
|
||||||
|
|
||||||
sentlen = sentlen + (tosend * multiplier);
|
|
||||||
current->seq = 0; // Not used in TX
|
current->seq = 0; // Not used in TX
|
||||||
current->time = QTime::currentTime();
|
current->time = QTime::currentTime();
|
||||||
current->sent = current->data.length();
|
current->sent = current->datain.length();
|
||||||
|
|
||||||
if (current->sent == chunkSize)
|
if (current->sent == chunkSize)
|
||||||
{
|
{
|
||||||
|
@ -1082,7 +1061,6 @@ qint64 audioHandler::writeData(const char* data, qint64 len)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (sentlen); // Always return the same number as we received
|
return (sentlen); // Always return the same number as we received
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1139,10 +1117,59 @@ void audioHandler::stateChanged(QAudio::State state)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void audioHandler::incomingAudio(const audioPacket data)
|
void audioHandler::incomingAudio(audioPacket data)
|
||||||
{
|
{
|
||||||
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
|
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
|
|
||||||
|
// Incoming data is 8bits?
|
||||||
|
if (radioSampleBits == 8)
|
||||||
|
{
|
||||||
|
QByteArray inPacket((int)data.datain.length() * 2, (char)0xff);
|
||||||
|
qint16* in = (qint16*)inPacket.data();
|
||||||
|
for (int f = 0; f < data.datain.length(); f++)
|
||||||
|
{
|
||||||
|
if (isUlaw)
|
||||||
|
{
|
||||||
|
in[f] = ulaw_decode[(quint8)data.datain[f]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Convert 8-bit sample to 16-bit
|
||||||
|
in[f] = (qint16)(((quint8)data.datain[f] << 8) - 32640);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.datain = inPacket; // Replace incoming data with converted.
|
||||||
|
}
|
||||||
|
|
||||||
|
//qDebug(logAudio()) << "Adding packet to buffer:" << (*packet).seq << ": " << inPacket.length();
|
||||||
|
|
||||||
|
/* We now have an array of 16bit samples in the NATIVE samplerate of the radio
|
||||||
|
If the radio sample rate is below 48000, we need to resample.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ratioDen != 1) {
|
||||||
|
|
||||||
|
// We need to resample
|
||||||
|
quint32 outFrames = ((data.datain.length() / 2) * ratioDen) / radioChannels;
|
||||||
|
quint32 inFrames = (data.datain.length() / 2) / radioChannels;
|
||||||
|
data.dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size.
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
if (this->radioChannels == 1) {
|
||||||
|
err = wf_resampler_process_int(resampler, 0, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err = wf_resampler_process_interleaved_int(resampler, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
qDebug(logAudio()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.dataout = data.datain;
|
||||||
|
}
|
||||||
|
|
||||||
audioBuffer.push_back(data);
|
audioBuffer.push_back(data);
|
||||||
|
|
||||||
// Sort the buffer by seq number. This is important and audio packets may have arrived out-of-order
|
// Sort the buffer by seq number. This is important and audio packets may have arrived out-of-order
|
||||||
|
@ -1193,9 +1220,63 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
|
||||||
packet = audioBuffer.erase(packet); // returns next packet
|
packet = audioBuffer.erase(packet); // returns next packet
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (packet->data.length() == chunkSize && ret.length() == 0)
|
if (packet->datain.length() == chunkSize && ret.length() == 0)
|
||||||
{
|
{
|
||||||
ret.append(packet->data);
|
/* We now have an array of samples in the computer native format (48000)
|
||||||
|
If the radio sample rate is below 48000, we need to resample.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ratioNum != 1)
|
||||||
|
{
|
||||||
|
// We need to resample (we are STILL 16 bit!)
|
||||||
|
quint32 outFrames = ((packet->datain.length() / 2) / ratioNum) / radioChannels;
|
||||||
|
quint32 inFrames = (packet->datain.length() / 2) / radioChannels;
|
||||||
|
packet->dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size.
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
if (this->radioChannels == 1) {
|
||||||
|
err = wf_resampler_process_int(resampler, 0, (const qint16*)packet->datain.constData(), &inFrames, (qint16*)packet->dataout.data(), &outFrames);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err = wf_resampler_process_interleaved_int(resampler, (const qint16*)packet->datain.constData(), &inFrames, (qint16*)packet->dataout.data(), &outFrames);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
qDebug(logAudio()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||||
|
}
|
||||||
|
//qDebug(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
|
||||||
|
//qDebug(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length();
|
||||||
|
if (radioSampleBits == 8)
|
||||||
|
{
|
||||||
|
packet->datain = packet->dataout; // Copy packet back to input buffer.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (radioSampleBits == 16 ){
|
||||||
|
// Only copy buffer if radioSampleBits is 16, as it will be handled below otherwise.
|
||||||
|
packet->dataout = packet->datain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to convert 16-bit to 8-bit?
|
||||||
|
if (radioSampleBits == 8) {
|
||||||
|
packet->dataout.resize(packet->datain.length() / 2);
|
||||||
|
qint16* in = (qint16*)packet->datain.data();
|
||||||
|
for (int f = 0; f < packet->dataout.length(); f++)
|
||||||
|
{
|
||||||
|
quint8 outdata = 0;
|
||||||
|
if (isUlaw) {
|
||||||
|
qint16 enc = qFromLittleEndian<quint16>(in + f);
|
||||||
|
if (enc >= 0)
|
||||||
|
outdata = ulaw_encode[enc];
|
||||||
|
else
|
||||||
|
outdata = 0x7f & ulaw_encode[-enc];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outdata = (quint8)(((qFromLittleEndian<qint16>(in + f) >> 8) ^ 0x80) & 0xff);
|
||||||
|
}
|
||||||
|
packet->dataout[f] = (char)outdata;
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.append(packet->dataout);
|
||||||
packet = audioBuffer.erase(packet); // returns next packet
|
packet = audioBuffer.erase(packet); // returns next packet
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -15,17 +15,20 @@
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
#include "resampler/speex_resampler.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
//#define BUFFER_SIZE (32*1024)
|
//#define BUFFER_SIZE (32*1024)
|
||||||
|
|
||||||
|
#define INTERNAL_SAMPLE_RATE 48000
|
||||||
|
|
||||||
struct audioPacket {
|
struct audioPacket {
|
||||||
quint16 seq;
|
quint16 seq;
|
||||||
QTime time;
|
QTime time;
|
||||||
quint16 sent;
|
quint16 sent;
|
||||||
QByteArray data;
|
QByteArray datain;
|
||||||
|
QByteArray dataout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +57,7 @@ public:
|
||||||
void getNextAudioChunk(QByteArray &data);
|
void getNextAudioChunk(QByteArray &data);
|
||||||
bool isChunkAvailable();
|
bool isChunkAvailable();
|
||||||
public slots:
|
public slots:
|
||||||
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, QString port);
|
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, QString port, quint8 resampleQuality);
|
||||||
void incomingAudio(const audioPacket data);
|
void incomingAudio(const audioPacket data);
|
||||||
void changeLatency(const quint16 newSize);
|
void changeLatency(const quint16 newSize);
|
||||||
|
|
||||||
|
@ -88,7 +91,12 @@ private:
|
||||||
QAudioDeviceInfo deviceInfo;
|
QAudioDeviceInfo deviceInfo;
|
||||||
quint16 radioSampleRate;
|
quint16 radioSampleRate;
|
||||||
quint8 radioSampleBits;
|
quint8 radioSampleBits;
|
||||||
|
quint8 radioChannels;
|
||||||
QVector<audioPacket> audioBuffer;
|
QVector<audioPacket> audioBuffer;
|
||||||
|
|
||||||
|
SpeexResamplerState* resampler;
|
||||||
|
unsigned int ratioNum;
|
||||||
|
unsigned int ratioDen;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUDIOHANDLER_H
|
#endif // AUDIOHANDLER_H
|
||||||
|
|
|
@ -294,7 +294,8 @@ typedef union conninfo_packet {
|
||||||
quint32 civport; // 0x7c
|
quint32 civport; // 0x7c
|
||||||
quint32 audioport; // 0x80
|
quint32 audioport; // 0x80
|
||||||
quint32 txbuffer; // 0x84
|
quint32 txbuffer; // 0x84
|
||||||
char unusedl[8]; // 0x88
|
quint8 convert; // 0x88
|
||||||
|
char unusedl[7]; // 0x89
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* Copyright (C) 2003 Jean-Marc Valin */
|
||||||
|
/**
|
||||||
|
@file arch.h
|
||||||
|
@brief Various architecture definitions Speex
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARCH_H
|
||||||
|
#define ARCH_H
|
||||||
|
|
||||||
|
/* A couple test to catch stupid option combinations */
|
||||||
|
#ifdef FIXED_POINT
|
||||||
|
|
||||||
|
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
|
||||||
|
#error Make up your mind. What CPU do you have?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
|
||||||
|
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OUTSIDE_SPEEX
|
||||||
|
#include "speex/speexdsp_types.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
|
||||||
|
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
|
||||||
|
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
|
||||||
|
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
|
||||||
|
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
|
||||||
|
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
|
||||||
|
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
|
||||||
|
|
||||||
|
#ifdef FIXED_POINT
|
||||||
|
|
||||||
|
typedef spx_int16_t spx_word16_t;
|
||||||
|
typedef spx_int32_t spx_word32_t;
|
||||||
|
typedef spx_word32_t spx_mem_t;
|
||||||
|
typedef spx_word16_t spx_coef_t;
|
||||||
|
typedef spx_word16_t spx_lsp_t;
|
||||||
|
typedef spx_word32_t spx_sig_t;
|
||||||
|
|
||||||
|
#define Q15ONE 32767
|
||||||
|
|
||||||
|
#define LPC_SCALING 8192
|
||||||
|
#define SIG_SCALING 16384
|
||||||
|
#define LSP_SCALING 8192.
|
||||||
|
#define GAMMA_SCALING 32768.
|
||||||
|
#define GAIN_SCALING 64
|
||||||
|
#define GAIN_SCALING_1 0.015625
|
||||||
|
|
||||||
|
#define LPC_SHIFT 13
|
||||||
|
#define LSP_SHIFT 13
|
||||||
|
#define SIG_SHIFT 14
|
||||||
|
#define GAIN_SHIFT 6
|
||||||
|
|
||||||
|
#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
|
||||||
|
|
||||||
|
#define VERY_SMALL 0
|
||||||
|
#define VERY_LARGE32 ((spx_word32_t)2147483647)
|
||||||
|
#define VERY_LARGE16 ((spx_word16_t)32767)
|
||||||
|
#define Q15_ONE ((spx_word16_t)32767)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef FIXED_DEBUG
|
||||||
|
#include "fixed_debug.h"
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "fixed_generic.h"
|
||||||
|
|
||||||
|
#ifdef ARM5E_ASM
|
||||||
|
#include "fixed_arm5e.h"
|
||||||
|
#elif defined (ARM4_ASM)
|
||||||
|
#include "fixed_arm4.h"
|
||||||
|
#elif defined (BFIN_ASM)
|
||||||
|
#include "fixed_bfin.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef float spx_mem_t;
|
||||||
|
typedef float spx_coef_t;
|
||||||
|
typedef float spx_lsp_t;
|
||||||
|
typedef float spx_sig_t;
|
||||||
|
typedef float spx_word16_t;
|
||||||
|
typedef float spx_word32_t;
|
||||||
|
|
||||||
|
#define Q15ONE 1.0f
|
||||||
|
#define LPC_SCALING 1.f
|
||||||
|
#define SIG_SCALING 1.f
|
||||||
|
#define LSP_SCALING 1.f
|
||||||
|
#define GAMMA_SCALING 1.f
|
||||||
|
#define GAIN_SCALING 1.f
|
||||||
|
#define GAIN_SCALING_1 1.f
|
||||||
|
|
||||||
|
|
||||||
|
#define VERY_SMALL 1e-15f
|
||||||
|
#define VERY_LARGE32 1e15f
|
||||||
|
#define VERY_LARGE16 1e15f
|
||||||
|
#define Q15_ONE ((spx_word16_t)1.f)
|
||||||
|
|
||||||
|
#define QCONST16(x,bits) (x)
|
||||||
|
#define QCONST32(x,bits) (x)
|
||||||
|
|
||||||
|
#define NEG16(x) (-(x))
|
||||||
|
#define NEG32(x) (-(x))
|
||||||
|
#define EXTRACT16(x) (x)
|
||||||
|
#define EXTEND32(x) (x)
|
||||||
|
#define SHR16(a,shift) (a)
|
||||||
|
#define SHL16(a,shift) (a)
|
||||||
|
#define SHR32(a,shift) (a)
|
||||||
|
#define SHL32(a,shift) (a)
|
||||||
|
#define PSHR16(a,shift) (a)
|
||||||
|
#define PSHR32(a,shift) (a)
|
||||||
|
#define VSHR32(a,shift) (a)
|
||||||
|
#define SATURATE16(x,a) (x)
|
||||||
|
#define SATURATE32(x,a) (x)
|
||||||
|
#define SATURATE32PSHR(x,shift,a) (x)
|
||||||
|
|
||||||
|
#define PSHR(a,shift) (a)
|
||||||
|
#define SHR(a,shift) (a)
|
||||||
|
#define SHL(a,shift) (a)
|
||||||
|
#define SATURATE(x,a) (x)
|
||||||
|
|
||||||
|
#define ADD16(a,b) ((a)+(b))
|
||||||
|
#define SUB16(a,b) ((a)-(b))
|
||||||
|
#define ADD32(a,b) ((a)+(b))
|
||||||
|
#define SUB32(a,b) ((a)-(b))
|
||||||
|
#define MULT16_16_16(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
|
||||||
|
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
|
||||||
|
|
||||||
|
#define MULT16_32_Q11(a,b) ((a)*(b))
|
||||||
|
#define MULT16_32_Q13(a,b) ((a)*(b))
|
||||||
|
#define MULT16_32_Q14(a,b) ((a)*(b))
|
||||||
|
#define MULT16_32_Q15(a,b) ((a)*(b))
|
||||||
|
#define MULT16_32_P15(a,b) ((a)*(b))
|
||||||
|
|
||||||
|
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
|
||||||
|
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
|
||||||
|
|
||||||
|
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
|
||||||
|
#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
|
||||||
|
#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
|
||||||
|
#define MULT16_16_Q11_32(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_Q13(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_Q14(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_Q15(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_P15(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_P13(a,b) ((a)*(b))
|
||||||
|
#define MULT16_16_P14(a,b) ((a)*(b))
|
||||||
|
|
||||||
|
#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
|
||||||
|
#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
|
||||||
|
#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
|
||||||
|
#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
|
||||||
|
|
||||||
|
#define WORD2INT(x) ((x) < -32767.5f ? -32768 : \
|
||||||
|
((x) > 32766.5f ? 32767 : (spx_int16_t)floor(.5 + (x))))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
|
||||||
|
|
||||||
|
/* 2 on TI C5x DSP */
|
||||||
|
#define BYTES_PER_CHAR 2
|
||||||
|
#define BITS_PER_CHAR 16
|
||||||
|
#define LOG2_BITS_PER_CHAR 4
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define BYTES_PER_CHAR 1
|
||||||
|
#define BITS_PER_CHAR 8
|
||||||
|
#define LOG2_BITS_PER_CHAR 3
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef FIXED_DEBUG
|
||||||
|
extern long long spx_mips;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,128 @@
|
||||||
|
/* Copyright (C) 2007-2008 Jean-Marc Valin
|
||||||
|
* Copyright (C) 2008 Thorvald Natvig
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
@file resample_sse.h
|
||||||
|
@brief Resampler functions (SSE version)
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
|
||||||
|
#define OVERRIDE_INNER_PRODUCT_SINGLE
|
||||||
|
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float ret;
|
||||||
|
__m128 sum = _mm_setzero_ps();
|
||||||
|
for (i=0;i<len;i+=8)
|
||||||
|
{
|
||||||
|
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
|
||||||
|
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
|
||||||
|
}
|
||||||
|
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
|
||||||
|
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
|
||||||
|
_mm_store_ss(&ret, sum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
|
||||||
|
static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
|
||||||
|
int i;
|
||||||
|
float ret;
|
||||||
|
__m128 sum = _mm_setzero_ps();
|
||||||
|
__m128 f = _mm_loadu_ps(frac);
|
||||||
|
for(i=0;i<len;i+=2)
|
||||||
|
{
|
||||||
|
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
|
||||||
|
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
|
||||||
|
}
|
||||||
|
sum = _mm_mul_ps(f, sum);
|
||||||
|
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
|
||||||
|
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
|
||||||
|
_mm_store_ss(&ret, sum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __SSE2__
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#define OVERRIDE_INNER_PRODUCT_DOUBLE
|
||||||
|
|
||||||
|
static inline double inner_product_double(const float *a, const float *b, unsigned int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double ret;
|
||||||
|
__m128d sum = _mm_setzero_pd();
|
||||||
|
__m128 t;
|
||||||
|
for (i=0;i<len;i+=8)
|
||||||
|
{
|
||||||
|
t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
|
||||||
|
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
|
||||||
|
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
||||||
|
|
||||||
|
t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
|
||||||
|
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
|
||||||
|
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
||||||
|
}
|
||||||
|
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
|
||||||
|
_mm_store_sd(&ret, sum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
|
||||||
|
static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
|
||||||
|
int i;
|
||||||
|
double ret;
|
||||||
|
__m128d sum;
|
||||||
|
__m128d sum1 = _mm_setzero_pd();
|
||||||
|
__m128d sum2 = _mm_setzero_pd();
|
||||||
|
__m128 f = _mm_loadu_ps(frac);
|
||||||
|
__m128d f1 = _mm_cvtps_pd(f);
|
||||||
|
__m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
|
||||||
|
__m128 t;
|
||||||
|
for(i=0;i<len;i+=2)
|
||||||
|
{
|
||||||
|
t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
|
||||||
|
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
|
||||||
|
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
||||||
|
|
||||||
|
t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
|
||||||
|
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
|
||||||
|
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
||||||
|
}
|
||||||
|
sum1 = _mm_mul_pd(f1, sum1);
|
||||||
|
sum2 = _mm_mul_pd(f2, sum2);
|
||||||
|
sum = _mm_add_pd(sum1, sum2);
|
||||||
|
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
|
||||||
|
_mm_store_sd(&ret, sum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,344 @@
|
||||||
|
/* Copyright (C) 2007 Jean-Marc Valin
|
||||||
|
|
||||||
|
File: speex_resampler.h
|
||||||
|
Resampling code
|
||||||
|
|
||||||
|
The design goals of this code are:
|
||||||
|
- Very fast algorithm
|
||||||
|
- Low memory requirement
|
||||||
|
- Good *perceptual* quality (and not best SNR)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SPEEX_RESAMPLER_H
|
||||||
|
#define SPEEX_RESAMPLER_H
|
||||||
|
|
||||||
|
#ifdef OUTSIDE_SPEEX
|
||||||
|
|
||||||
|
/********* WARNING: MENTAL SANITY ENDS HERE *************/
|
||||||
|
|
||||||
|
/* If the resampler is defined outside of Speex, we change the symbol names so that
|
||||||
|
there won't be any clash if linking with Speex later on. */
|
||||||
|
|
||||||
|
/* #define RANDOM_PREFIX your software name here */
|
||||||
|
#ifndef RANDOM_PREFIX
|
||||||
|
#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CAT_PREFIX2(a,b) a ## b
|
||||||
|
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
|
||||||
|
|
||||||
|
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
|
||||||
|
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
|
||||||
|
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
|
||||||
|
#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
|
||||||
|
#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
|
||||||
|
#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
|
||||||
|
#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
|
||||||
|
#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
|
||||||
|
#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
|
||||||
|
#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
|
||||||
|
#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
|
||||||
|
#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
|
||||||
|
#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
|
||||||
|
#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
|
||||||
|
#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
|
||||||
|
#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
|
||||||
|
#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
|
||||||
|
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
|
||||||
|
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
|
||||||
|
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
|
||||||
|
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
|
||||||
|
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
|
||||||
|
|
||||||
|
#define spx_int16_t short
|
||||||
|
#define spx_int32_t int
|
||||||
|
#define spx_uint16_t unsigned short
|
||||||
|
#define spx_uint32_t unsigned int
|
||||||
|
|
||||||
|
#define speex_assert(cond)
|
||||||
|
|
||||||
|
#else /* OUTSIDE_SPEEX */
|
||||||
|
|
||||||
|
#include "speexdsp_types.h"
|
||||||
|
|
||||||
|
#endif /* OUTSIDE_SPEEX */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SPEEX_RESAMPLER_QUALITY_MAX 10
|
||||||
|
#define SPEEX_RESAMPLER_QUALITY_MIN 0
|
||||||
|
#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
|
||||||
|
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
|
||||||
|
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RESAMPLER_ERR_SUCCESS = 0,
|
||||||
|
RESAMPLER_ERR_ALLOC_FAILED = 1,
|
||||||
|
RESAMPLER_ERR_BAD_STATE = 2,
|
||||||
|
RESAMPLER_ERR_INVALID_ARG = 3,
|
||||||
|
RESAMPLER_ERR_PTR_OVERLAP = 4,
|
||||||
|
RESAMPLER_ERR_OVERFLOW = 5,
|
||||||
|
|
||||||
|
RESAMPLER_ERR_MAX_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpeexResamplerState_;
|
||||||
|
typedef struct SpeexResamplerState_ SpeexResamplerState;
|
||||||
|
|
||||||
|
/** Create a new resampler with integer input and output rates.
|
||||||
|
* @param nb_channels Number of channels to be processed
|
||||||
|
* @param in_rate Input sampling rate (integer number of Hz).
|
||||||
|
* @param out_rate Output sampling rate (integer number of Hz).
|
||||||
|
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
|
||||||
|
* and 10 has very high quality.
|
||||||
|
* @return Newly created resampler state
|
||||||
|
* @retval NULL Error: not enough memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
|
||||||
|
spx_uint32_t in_rate,
|
||||||
|
spx_uint32_t out_rate,
|
||||||
|
int quality,
|
||||||
|
int *err);
|
||||||
|
|
||||||
|
/** Create a new resampler with fractional input/output rates. The sampling
|
||||||
|
* rate ratio is an arbitrary rational number with both the numerator and
|
||||||
|
* denominator being 32-bit integers.
|
||||||
|
* @param nb_channels Number of channels to be processed
|
||||||
|
* @param ratio_num Numerator of the sampling rate ratio
|
||||||
|
* @param ratio_den Denominator of the sampling rate ratio
|
||||||
|
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
|
||||||
|
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
|
||||||
|
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
|
||||||
|
* and 10 has very high quality.
|
||||||
|
* @return Newly created resampler state
|
||||||
|
* @retval NULL Error: not enough memory
|
||||||
|
*/
|
||||||
|
SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
|
||||||
|
spx_uint32_t ratio_num,
|
||||||
|
spx_uint32_t ratio_den,
|
||||||
|
spx_uint32_t in_rate,
|
||||||
|
spx_uint32_t out_rate,
|
||||||
|
int quality,
|
||||||
|
int *err);
|
||||||
|
|
||||||
|
/** Destroy a resampler state.
|
||||||
|
* @param st Resampler state
|
||||||
|
*/
|
||||||
|
void speex_resampler_destroy(SpeexResamplerState *st);
|
||||||
|
|
||||||
|
/** Resample a float array. The input and output buffers must *not* overlap.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param channel_index Index of the channel to process for the multi-channel
|
||||||
|
* base (0 otherwise)
|
||||||
|
* @param in Input buffer
|
||||||
|
* @param in_len Number of input samples in the input buffer. Returns the
|
||||||
|
* number of samples processed
|
||||||
|
* @param out Output buffer
|
||||||
|
* @param out_len Size of the output buffer. Returns the number of samples written
|
||||||
|
*/
|
||||||
|
int speex_resampler_process_float(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t channel_index,
|
||||||
|
const float *in,
|
||||||
|
spx_uint32_t *in_len,
|
||||||
|
float *out,
|
||||||
|
spx_uint32_t *out_len);
|
||||||
|
|
||||||
|
/** Resample an int array. The input and output buffers must *not* overlap.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param channel_index Index of the channel to process for the multi-channel
|
||||||
|
* base (0 otherwise)
|
||||||
|
* @param in Input buffer
|
||||||
|
* @param in_len Number of input samples in the input buffer. Returns the number
|
||||||
|
* of samples processed
|
||||||
|
* @param out Output buffer
|
||||||
|
* @param out_len Size of the output buffer. Returns the number of samples written
|
||||||
|
*/
|
||||||
|
int speex_resampler_process_int(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t channel_index,
|
||||||
|
const spx_int16_t *in,
|
||||||
|
spx_uint32_t *in_len,
|
||||||
|
spx_int16_t *out,
|
||||||
|
spx_uint32_t *out_len);
|
||||||
|
|
||||||
|
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param in Input buffer
|
||||||
|
* @param in_len Number of input samples in the input buffer. Returns the number
|
||||||
|
* of samples processed. This is all per-channel.
|
||||||
|
* @param out Output buffer
|
||||||
|
* @param out_len Size of the output buffer. Returns the number of samples written.
|
||||||
|
* This is all per-channel.
|
||||||
|
*/
|
||||||
|
int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
|
||||||
|
const float *in,
|
||||||
|
spx_uint32_t *in_len,
|
||||||
|
float *out,
|
||||||
|
spx_uint32_t *out_len);
|
||||||
|
|
||||||
|
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param in Input buffer
|
||||||
|
* @param in_len Number of input samples in the input buffer. Returns the number
|
||||||
|
* of samples processed. This is all per-channel.
|
||||||
|
* @param out Output buffer
|
||||||
|
* @param out_len Size of the output buffer. Returns the number of samples written.
|
||||||
|
* This is all per-channel.
|
||||||
|
*/
|
||||||
|
int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
|
||||||
|
const spx_int16_t *in,
|
||||||
|
spx_uint32_t *in_len,
|
||||||
|
spx_int16_t *out,
|
||||||
|
spx_uint32_t *out_len);
|
||||||
|
|
||||||
|
/** Set (change) the input/output sampling rates (integer value).
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param in_rate Input sampling rate (integer number of Hz).
|
||||||
|
* @param out_rate Output sampling rate (integer number of Hz).
|
||||||
|
*/
|
||||||
|
int speex_resampler_set_rate(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t in_rate,
|
||||||
|
spx_uint32_t out_rate);
|
||||||
|
|
||||||
|
/** Get the current input/output sampling rates (integer value).
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param in_rate Input sampling rate (integer number of Hz) copied.
|
||||||
|
* @param out_rate Output sampling rate (integer number of Hz) copied.
|
||||||
|
*/
|
||||||
|
void speex_resampler_get_rate(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t *in_rate,
|
||||||
|
spx_uint32_t *out_rate);
|
||||||
|
|
||||||
|
/** Set (change) the input/output sampling rates and resampling ratio
|
||||||
|
* (fractional values in Hz supported).
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param ratio_num Numerator of the sampling rate ratio
|
||||||
|
* @param ratio_den Denominator of the sampling rate ratio
|
||||||
|
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
|
||||||
|
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
|
||||||
|
*/
|
||||||
|
int speex_resampler_set_rate_frac(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t ratio_num,
|
||||||
|
spx_uint32_t ratio_den,
|
||||||
|
spx_uint32_t in_rate,
|
||||||
|
spx_uint32_t out_rate);
|
||||||
|
|
||||||
|
/** Get the current resampling ratio. This will be reduced to the least
|
||||||
|
* common denominator.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param ratio_num Numerator of the sampling rate ratio copied
|
||||||
|
* @param ratio_den Denominator of the sampling rate ratio copied
|
||||||
|
*/
|
||||||
|
void speex_resampler_get_ratio(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t *ratio_num,
|
||||||
|
spx_uint32_t *ratio_den);
|
||||||
|
|
||||||
|
/** Set (change) the conversion quality.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param quality Resampling quality between 0 and 10, where 0 has poor
|
||||||
|
* quality and 10 has very high quality.
|
||||||
|
*/
|
||||||
|
int speex_resampler_set_quality(SpeexResamplerState *st,
|
||||||
|
int quality);
|
||||||
|
|
||||||
|
/** Get the conversion quality.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param quality Resampling quality between 0 and 10, where 0 has poor
|
||||||
|
* quality and 10 has very high quality.
|
||||||
|
*/
|
||||||
|
void speex_resampler_get_quality(SpeexResamplerState *st,
|
||||||
|
int *quality);
|
||||||
|
|
||||||
|
/** Set (change) the input stride.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param stride Input stride
|
||||||
|
*/
|
||||||
|
void speex_resampler_set_input_stride(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t stride);
|
||||||
|
|
||||||
|
/** Get the input stride.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param stride Input stride copied
|
||||||
|
*/
|
||||||
|
void speex_resampler_get_input_stride(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t *stride);
|
||||||
|
|
||||||
|
/** Set (change) the output stride.
|
||||||
|
* @param st Resampler state
|
||||||
|
* @param stride Output stride
|
||||||
|
*/
|
||||||
|
void speex_resampler_set_output_stride(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t stride);
|
||||||
|
|
||||||
|
/** Get the output stride.
|
||||||
|
* @param st Resampler state copied
|
||||||
|
* @param stride Output stride
|
||||||
|
*/
|
||||||
|
void speex_resampler_get_output_stride(SpeexResamplerState *st,
|
||||||
|
spx_uint32_t *stride);
|
||||||
|
|
||||||
|
/** Get the latency introduced by the resampler measured in input samples.
|
||||||
|
* @param st Resampler state
|
||||||
|
*/
|
||||||
|
int speex_resampler_get_input_latency(SpeexResamplerState *st);
|
||||||
|
|
||||||
|
/** Get the latency introduced by the resampler measured in output samples.
|
||||||
|
* @param st Resampler state
|
||||||
|
*/
|
||||||
|
int speex_resampler_get_output_latency(SpeexResamplerState *st);
|
||||||
|
|
||||||
|
/** Make sure that the first samples to go out of the resamplers don't have
|
||||||
|
* leading zeros. This is only useful before starting to use a newly created
|
||||||
|
* resampler. It is recommended to use that when resampling an audio file, as
|
||||||
|
* it will generate a file with the same length. For real-time processing,
|
||||||
|
* it is probably easier not to use this call (so that the output duration
|
||||||
|
* is the same for the first frame).
|
||||||
|
* @param st Resampler state
|
||||||
|
*/
|
||||||
|
int speex_resampler_skip_zeros(SpeexResamplerState *st);
|
||||||
|
|
||||||
|
/** Reset a resampler so a new (unrelated) stream can be processed.
|
||||||
|
* @param st Resampler state
|
||||||
|
*/
|
||||||
|
int speex_resampler_reset_mem(SpeexResamplerState *st);
|
||||||
|
|
||||||
|
/** Returns the English meaning for an error code
|
||||||
|
* @param err Error code
|
||||||
|
* @return English string
|
||||||
|
*/
|
||||||
|
const char *speex_resampler_strerror(int err);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,7 +15,8 @@ udpHandler::udpHandler(udpPreferences prefs) :
|
||||||
rxCodec(prefs.audioRXCodec),
|
rxCodec(prefs.audioRXCodec),
|
||||||
txCodec(prefs.audioTXCodec),
|
txCodec(prefs.audioTXCodec),
|
||||||
audioInputPort(prefs.audioInput),
|
audioInputPort(prefs.audioInput),
|
||||||
audioOutputPort(prefs.audioOutput)
|
audioOutputPort(prefs.audioOutput),
|
||||||
|
resampleQuality(prefs.resampleQuality)
|
||||||
{
|
{
|
||||||
|
|
||||||
this->port = this->controlPort;
|
this->port = this->controlPort;
|
||||||
|
@ -150,6 +151,8 @@ void udpHandler::dataReceived()
|
||||||
control_packet_t in = (control_packet_t)r.constData();
|
control_packet_t in = (control_packet_t)r.constData();
|
||||||
if (in->type == 0x04) {
|
if (in->type == 0x04) {
|
||||||
// If timer is active, stop it as they are obviously there!
|
// If timer is active, stop it as they are obviously there!
|
||||||
|
qDebug(logUdp()) << this->metaObject()->className() << ": Received I am here from: " <<datagram.senderAddress();
|
||||||
|
|
||||||
if (areYouThereTimer->isActive()) {
|
if (areYouThereTimer->isActive()) {
|
||||||
// send ping packets every second
|
// send ping packets every second
|
||||||
areYouThereTimer->stop();
|
areYouThereTimer->stop();
|
||||||
|
@ -305,7 +308,7 @@ void udpHandler::dataReceived()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
civ = new udpCivData(localIP, radioIP, civPort);
|
civ = new udpCivData(localIP, radioIP, civPort);
|
||||||
audio = new udpAudio(localIP, radioIP, audioPort, rxLatency, txLatency, rxSampleRate, rxCodec, txSampleRate, txCodec, audioOutputPort, audioInputPort);
|
audio = new udpAudio(localIP, radioIP, audioPort, rxLatency, txLatency, rxSampleRate, rxCodec, txSampleRate, txCodec, audioOutputPort, audioInputPort,resampleQuality);
|
||||||
|
|
||||||
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
||||||
QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
|
QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
|
||||||
|
@ -399,7 +402,7 @@ void udpHandler::sendRequestStream()
|
||||||
p.civport = qToBigEndian((quint32)civPort);
|
p.civport = qToBigEndian((quint32)civPort);
|
||||||
p.audioport = qToBigEndian((quint32)audioPort);
|
p.audioport = qToBigEndian((quint32)audioPort);
|
||||||
p.txbuffer = qToBigEndian((quint32)txLatency);
|
p.txbuffer = qToBigEndian((quint32)txLatency);
|
||||||
|
p.convert = 1;
|
||||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -642,7 +645,7 @@ void udpCivData::dataReceived()
|
||||||
|
|
||||||
|
|
||||||
// Audio stream
|
// Audio stream
|
||||||
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QString outputPort, QString inputPort)
|
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QString outputPort, QString inputPort,quint8 resampleQuality)
|
||||||
{
|
{
|
||||||
qDebug(logUdp()) << "Starting udpAudio";
|
qDebug(logUdp()) << "Starting udpAudio";
|
||||||
this->localIP = local;
|
this->localIP = local;
|
||||||
|
@ -685,7 +688,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
|
|
||||||
rxaudio->moveToThread(rxAudioThread);
|
rxaudio->moveToThread(rxAudioThread);
|
||||||
|
|
||||||
connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool, QString)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QString)));
|
connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool, QString, quint8)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QString, quint8)));
|
||||||
|
|
||||||
qRegisterMetaType<audioPacket>();
|
qRegisterMetaType<audioPacket>();
|
||||||
connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket)));
|
connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket)));
|
||||||
|
@ -704,7 +707,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
|
|
||||||
txaudio->moveToThread(txAudioThread);
|
txaudio->moveToThread(txAudioThread);
|
||||||
|
|
||||||
connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool,QString)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QString)));
|
connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool,QString,quint8)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QString,quint8)));
|
||||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||||
|
|
||||||
rxAudioThread->start();
|
rxAudioThread->start();
|
||||||
|
@ -717,8 +720,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
||||||
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
|
||||||
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
||||||
|
|
||||||
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, txLatency, txIsUlawCodec, true, inputPort);
|
emit setupTxAudio(txNumSamples, txChannelCount, txSampleRate, txLatency, txIsUlawCodec, true, inputPort,resampleQuality);
|
||||||
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, txLatency, rxIsUlawCodec, false, outputPort);
|
emit setupRxAudio(rxNumSamples, rxChannelCount, rxSampleRate, txLatency, rxIsUlawCodec, false, outputPort,resampleQuality);
|
||||||
|
|
||||||
watchdogTimer = new QTimer();
|
watchdogTimer = new QTimer();
|
||||||
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
||||||
|
@ -788,7 +791,12 @@ void udpAudio::sendTxAudio()
|
||||||
p.len = sizeof(p) + partial.length();
|
p.len = sizeof(p) + partial.length();
|
||||||
p.sentid = myId;
|
p.sentid = myId;
|
||||||
p.rcvdid = remoteId;
|
p.rcvdid = remoteId;
|
||||||
p.ident = 0x0080; // TX audio is always this?
|
if (partial.length() == 0xa0) {
|
||||||
|
p.ident = 0x9781;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.ident = 0x0080; // TX audio is always this?
|
||||||
|
}
|
||||||
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
|
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
|
||||||
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
|
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||||
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||||
|
@ -838,23 +846,18 @@ void udpAudio::dataReceived()
|
||||||
|
|
||||||
*/
|
*/
|
||||||
control_packet_t in = (control_packet_t)r.constData();
|
control_packet_t in = (control_packet_t)r.constData();
|
||||||
if (in->type != 0x01) {
|
if (in->type != 0x01 && in->len >= 0xAC) {
|
||||||
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
|
// 0xac is the smallest possible audio packet.
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\x44\x02") ||
|
lastReceived = QTime::currentTime();
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\xd8\x03") ||
|
audioPacket tempAudio;
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
tempAudio.seq = in->seq;
|
||||||
{
|
tempAudio.time = lastReceived;
|
||||||
lastReceived = QTime::currentTime();
|
tempAudio.sent = 0;
|
||||||
audioPacket tempAudio;
|
tempAudio.datain = r.mid(0x18);
|
||||||
tempAudio.seq = in->seq;
|
// Prefer signal/slot to forward audio as it is thread/safe
|
||||||
tempAudio.time = lastReceived;
|
// Need to do more testing but latency appears fine.
|
||||||
tempAudio.sent = 0;
|
emit haveAudioData(tempAudio);
|
||||||
tempAudio.data = r.mid(0x18);
|
//rxaudio->incomingAudio(tempAudio);
|
||||||
// Prefer signal/slot to forward audio as it is thread/safe
|
|
||||||
// Need to do more testing but latency appears fine.
|
|
||||||
emit haveAudioData(tempAudio);
|
|
||||||
//rxaudio->incomingAudio(tempAudio);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -956,7 +959,7 @@ void udpBase::dataReceived(QByteArray r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (in->type == 0x04) {
|
if (in->type == 0x04) {
|
||||||
qDebug(logUdp()) << this->metaObject()->className() << ": Received I am here";
|
qDebug(logUdp()) << this->metaObject()->className() << ": Received I am here ";
|
||||||
areYouThereCounter = 0;
|
areYouThereCounter = 0;
|
||||||
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
|
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
|
||||||
remoteId = in->sentid;
|
remoteId = in->sentid;
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct udpPreferences {
|
||||||
quint8 audioRXCodec;
|
quint8 audioRXCodec;
|
||||||
quint16 audioTXSampleRate;
|
quint16 audioTXSampleRate;
|
||||||
quint8 audioTXCodec;
|
quint8 audioTXCodec;
|
||||||
|
quint8 resampleQuality;
|
||||||
};
|
};
|
||||||
|
|
||||||
void passcode(QString in, QByteArray& out);
|
void passcode(QString in, QByteArray& out);
|
||||||
|
@ -166,14 +167,14 @@ class udpAudio : public udpBase
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QString outputPort, QString inputPort);
|
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QString outputPort, QString inputPort,quint8 resampleQuality);
|
||||||
~udpAudio();
|
~udpAudio();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void haveAudioData(audioPacket data);
|
void haveAudioData(audioPacket data);
|
||||||
|
|
||||||
void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QString port);
|
void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QString port,quint8 resampleQuality);
|
||||||
void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QString port);
|
void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QString port,quint8 resampleQuality);
|
||||||
|
|
||||||
void haveChangeLatency(quint16 value);
|
void haveChangeLatency(quint16 value);
|
||||||
|
|
||||||
|
@ -277,6 +278,8 @@ private:
|
||||||
QString audioInputPort;
|
QString audioInputPort;
|
||||||
QString audioOutputPort;
|
QString audioOutputPort;
|
||||||
|
|
||||||
|
quint8 resampleQuality;
|
||||||
|
|
||||||
quint16 reauthInterval = 60000;
|
quint16 reauthInterval = 60000;
|
||||||
QString devName;
|
QString devName;
|
||||||
QString compName;
|
QString compName;
|
||||||
|
|
|
@ -1062,14 +1062,14 @@ void udpServer::receiveAudioData(const audioPacket &d)
|
||||||
if (client != Q_NULLPTR && client->connected) {
|
if (client != Q_NULLPTR && client->connected) {
|
||||||
audio_packet p;
|
audio_packet p;
|
||||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
p.len = sizeof(p) + d.data.length();
|
p.len = sizeof(p) + d.datain.length();
|
||||||
p.sentid = client->myId;
|
p.sentid = client->myId;
|
||||||
p.rcvdid = client->remoteId;
|
p.rcvdid = client->remoteId;
|
||||||
p.ident = 0x0080; // audio is always this?
|
p.ident = 0x0080; // audio is always this?
|
||||||
p.datalen = (quint16)qToBigEndian((quint16)d.data.length());
|
p.datalen = (quint16)qToBigEndian((quint16)d.datain.length());
|
||||||
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
|
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||||
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||||
t.append(d.data);
|
t.append(d.datain);
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
client->txSeqBuf.append(SEQBUFENTRY());
|
client->txSeqBuf.append(SEQBUFENTRY());
|
||||||
client->txSeqBuf.last().seqNum = p.seq;
|
client->txSeqBuf.last().seqNum = p.seq;
|
||||||
|
|
|
@ -761,8 +761,7 @@ void wfmain::setDefPrefs()
|
||||||
udpDefPrefs.audioRXCodec = 4;
|
udpDefPrefs.audioRXCodec = 4;
|
||||||
udpDefPrefs.audioTXSampleRate = 48000;
|
udpDefPrefs.audioTXSampleRate = 48000;
|
||||||
udpDefPrefs.audioTXCodec = 4;
|
udpDefPrefs.audioTXCodec = 4;
|
||||||
|
udpDefPrefs.resampleQuality = 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wfmain::loadSettings()
|
void wfmain::loadSettings()
|
||||||
|
@ -878,6 +877,8 @@ void wfmain::loadSettings()
|
||||||
ui->audioInputCombo->setCurrentIndex(audioInputIndex);
|
ui->audioInputCombo->setCurrentIndex(audioInputIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
udpPrefs.resampleQuality = settings.value("ResampleQuality", udpDefPrefs.resampleQuality).toInt();
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.beginGroup("Server");
|
settings.beginGroup("Server");
|
||||||
|
@ -985,6 +986,7 @@ void wfmain::saveSettings()
|
||||||
settings.setValue("AudioTXCodec", udpPrefs.audioTXCodec);
|
settings.setValue("AudioTXCodec", udpPrefs.audioTXCodec);
|
||||||
settings.setValue("AudioOutput", udpPrefs.audioOutput);
|
settings.setValue("AudioOutput", udpPrefs.audioOutput);
|
||||||
settings.setValue("AudioInput", udpPrefs.audioInput);
|
settings.setValue("AudioInput", udpPrefs.audioInput);
|
||||||
|
settings.setValue("ResampleQuality", udpPrefs.resampleQuality);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
// Memory channels
|
// Memory channels
|
||||||
|
|
12
wfview.pro
12
wfview.pro
|
@ -31,6 +31,10 @@ QMAKE_LFLAGS += -O2 -march=native -s
|
||||||
DEFINES += QT_DEPRECATED_WARNINGS
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY
|
DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY
|
||||||
|
|
||||||
|
# These defines are used for the resampler
|
||||||
|
DEFINES += OUTSIDE_SPEEX
|
||||||
|
DEFINES += RANDOM_PREFIX=wf
|
||||||
|
|
||||||
linux:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\"
|
linux:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\"
|
||||||
|
|
||||||
linux:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\""
|
linux:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\""
|
||||||
|
@ -88,7 +92,8 @@ SOURCES += main.cpp\
|
||||||
udpserver.cpp \
|
udpserver.cpp \
|
||||||
meter.cpp \
|
meter.cpp \
|
||||||
qledlabel.cpp \
|
qledlabel.cpp \
|
||||||
pttyhandler.cpp
|
pttyhandler.cpp \
|
||||||
|
resampler/resample.cpp
|
||||||
|
|
||||||
HEADERS += wfmain.h \
|
HEADERS += wfmain.h \
|
||||||
commhandler.h \
|
commhandler.h \
|
||||||
|
@ -105,7 +110,10 @@ HEADERS += wfmain.h \
|
||||||
packettypes.h \
|
packettypes.h \
|
||||||
meter.h \
|
meter.h \
|
||||||
qledlabel.h \
|
qledlabel.h \
|
||||||
pttyhandler.h
|
pttyhandler.h \
|
||||||
|
resampler/speex_resampler.h \
|
||||||
|
resampler/arch.h \
|
||||||
|
resampler/resample_sse.h
|
||||||
|
|
||||||
|
|
||||||
FORMS += wfmain.ui \
|
FORMS += wfmain.ui \
|
||||||
|
|
10
wfview.sln
10
wfview.sln
|
@ -8,13 +8,23 @@ EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|x64 = Release|x64
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
Template|x64 = Template|x64
|
||||||
|
Template|x86 = Template|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.ActiveCfg = Debug|x64
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.Build.0 = Debug|x64
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.ActiveCfg = Release|x64
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.ActiveCfg = Release|x64
|
||||||
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.Build.0 = Release|x64
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.Build.0 = Release|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x64.ActiveCfg = Release|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x64.Build.0 = Release|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x86.ActiveCfg = Release|x64
|
||||||
|
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Template|x86.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -152,7 +152,7 @@
|
||||||
<ExceptionHandling>Sync</ExceptionHandling>
|
<ExceptionHandling>Sync</ExceptionHandling>
|
||||||
<ObjectFileName>debug\</ObjectFileName>
|
<ObjectFileName>debug\</ObjectFileName>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_OPENGL;HOST=1;UNAME=1;GITSHORT=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_OPENGL;HOST=1;UNAME=1;GITSHORT=1;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessToFile>false</PreprocessToFile>
|
<PreprocessToFile>false</PreprocessToFile>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
@ -210,6 +210,7 @@
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="c:\qcustomplot\qcustomplot.cpp" />
|
<ClCompile Include="c:\qcustomplot\qcustomplot.cpp" />
|
||||||
<ClCompile Include="meter.cpp" />
|
<ClCompile Include="meter.cpp" />
|
||||||
|
<ClCompile Include="resampler\resample.c" />
|
||||||
<ClCompile Include="rigcommander.cpp" />
|
<ClCompile Include="rigcommander.cpp" />
|
||||||
<ClCompile Include="rigidentities.cpp" />
|
<ClCompile Include="rigidentities.cpp" />
|
||||||
<ClCompile Include="audiohandler.cpp" />
|
<ClCompile Include="audiohandler.cpp" />
|
||||||
|
@ -237,6 +238,7 @@
|
||||||
<QtMoc Include="meter.h" />
|
<QtMoc Include="meter.h" />
|
||||||
<ClInclude Include="packettypes.h" />
|
<ClInclude Include="packettypes.h" />
|
||||||
<QtMoc Include="pttyhandler.h" />
|
<QtMoc Include="pttyhandler.h" />
|
||||||
|
<ClInclude Include="resampler\speex_resampler.h" />
|
||||||
<ClInclude Include="rigidentities.h" />
|
<ClInclude Include="rigidentities.h" />
|
||||||
<QtMoc Include="udphandler.h">
|
<QtMoc Include="udphandler.h">
|
||||||
</QtMoc>
|
</QtMoc>
|
||||||
|
|
|
@ -108,6 +108,9 @@
|
||||||
<ClCompile Include="pttyhandler.cpp">
|
<ClCompile Include="pttyhandler.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="resampler\resample.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<QtMoc Include="commhandler.h">
|
<QtMoc Include="commhandler.h">
|
||||||
|
@ -323,6 +326,10 @@
|
||||||
<None Include="resources\wfview.png" />
|
<None Include="resources\wfview.png" />
|
||||||
<None Include="resources\wfview.png" />
|
<None Include="resources\wfview.png" />
|
||||||
<None Include="resources\wfview.png" />
|
<None Include="resources\wfview.png" />
|
||||||
|
<None Include="resources\wfview.png" />
|
||||||
|
<None Include="resources\wfview.png" />
|
||||||
|
<None Include="resources\wfview.png" />
|
||||||
|
<None Include="resources\wfview.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="resources\install.sh">
|
<None Include="resources\install.sh">
|
||||||
|
@ -339,5 +346,8 @@
|
||||||
<ClInclude Include="packettypes.h">
|
<ClInclude Include="packettypes.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="resampler\speex_resampler.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Ładowanie…
Reference in New Issue