mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-09-04 06:07:49 -04:00
parent
582ce24c62
commit
38bc6563d4
@ -31,7 +31,7 @@ class AudioOutputPipe;
|
|||||||
class SDRANGELOVE_API AudioOutput : QIODevice {
|
class SDRANGELOVE_API AudioOutput : QIODevice {
|
||||||
public:
|
public:
|
||||||
AudioOutput();
|
AudioOutput();
|
||||||
~AudioOutput();
|
virtual ~AudioOutput();
|
||||||
|
|
||||||
bool start(int device, int rate);
|
bool start(int device, int rate);
|
||||||
void stop();
|
void stop();
|
||||||
@ -47,9 +47,9 @@ private:
|
|||||||
AudioFifos m_audioFifos;
|
AudioFifos m_audioFifos;
|
||||||
std::vector<qint32> m_mixBuffer;
|
std::vector<qint32> m_mixBuffer;
|
||||||
|
|
||||||
bool open(OpenMode mode);
|
//virtual bool open(OpenMode mode);
|
||||||
qint64 readData(char* data, qint64 maxLen);
|
virtual qint64 readData(char* data, qint64 maxLen);
|
||||||
qint64 writeData(const char* data, qint64 len);
|
virtual qint64 writeData(const char* data, qint64 len);
|
||||||
|
|
||||||
friend class AudioOutputPipe;
|
friend class AudioOutputPipe;
|
||||||
};
|
};
|
||||||
|
@ -98,7 +98,7 @@ private:
|
|||||||
typedef std::list<ThreadedSampleSink*> ThreadedSampleSinks;
|
typedef std::list<ThreadedSampleSink*> ThreadedSampleSinks;
|
||||||
ThreadedSampleSinks m_threadedSampleSinks; //!< sample sinks on their own threads (usually channels)
|
ThreadedSampleSinks m_threadedSampleSinks; //!< sample sinks on their own threads (usually channels)
|
||||||
|
|
||||||
AudioOutput m_audioSink;
|
AudioOutput m_audioOutput;
|
||||||
|
|
||||||
uint m_sampleRate;
|
uint m_sampleRate;
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
|
@ -46,7 +46,7 @@ public:
|
|||||||
void stop(); //!< this thread exit() and wait()
|
void stop(); //!< this thread exit() and wait()
|
||||||
|
|
||||||
bool sendWaitSink(Message& cmd); //!< Send message to sink synchronously
|
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;
|
QString getSampleSinkObjectName() const;
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera
|
|||||||
{
|
{
|
||||||
Complex ci;
|
Complex ci;
|
||||||
|
|
||||||
if (m_audioFifo->size() <= 0)
|
if (m_audioFifo->size() == 0)
|
||||||
{
|
{
|
||||||
return;
|
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);
|
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||||
c *= m_nco.nextIQ();
|
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));
|
m_squelchState = m_running.m_audioSampleRate/ 20;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
|
||||||
|
|
||||||
|
/* SAme remark as above
|
||||||
if (res != m_audioBufferFill)
|
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;
|
m_audioBufferFill = 0;
|
||||||
}
|
}
|
||||||
|
@ -107,15 +107,19 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
{
|
{
|
||||||
Complex ci;
|
Complex ci;
|
||||||
|
|
||||||
if(m_audioFifo->size() <= 0)
|
if (m_audioFifo->size() == 0)
|
||||||
|
{
|
||||||
return;
|
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);
|
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||||
c *= m_nco.nextIQ();
|
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));
|
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
|
||||||
|
|
||||||
qint16 sample;
|
qint16 sample;
|
||||||
@ -160,7 +164,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
|
|
||||||
// AF processing
|
// AF processing
|
||||||
|
|
||||||
if(m_afSquelch.analyze(&demod)) {
|
if(m_afSquelch.analyze(&demod))
|
||||||
|
{
|
||||||
m_squelchOpen = m_afSquelch.open();
|
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 (m_ctcssDetector.getDetectedTone(maxToneIndex))
|
||||||
{
|
{
|
||||||
if (maxToneIndex+1 != m_ctcssIndex) {
|
if (maxToneIndex+1 != m_ctcssIndex)
|
||||||
|
{
|
||||||
m_nfmDemodGUI->setCtcssFreq(m_ctcssDetector.getToneSet()[maxToneIndex]);
|
m_nfmDemodGUI->setCtcssFreq(m_ctcssDetector.getToneSet()[maxToneIndex]);
|
||||||
m_ctcssIndex = maxToneIndex+1;
|
m_ctcssIndex = maxToneIndex+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_ctcssIndex != 0) {
|
if (m_ctcssIndex != 0)
|
||||||
|
{
|
||||||
m_nfmDemodGUI->setCtcssFreq(0);
|
m_nfmDemodGUI->setCtcssFreq(0);
|
||||||
m_ctcssIndex = 0;
|
m_ctcssIndex = 0;
|
||||||
}
|
}
|
||||||
@ -204,7 +211,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_ctcssIndex != 0) {
|
if (m_ctcssIndex != 0)
|
||||||
|
{
|
||||||
m_nfmDemodGUI->setCtcssFreq(0);
|
m_nfmDemodGUI->setCtcssFreq(0);
|
||||||
m_ctcssIndex = 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].l = sample;
|
||||||
m_audioBuffer[m_audioBufferFill].r = sample;
|
m_audioBuffer[m_audioBufferFill].r = sample;
|
||||||
++m_audioBufferFill;
|
++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);
|
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);
|
qDebug("lost %u audio samples", m_audioBufferFill - res);
|
||||||
|
}*/
|
||||||
|
|
||||||
m_audioBufferFill = 0;
|
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);
|
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);
|
qDebug("lost %u samples", m_audioBufferFill - res);
|
||||||
|
}*/
|
||||||
|
|
||||||
m_audioBufferFill = 0;
|
m_audioBufferFill = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,16 +22,17 @@
|
|||||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
AudioFifo::AudioFifo() :
|
AudioFifo::AudioFifo() :
|
||||||
m_fifo(NULL)
|
m_fifo(0)
|
||||||
{
|
{
|
||||||
m_size = 0;
|
m_size = 0;
|
||||||
m_fill = 0;
|
m_fill = 0;
|
||||||
m_head = 0;
|
m_head = 0;
|
||||||
m_tail = 0;
|
m_tail = 0;
|
||||||
|
m_sampleSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioFifo::AudioFifo(uint sampleSize, uint numSamples) :
|
AudioFifo::AudioFifo(uint sampleSize, uint numSamples) :
|
||||||
m_fifo(NULL)
|
m_fifo(0)
|
||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
@ -42,9 +43,10 @@ AudioFifo::~AudioFifo()
|
|||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
if(m_fifo != NULL) {
|
if (m_fifo != 0)
|
||||||
|
{
|
||||||
delete[] m_fifo;
|
delete[] m_fifo;
|
||||||
m_fifo = NULL;
|
m_fifo = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_writeWaitCondition.wakeOne();
|
m_writeWaitCondition.wakeOne();
|
||||||
@ -67,39 +69,63 @@ uint AudioFifo::write(const quint8* data, uint numSamples, int timeout)
|
|||||||
uint remaining;
|
uint remaining;
|
||||||
uint copyLen;
|
uint copyLen;
|
||||||
|
|
||||||
if(m_fifo == NULL)
|
if(m_fifo == 0)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
time.start();
|
time.start();
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
|
|
||||||
if(timeout == 0)
|
if(timeout == 0)
|
||||||
|
{
|
||||||
total = MIN(numSamples, m_size - m_fill);
|
total = MIN(numSamples, m_size - m_fill);
|
||||||
else total = numSamples;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
total = numSamples;
|
||||||
|
}
|
||||||
|
|
||||||
remaining = total;
|
remaining = total;
|
||||||
while(remaining > 0) {
|
|
||||||
if(isFull()) {
|
while (remaining > 0)
|
||||||
if(time.elapsed() < timeout) {
|
{
|
||||||
|
if (isFull())
|
||||||
|
{
|
||||||
|
if (time.elapsed() < timeout)
|
||||||
|
{
|
||||||
m_writeWaitLock.lock();
|
m_writeWaitLock.lock();
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
int ms = timeout - time.elapsed();
|
int ms = timeout - time.elapsed();
|
||||||
|
|
||||||
if(ms < 1)
|
if(ms < 1)
|
||||||
|
{
|
||||||
ms = 1;
|
ms = 1;
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = m_writeWaitCondition.wait(&m_writeWaitLock, ms);
|
bool ok = m_writeWaitCondition.wait(&m_writeWaitLock, ms);
|
||||||
m_writeWaitLock.unlock();
|
m_writeWaitLock.unlock();
|
||||||
|
|
||||||
if(!ok)
|
if(!ok)
|
||||||
|
{
|
||||||
return total - remaining;
|
return total - remaining;
|
||||||
|
}
|
||||||
|
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
if(m_fifo == NULL) {
|
|
||||||
|
if(m_fifo == 0)
|
||||||
|
{
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
return total - remaining;
|
return total - remaining;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copyLen = MIN(remaining, m_size - m_fill);
|
copyLen = MIN(remaining, m_size - m_fill);
|
||||||
copyLen = MIN(copyLen, m_size - m_tail);
|
copyLen = MIN(copyLen, m_size - m_tail);
|
||||||
memcpy(m_fifo + (m_tail * m_sampleSize), data, copyLen * m_sampleSize);
|
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 remaining;
|
||||||
uint copyLen;
|
uint copyLen;
|
||||||
|
|
||||||
if(m_fifo == NULL)
|
if(m_fifo == 0)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
time.start();
|
time.start();
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
|
|
||||||
if(timeout == 0)
|
if(timeout == 0)
|
||||||
|
{
|
||||||
total = MIN(numSamples, m_fill);
|
total = MIN(numSamples, m_fill);
|
||||||
else total = numSamples;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
total = numSamples;
|
||||||
|
}
|
||||||
|
|
||||||
remaining = total;
|
remaining = total;
|
||||||
while(remaining > 0) {
|
|
||||||
if(isEmpty()) {
|
while(remaining > 0)
|
||||||
if(time.elapsed() < timeout) {
|
{
|
||||||
|
if(isEmpty())
|
||||||
|
{
|
||||||
|
if(time.elapsed() < timeout)
|
||||||
|
{
|
||||||
m_readWaitLock.lock();
|
m_readWaitLock.lock();
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
int ms = timeout - time.elapsed();
|
int ms = timeout - time.elapsed();
|
||||||
|
|
||||||
if(ms < 1)
|
if(ms < 1)
|
||||||
|
{
|
||||||
ms = 1;
|
ms = 1;
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = m_readWaitCondition.wait(&m_readWaitLock, ms);
|
bool ok = m_readWaitCondition.wait(&m_readWaitLock, ms);
|
||||||
m_readWaitLock.unlock();
|
m_readWaitLock.unlock();
|
||||||
|
|
||||||
if(!ok)
|
if(!ok)
|
||||||
|
{
|
||||||
return total - remaining;
|
return total - remaining;
|
||||||
|
}
|
||||||
|
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
if(m_fifo == NULL) {
|
|
||||||
|
if(m_fifo == 0)
|
||||||
|
{
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
return total - remaining;
|
return total - remaining;
|
||||||
}
|
}
|
||||||
@ -176,7 +225,10 @@ uint AudioFifo::drain(uint numSamples)
|
|||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
if(numSamples > m_fill)
|
if(numSamples > m_fill)
|
||||||
|
{
|
||||||
numSamples = m_fill;
|
numSamples = m_fill;
|
||||||
|
}
|
||||||
|
|
||||||
m_head = (m_head + numSamples) % m_size;
|
m_head = (m_head + numSamples) % m_size;
|
||||||
m_fill -= numSamples;
|
m_fill -= numSamples;
|
||||||
|
|
||||||
@ -197,7 +249,8 @@ void AudioFifo::clear()
|
|||||||
|
|
||||||
bool AudioFifo::create(uint sampleSize, uint numSamples)
|
bool AudioFifo::create(uint sampleSize, uint numSamples)
|
||||||
{
|
{
|
||||||
if(m_fifo != NULL) {
|
if(m_fifo != 0)
|
||||||
|
{
|
||||||
delete[] m_fifo;
|
delete[] m_fifo;
|
||||||
m_fifo = NULL;
|
m_fifo = NULL;
|
||||||
}
|
}
|
||||||
@ -208,7 +261,8 @@ bool AudioFifo::create(uint sampleSize, uint numSamples)
|
|||||||
m_head = 0;
|
m_head = 0;
|
||||||
m_tail = 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");
|
qDebug("out of memory");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,12 @@ AudioOutput::~AudioOutput()
|
|||||||
stop();
|
stop();
|
||||||
|
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
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;
|
delete *it;
|
||||||
|
}
|
||||||
|
|
||||||
m_audioFifos.clear();
|
m_audioFifos.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,30 +47,55 @@ bool AudioOutput::start(int device, int rate)
|
|||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
Q_UNUSED(device);
|
//Q_UNUSED(device);
|
||||||
Q_UNUSED(rate);
|
//Q_UNUSED(rate);
|
||||||
|
|
||||||
QAudioFormat format;
|
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.setChannelCount(2);
|
||||||
format.setSampleSize(16);
|
format.setSampleSize(16);
|
||||||
format.setCodec("audio/pcm");
|
format.setCodec("audio/pcm");
|
||||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||||
format.setSampleType(QAudioFormat::SignedInt);
|
format.setSampleType(QAudioFormat::SignedInt);
|
||||||
|
|
||||||
if(!devInfo.isFormatSupported(format)) {
|
if (!devInfo.isFormatSupported(format))
|
||||||
qWarning("48kHz S16_LE audio format not supported");
|
{
|
||||||
|
qWarning("AudioOutput::start: %d Hz S16_LE audio format not supported", rate);
|
||||||
format = devInfo.nearestFormat(format);
|
format = devInfo.nearestFormat(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(format.sampleSize() != 16) {
|
if (format.sampleSize() != 16)
|
||||||
qWarning("Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName()));
|
{
|
||||||
|
qWarning("AudioOutput::start: Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_audioOutput = new QAudioOutput(format);
|
m_audioOutput = new QAudioOutput(devInfo, format);
|
||||||
|
|
||||||
QIODevice::open(QIODevice::ReadOnly);
|
QIODevice::open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
@ -80,11 +109,13 @@ void AudioOutput::stop()
|
|||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
if(m_audioOutput != NULL) {
|
if (m_audioOutput != 0)
|
||||||
|
{
|
||||||
m_audioOutput->stop();
|
m_audioOutput->stop();
|
||||||
delete m_audioOutput;
|
delete m_audioOutput;
|
||||||
m_audioOutput = NULL;
|
m_audioOutput = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QIODevice::close();
|
QIODevice::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,34 +133,53 @@ void AudioOutput::removeFifo(AudioFifo* audioFifo)
|
|||||||
m_audioFifos.remove(audioFifo);
|
m_audioFifos.remove(audioFifo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
bool AudioOutput::open(OpenMode mode)
|
bool AudioOutput::open(OpenMode mode)
|
||||||
{
|
{
|
||||||
Q_UNUSED(mode);
|
Q_UNUSED(mode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
qint64 AudioOutput::readData(char* data, qint64 maxLen)
|
qint64 AudioOutput::readData(char* data, qint64 maxLen)
|
||||||
{
|
{
|
||||||
|
//qDebug("AudioOutput::readData: %lld", maxLen);
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
|
|
||||||
unsigned int framesPerBuffer = maxLen / 4;
|
unsigned int framesPerBuffer = maxLen / 4;
|
||||||
if(framesPerBuffer == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if(m_mixBuffer.size() < framesPerBuffer * 2) {
|
if (framesPerBuffer == 0)
|
||||||
m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo)
|
{
|
||||||
if(m_mixBuffer.size() != framesPerBuffer * 2)
|
return 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
|
memset(&m_mixBuffer[0], 0x00, 2 * framesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence
|
||||||
|
|
||||||
// sum up a block from all fifos
|
// 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
|
// use outputBuffer as temp - yes, one memcpy could be saved
|
||||||
uint samples = (*it)->read((quint8*)data, framesPerBuffer, 1);
|
uint samples = (*it)->read((quint8*) data, framesPerBuffer, 1);
|
||||||
const qint16* src = (const qint16*)data;
|
const qint16* src = (const qint16*) data;
|
||||||
std::vector<qint32>::iterator dst = m_mixBuffer.begin();
|
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;
|
*dst += *src;
|
||||||
++src;
|
++src;
|
||||||
++dst;
|
++dst;
|
||||||
@ -140,22 +190,40 @@ qint64 AudioOutput::readData(char* data, qint64 maxLen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert to int16
|
// convert to int16
|
||||||
|
|
||||||
std::vector<qint32>::const_iterator src = m_mixBuffer.begin();
|
std::vector<qint32>::const_iterator src = m_mixBuffer.begin();
|
||||||
qint16* dst = (qint16*)data;
|
qint16* dst = (qint16*) data;
|
||||||
for(uint i = 0; i < framesPerBuffer; i++) {
|
|
||||||
|
for (uint i = 0; i < framesPerBuffer; i++)
|
||||||
|
{
|
||||||
// left channel
|
// left channel
|
||||||
|
|
||||||
qint32 s = *src++;
|
qint32 s = *src++;
|
||||||
|
|
||||||
if(s < -32768)
|
if(s < -32768)
|
||||||
|
{
|
||||||
s = -32768;
|
s = -32768;
|
||||||
else if(s > 32767)
|
}
|
||||||
|
else if (s > 32767)
|
||||||
|
{
|
||||||
s = 32767;
|
s = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
*dst++ = s;
|
*dst++ = s;
|
||||||
|
|
||||||
// right channel
|
// right channel
|
||||||
|
|
||||||
s = *src++;
|
s = *src++;
|
||||||
|
|
||||||
if(s < -32768)
|
if(s < -32768)
|
||||||
|
{
|
||||||
s = -32768;
|
s = -32768;
|
||||||
else if(s > 32767)
|
}
|
||||||
|
else if (s > 32767)
|
||||||
|
{
|
||||||
s = 32767;
|
s = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
*dst++ = s;
|
*dst++ = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,16 +271,25 @@ void DSPEngine::work()
|
|||||||
if (part1begin != part1end)
|
if (part1begin != part1end)
|
||||||
{
|
{
|
||||||
// correct stuff
|
// correct stuff
|
||||||
if (m_dcOffsetCorrection) {
|
if (m_dcOffsetCorrection)
|
||||||
|
{
|
||||||
dcOffset(part1begin, part1end);
|
dcOffset(part1begin, part1end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_iqImbalanceCorrection) {
|
if (m_iqImbalanceCorrection)
|
||||||
|
{
|
||||||
imbalance(part1begin, part1end);
|
imbalance(part1begin, part1end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// feed data to handlers
|
// feed data to direct sinks
|
||||||
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
|
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);
|
(*it)->feed(part1begin, part1end, positiveOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,16 +298,25 @@ void DSPEngine::work()
|
|||||||
if(part2begin != part2end)
|
if(part2begin != part2end)
|
||||||
{
|
{
|
||||||
// correct stuff
|
// correct stuff
|
||||||
if(m_dcOffsetCorrection) {
|
if (m_dcOffsetCorrection)
|
||||||
|
{
|
||||||
dcOffset(part2begin, part2end);
|
dcOffset(part2begin, part2end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_iqImbalanceCorrection) {
|
if (m_iqImbalanceCorrection)
|
||||||
|
{
|
||||||
imbalance(part2begin, part2end);
|
imbalance(part2begin, part2end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// feed data to handlers
|
// feed data to direct sinks
|
||||||
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
|
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);
|
(*it)->feed(part2begin, part2end, positiveOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,7 +362,7 @@ DSPEngine::State DSPEngine::gotoIdle()
|
|||||||
|
|
||||||
m_sampleSource->stop();
|
m_sampleSource->stop();
|
||||||
m_deviceDescription.clear();
|
m_deviceDescription.clear();
|
||||||
m_audioSink.stop();
|
m_audioOutput.stop();
|
||||||
m_sampleRate = 0;
|
m_sampleRate = 0;
|
||||||
|
|
||||||
return StIdle;
|
return StIdle;
|
||||||
@ -444,7 +462,7 @@ DSPEngine::State DSPEngine::gotoRunning()
|
|||||||
return gotoError("Could not start sample source");
|
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++)
|
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))
|
else if (DSPAddAudioSink::match(*message))
|
||||||
{
|
{
|
||||||
m_audioSink.addFifo(((DSPAddAudioSink*) message)->getAudioFifo());
|
m_audioOutput.addFifo(((DSPAddAudioSink*) message)->getAudioFifo());
|
||||||
}
|
}
|
||||||
else if (DSPRemoveAudioSink::match(*message))
|
else if (DSPRemoveAudioSink::match(*message))
|
||||||
{
|
{
|
||||||
m_audioSink.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo());
|
m_audioOutput.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_syncMessenger.done(m_state);
|
m_syncMessenger.done(m_state);
|
||||||
|
@ -38,7 +38,7 @@ void ThreadedSampleSink::run()
|
|||||||
exec();
|
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);
|
m_sampleSink->feed(begin, end, positiveOnly);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user