1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-28 15:56:33 -04:00

Deep redesign: debug session #2 phase #1

This commit is contained in:
f4exb 2015-08-20 03:38:31 +02:00
parent 582ce24c62
commit 38bc6563d4
9 changed files with 301 additions and 139 deletions

View File

@ -31,7 +31,7 @@ class AudioOutputPipe;
class SDRANGELOVE_API AudioOutput : QIODevice {
public:
AudioOutput();
~AudioOutput();
virtual ~AudioOutput();
bool start(int device, int rate);
void stop();
@ -47,9 +47,9 @@ private:
AudioFifos m_audioFifos;
std::vector<qint32> m_mixBuffer;
bool open(OpenMode mode);
qint64 readData(char* data, qint64 maxLen);
qint64 writeData(const char* data, qint64 len);
//virtual bool open(OpenMode mode);
virtual qint64 readData(char* data, qint64 maxLen);
virtual qint64 writeData(const char* data, qint64 len);
friend class AudioOutputPipe;
};

View File

@ -98,7 +98,7 @@ private:
typedef std::list<ThreadedSampleSink*> ThreadedSampleSinks;
ThreadedSampleSinks m_threadedSampleSinks; //!< sample sinks on their own threads (usually channels)
AudioOutput m_audioSink;
AudioOutput m_audioOutput;
uint m_sampleRate;
quint64 m_centerFrequency;

View File

@ -46,7 +46,7 @@ public:
void stop(); //!< this thread exit() and wait()
bool sendWaitSink(Message& cmd); //!< Send message to sink synchronously
void feed(SampleVector::const_iterator& begin, SampleVector::const_iterator& end, bool positiveOnly); //!< Feed sink with samples
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); //!< Feed sink with samples
QString getSampleSinkObjectName() const;

View File

@ -63,7 +63,7 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera
{
Complex ci;
if (m_audioFifo->size() <= 0)
if (m_audioFifo->size() == 0)
{
return;
}
@ -73,68 +73,67 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
c *= m_nco.nextIQ();
if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
m_movingAverage.feed(magsq);
if (m_movingAverage.average() >= m_squelchLevel)
{
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
m_movingAverage.feed(magsq);
if (m_movingAverage.average() >= m_squelchLevel)
{
m_squelchState = m_running.m_audioSampleRate/ 20;
}
qint16 sample;
if (m_squelchState > 0)
{
m_squelchState--;
Real demod = sqrt(magsq);
demod = m_lowpass.filter(demod);
if (demod < -1)
{
demod = -1;
}
else if (demod > 1)
{
demod = 1;
}
m_volumeAGC.feed(demod);
demod *= (0.003 / m_volumeAGC.getValue());
demod *= m_running.m_volume;
sample = demod * 32700 * 16;
}
else
{
m_volumeAGC.close();
sample = 0;
}
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
if (res != m_audioBufferFill)
{
qDebug("lost %u audio samples", m_audioBufferFill - res);
}
m_audioBufferFill = 0;
}
m_interpolatorDistanceRemain += m_interpolatorDistance;
m_squelchState = m_running.m_audioSampleRate/ 20;
}
qint16 sample;
if (m_squelchState > 0)
{
m_squelchState--;
Real demod = sqrt(magsq);
demod = m_lowpass.filter(demod);
if (demod < -1)
{
demod = -1;
}
else if (demod > 1)
{
demod = 1;
}
m_volumeAGC.feed(demod);
demod *= (0.003 / m_volumeAGC.getValue());
demod *= m_running.m_volume;
sample = demod * 32700 * 16;
}
else
{
m_volumeAGC.close();
sample = 0;
}
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
/* FIXME: Not necessarily bad, There is a race between threads but generally it works i.e. samples are not lost
if (res != m_audioBufferFill)
{
qDebug("AMDemod::feed: %u/%u audio samples lost", m_audioBufferFill - res, m_audioBufferFill);
}*/
m_audioBufferFill = 0;
}
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
@ -142,10 +141,11 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera
{
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
/* SAme remark as above
if (res != m_audioBufferFill)
{
qDebug("lost %u samples", m_audioBufferFill - res);
}
qDebug("AMDemod::feed: %u samples written vs %u requested", res, m_audioBufferFill);
}*/
m_audioBufferFill = 0;
}

View File

@ -107,15 +107,19 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
{
Complex ci;
if(m_audioFifo->size() <= 0)
if (m_audioFifo->size() == 0)
{
return;
}
for(SampleVector::const_iterator it = begin; it != end; ++it) {
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
c *= m_nco.nextIQ();
{
if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) {
if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
qint16 sample;
@ -160,7 +164,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
// AF processing
if(m_afSquelch.analyze(&demod)) {
if(m_afSquelch.analyze(&demod))
{
m_squelchOpen = m_afSquelch.open();
}
@ -176,14 +181,16 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
if (m_ctcssDetector.getDetectedTone(maxToneIndex))
{
if (maxToneIndex+1 != m_ctcssIndex) {
if (maxToneIndex+1 != m_ctcssIndex)
{
m_nfmDemodGUI->setCtcssFreq(m_ctcssDetector.getToneSet()[maxToneIndex]);
m_ctcssIndex = maxToneIndex+1;
}
}
else
{
if (m_ctcssIndex != 0) {
if (m_ctcssIndex != 0)
{
m_nfmDemodGUI->setCtcssFreq(0);
m_ctcssIndex = 0;
}
@ -204,7 +211,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
}
else
{
if (m_ctcssIndex != 0) {
if (m_ctcssIndex != 0)
{
m_nfmDemodGUI->setCtcssFreq(0);
m_ctcssIndex = 0;
}
@ -216,10 +224,17 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if(m_audioBufferFill >= m_audioBuffer.size()) {
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
if(res != m_audioBufferFill)
/* FIXME: Not necessarily bad, There is a race between threads but generally it works i.e. samples are not lost
if (res != m_audioBufferFill)
{
qDebug("lost %u audio samples", m_audioBufferFill - res);
}*/
m_audioBufferFill = 0;
}
@ -227,10 +242,17 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
}
}
}
if(m_audioBufferFill > 0) {
if (m_audioBufferFill > 0)
{
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
if(res != m_audioBufferFill)
/* Same remark as above
if (res != m_audioBufferFill)
{
qDebug("lost %u samples", m_audioBufferFill - res);
}*/
m_audioBufferFill = 0;
}

View File

@ -22,16 +22,17 @@
#define MIN(x, y) ((x) < (y) ? (x) : (y))
AudioFifo::AudioFifo() :
m_fifo(NULL)
m_fifo(0)
{
m_size = 0;
m_fill = 0;
m_head = 0;
m_tail = 0;
m_sampleSize = 0;
}
AudioFifo::AudioFifo(uint sampleSize, uint numSamples) :
m_fifo(NULL)
m_fifo(0)
{
QMutexLocker mutexLocker(&m_mutex);
@ -42,9 +43,10 @@ AudioFifo::~AudioFifo()
{
QMutexLocker mutexLocker(&m_mutex);
if(m_fifo != NULL) {
if (m_fifo != 0)
{
delete[] m_fifo;
m_fifo = NULL;
m_fifo = 0;
}
m_writeWaitCondition.wakeOne();
@ -67,39 +69,63 @@ uint AudioFifo::write(const quint8* data, uint numSamples, int timeout)
uint remaining;
uint copyLen;
if(m_fifo == NULL)
if(m_fifo == 0)
{
return 0;
}
time.start();
m_mutex.lock();
if(timeout == 0)
{
total = MIN(numSamples, m_size - m_fill);
else total = numSamples;
}
else
{
total = numSamples;
}
remaining = total;
while(remaining > 0) {
if(isFull()) {
if(time.elapsed() < timeout) {
while (remaining > 0)
{
if (isFull())
{
if (time.elapsed() < timeout)
{
m_writeWaitLock.lock();
m_mutex.unlock();
int ms = timeout - time.elapsed();
if(ms < 1)
{
ms = 1;
}
bool ok = m_writeWaitCondition.wait(&m_writeWaitLock, ms);
m_writeWaitLock.unlock();
if(!ok)
{
return total - remaining;
}
m_mutex.lock();
if(m_fifo == NULL) {
if(m_fifo == 0)
{
m_mutex.unlock();
return 0;
}
} else {
}
else
{
m_mutex.unlock();
return total - remaining;
}
}
copyLen = MIN(remaining, m_size - m_fill);
copyLen = MIN(copyLen, m_size - m_tail);
memcpy(m_fifo + (m_tail * m_sampleSize), data, copyLen * m_sampleSize);
@ -122,35 +148,58 @@ uint AudioFifo::read(quint8* data, uint numSamples, int timeout)
uint remaining;
uint copyLen;
if(m_fifo == NULL)
if(m_fifo == 0)
{
return 0;
}
time.start();
m_mutex.lock();
if(timeout == 0)
{
total = MIN(numSamples, m_fill);
else total = numSamples;
}
else
{
total = numSamples;
}
remaining = total;
while(remaining > 0) {
if(isEmpty()) {
if(time.elapsed() < timeout) {
while(remaining > 0)
{
if(isEmpty())
{
if(time.elapsed() < timeout)
{
m_readWaitLock.lock();
m_mutex.unlock();
int ms = timeout - time.elapsed();
if(ms < 1)
{
ms = 1;
}
bool ok = m_readWaitCondition.wait(&m_readWaitLock, ms);
m_readWaitLock.unlock();
if(!ok)
{
return total - remaining;
}
m_mutex.lock();
if(m_fifo == NULL) {
if(m_fifo == 0)
{
m_mutex.unlock();
return 0;
}
} else {
}
else
{
m_mutex.unlock();
return total - remaining;
}
@ -176,7 +225,10 @@ uint AudioFifo::drain(uint numSamples)
QMutexLocker mutexLocker(&m_mutex);
if(numSamples > m_fill)
{
numSamples = m_fill;
}
m_head = (m_head + numSamples) % m_size;
m_fill -= numSamples;
@ -197,7 +249,8 @@ void AudioFifo::clear()
bool AudioFifo::create(uint sampleSize, uint numSamples)
{
if(m_fifo != NULL) {
if(m_fifo != 0)
{
delete[] m_fifo;
m_fifo = NULL;
}
@ -208,7 +261,8 @@ bool AudioFifo::create(uint sampleSize, uint numSamples)
m_head = 0;
m_tail = 0;
if((m_fifo = new qint8[numSamples * m_sampleSize]) == NULL) {
if((m_fifo = new qint8[numSamples * m_sampleSize]) == 0)
{
qDebug("out of memory");
return false;
}

View File

@ -34,8 +34,12 @@ AudioOutput::~AudioOutput()
stop();
QMutexLocker mutexLocker(&m_mutex);
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
{
delete *it;
}
m_audioFifos.clear();
}
@ -43,30 +47,55 @@ bool AudioOutput::start(int device, int rate)
{
QMutexLocker mutexLocker(&m_mutex);
Q_UNUSED(device);
Q_UNUSED(rate);
//Q_UNUSED(device);
//Q_UNUSED(rate);
QAudioFormat format;
QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice());
QAudioDeviceInfo devInfo;
format.setSampleRate(48000);
if (device < 0)
{
devInfo = QAudioDeviceInfo::defaultOutputDevice();
qWarning("AudioOutput::start: using default device %s", qPrintable(devInfo.defaultOutputDevice().deviceName()));
}
else
{
QList<QAudioDeviceInfo> devicesInfo = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
if (device < devicesInfo.size())
{
devInfo = devicesInfo[device];
qWarning("AudioOutput::start: using audio device #%d: %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName()));
}
else
{
devInfo = QAudioDeviceInfo::defaultOutputDevice();
qWarning("AudioOutput::start: audio device #%d does not exist. Using default device %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName()));
}
}
//QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice());
format.setSampleRate(rate);
format.setChannelCount(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if(!devInfo.isFormatSupported(format)) {
qWarning("48kHz S16_LE audio format not supported");
if (!devInfo.isFormatSupported(format))
{
qWarning("AudioOutput::start: %d Hz S16_LE audio format not supported", rate);
format = devInfo.nearestFormat(format);
}
if(format.sampleSize() != 16) {
qWarning("Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName()));
if (format.sampleSize() != 16)
{
qWarning("AudioOutput::start: Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName()));
return false;
}
m_audioOutput = new QAudioOutput(format);
m_audioOutput = new QAudioOutput(devInfo, format);
QIODevice::open(QIODevice::ReadOnly);
@ -80,11 +109,13 @@ void AudioOutput::stop()
{
QMutexLocker mutexLocker(&m_mutex);
if(m_audioOutput != NULL) {
if (m_audioOutput != 0)
{
m_audioOutput->stop();
delete m_audioOutput;
m_audioOutput = NULL;
m_audioOutput = 0;
}
QIODevice::close();
}
@ -102,34 +133,53 @@ void AudioOutput::removeFifo(AudioFifo* audioFifo)
m_audioFifos.remove(audioFifo);
}
/*
bool AudioOutput::open(OpenMode mode)
{
Q_UNUSED(mode);
return false;
}
}*/
qint64 AudioOutput::readData(char* data, qint64 maxLen)
{
//qDebug("AudioOutput::readData: %lld", maxLen);
QMutexLocker mutexLocker(&m_mutex);
unsigned int framesPerBuffer = maxLen / 4;
if(framesPerBuffer == 0)
return 0;
if(m_mixBuffer.size() < framesPerBuffer * 2) {
m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo)
if(m_mixBuffer.size() != framesPerBuffer * 2)
return 0;
if (framesPerBuffer == 0)
{
return 0;
}
if (m_mixBuffer.size() < framesPerBuffer * 2)
{
m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo)
if (m_mixBuffer.size() != framesPerBuffer * 2)
{
return 0;
}
}
memset(&m_mixBuffer[0], 0x00, 2 * framesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence
// sum up a block from all fifos
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) {
for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
{
// use outputBuffer as temp - yes, one memcpy could be saved
uint samples = (*it)->read((quint8*)data, framesPerBuffer, 1);
const qint16* src = (const qint16*)data;
uint samples = (*it)->read((quint8*) data, framesPerBuffer, 1);
const qint16* src = (const qint16*) data;
std::vector<qint32>::iterator dst = m_mixBuffer.begin();
for(uint i = 0; i < samples; i++) {
if (samples != framesPerBuffer)
{
qDebug("AudioOutput::readData: read %d samples vs %d requested", samples, framesPerBuffer);
}
for (uint i = 0; i < samples; i++)
{
*dst += *src;
++src;
++dst;
@ -140,22 +190,40 @@ qint64 AudioOutput::readData(char* data, qint64 maxLen)
}
// convert to int16
std::vector<qint32>::const_iterator src = m_mixBuffer.begin();
qint16* dst = (qint16*)data;
for(uint i = 0; i < framesPerBuffer; i++) {
qint16* dst = (qint16*) data;
for (uint i = 0; i < framesPerBuffer; i++)
{
// left channel
qint32 s = *src++;
if(s < -32768)
{
s = -32768;
else if(s > 32767)
}
else if (s > 32767)
{
s = 32767;
}
*dst++ = s;
// right channel
s = *src++;
if(s < -32768)
{
s = -32768;
else if(s > 32767)
}
else if (s > 32767)
{
s = 32767;
}
*dst++ = s;
}

View File

@ -271,16 +271,25 @@ void DSPEngine::work()
if (part1begin != part1end)
{
// correct stuff
if (m_dcOffsetCorrection) {
if (m_dcOffsetCorrection)
{
dcOffset(part1begin, part1end);
}
if (m_iqImbalanceCorrection) {
if (m_iqImbalanceCorrection)
{
imbalance(part1begin, part1end);
}
// feed data to handlers
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
// feed data to direct sinks
for (SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); ++it)
{
(*it)->feed(part1begin, part1end, positiveOnly);
}
// feed data to threaded sinks
for (ThreadedSampleSinks::const_iterator it = m_threadedSampleSinks.begin(); it != m_threadedSampleSinks.end(); ++it)
{
(*it)->feed(part1begin, part1end, positiveOnly);
}
}
@ -289,16 +298,25 @@ void DSPEngine::work()
if(part2begin != part2end)
{
// correct stuff
if(m_dcOffsetCorrection) {
if (m_dcOffsetCorrection)
{
dcOffset(part2begin, part2end);
}
if(m_iqImbalanceCorrection) {
if (m_iqImbalanceCorrection)
{
imbalance(part2begin, part2end);
}
// feed data to handlers
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
// feed data to direct sinks
for (SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
{
(*it)->feed(part2begin, part2end, positiveOnly);
}
// feed data to threaded sinks
for (ThreadedSampleSinks::const_iterator it = m_threadedSampleSinks.begin(); it != m_threadedSampleSinks.end(); ++it)
{
(*it)->feed(part2begin, part2end, positiveOnly);
}
}
@ -344,7 +362,7 @@ DSPEngine::State DSPEngine::gotoIdle()
m_sampleSource->stop();
m_deviceDescription.clear();
m_audioSink.stop();
m_audioOutput.stop();
m_sampleRate = 0;
return StIdle;
@ -444,7 +462,7 @@ DSPEngine::State DSPEngine::gotoRunning()
return gotoError("Could not start sample source");
}
m_audioSink.start(0, 48000);
m_audioOutput.start(-1, 48000); // Use default output device at 48 kHz
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
{
@ -591,11 +609,11 @@ void DSPEngine::handleSynchronousMessages()
}
else if (DSPAddAudioSink::match(*message))
{
m_audioSink.addFifo(((DSPAddAudioSink*) message)->getAudioFifo());
m_audioOutput.addFifo(((DSPAddAudioSink*) message)->getAudioFifo());
}
else if (DSPRemoveAudioSink::match(*message))
{
m_audioSink.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo());
m_audioOutput.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo());
}
m_syncMessenger.done(m_state);

View File

@ -38,7 +38,7 @@ void ThreadedSampleSink::run()
exec();
}
void ThreadedSampleSink::feed(SampleVector::const_iterator& begin, SampleVector::const_iterator& end, bool positiveOnly)
void ThreadedSampleSink::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
{
m_sampleSink->feed(begin, end, positiveOnly);
}