diff --git a/sdrbase/audio/audiodevicemanager.cpp b/sdrbase/audio/audiodevicemanager.cpp index 382b756ec..d83003ad3 100644 --- a/sdrbase/audio/audiodevicemanager.cpp +++ b/sdrbase/audio/audiodevicemanager.cpp @@ -21,6 +21,7 @@ #include "util/messagequeue.h" #include "dsp/dspcommands.h" +#include #include #include #include @@ -99,10 +100,18 @@ AudioDeviceManager::AudioDeviceManager() AudioDeviceManager::~AudioDeviceManager() { - QMap::iterator it = m_audioOutputs.begin(); + QMap::iterator ait = m_audioOutputs.begin(); - for (; it != m_audioOutputs.end(); ++it) { - delete(*it); + for (; ait != m_audioOutputs.end(); ++ait) { + (*ait)->getInputMessageQueue()->push(AudioOutputDevice::MsgStop::create()); + } + + QMap::iterator it = m_audioOutputThreads.begin(); + + for (; it != m_audioOutputThreads.end(); ++it) + { + (*it)->exit(); + (*it)->wait(); } } @@ -258,8 +267,36 @@ void AudioDeviceManager::addAudioSink(AudioFifo* audioFifo, MessageQueue *sample { qDebug("AudioDeviceManager::addAudioSink: %d: %p", outputDeviceIndex, audioFifo); - if (m_audioOutputs.find(outputDeviceIndex) == m_audioOutputs.end()) { - m_audioOutputs[outputDeviceIndex] = new AudioOutputDevice(); + if (m_audioOutputs.find(outputDeviceIndex) == m_audioOutputs.end()) + { + QThread *thread = new QThread(); + AudioOutputDevice *audioOutputDevice = new AudioOutputDevice(); + m_audioOutputs[outputDeviceIndex] = audioOutputDevice; + m_audioOutputThreads[outputDeviceIndex] = thread; + + if (outputDeviceIndex < 0) { + audioOutputDevice->setDeviceName("System default"); + } else { + audioOutputDevice->setDeviceName(m_outputDevicesInfo[outputDeviceIndex].deviceName()); + } + + qDebug("AudioDeviceManager::addAudioSink: new AudioOutputDevice on thread: %p", thread); + audioOutputDevice->moveToThread(thread); + + QObject::connect( + thread, + &QThread::finished, + audioOutputDevice, + &QObject::deleteLater + ); + QObject::connect( + thread, + &QThread::finished, + thread, + &QThread::deleteLater + ); + + thread->start(); } if ((m_audioOutputs[outputDeviceIndex]->getNbFifos() == 0) && @@ -405,8 +442,10 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex) decimationFactor = m_audioOutputInfos[deviceName].udpDecimationFactor; } - m_audioOutputs[outputDeviceIndex]->start(outputDeviceIndex, sampleRate); - m_audioOutputInfos[deviceName].sampleRate = m_audioOutputs[outputDeviceIndex]->getRate(); // update with actual rate + AudioOutputDevice::MsgStart *msg = AudioOutputDevice::MsgStart::create(outputDeviceIndex, sampleRate); + m_audioOutputs[outputDeviceIndex]->getInputMessageQueue()->push(msg); + + m_audioOutputInfos[deviceName].sampleRate = sampleRate; // FIXME: possible change of sample rate in AudioOutputDevice m_audioOutputInfos[deviceName].udpAddress = udpAddress; m_audioOutputInfos[deviceName].udpPort = udpPort; m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP; @@ -424,7 +463,8 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex) void AudioDeviceManager::stopAudioOutput(int outputDeviceIndex) { - m_audioOutputs[outputDeviceIndex]->stop(); + AudioOutputDevice::MsgStop *msg = AudioOutputDevice::MsgStop::create(); + m_audioOutputs[outputDeviceIndex]->getInputMessageQueue()->push(msg); } void AudioDeviceManager::startAudioInput(int inputDeviceIndex) @@ -626,8 +666,12 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate) { - audioOutput->stop(); - audioOutput->start(outputDeviceIndex, deviceInfo.sampleRate); + AudioOutputDevice::MsgStop *msgStop = AudioOutputDevice::MsgStop::create(); + audioOutput->getInputMessageQueue()->push(msgStop); + + AudioOutputDevice::MsgStart *msgStart = AudioOutputDevice::MsgStart::create(outputDeviceIndex, deviceInfo.sampleRate); + audioOutput->getInputMessageQueue()->push(msgStart); + m_audioOutputInfos[deviceName].sampleRate = audioOutput->getRate(); // store actual sample rate // send message to attached channels diff --git a/sdrbase/audio/audiodevicemanager.h b/sdrbase/audio/audiodevicemanager.h index 6f260deee..02b9ebe7b 100644 --- a/sdrbase/audio/audiodevicemanager.h +++ b/sdrbase/audio/audiodevicemanager.h @@ -28,6 +28,7 @@ #include "audio/audiodeviceinfo.h" #include "export.h" +class QThread; class QDataStream; class AudioFifo; class MessageQueue; @@ -136,6 +137,7 @@ private: QMap m_audioFifoToSinkMessageQueues; //!< audio sink FIFO to attached sink message queue QMap > m_outputDeviceSinkMessageQueues; //!< sink message queues attached to device QMap m_audioOutputs; //!< audio device index to audio output map (index -1 is default device) + QMap m_audioOutputThreads; //!< audio device index to audio output threads map QMap m_audioOutputInfos; //!< audio device name to audio output info QMap m_audioSourceFifos; //< audio source FIFO to audio input device index-1 map diff --git a/sdrbase/audio/audiooutputdevice.cpp b/sdrbase/audio/audiooutputdevice.cpp index 32d32c840..8af10e2ad 100644 --- a/sdrbase/audio/audiooutputdevice.cpp +++ b/sdrbase/audio/audiooutputdevice.cpp @@ -17,6 +17,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include @@ -29,6 +30,9 @@ #include "audionetsink.h" #include "dsp/wavfilerecord.h" +MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStart, Message) +MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStop, Message) + AudioOutputDevice::AudioOutputDevice() : m_audioOutput(nullptr), m_audioNetSink(nullptr), @@ -45,6 +49,7 @@ AudioOutputDevice::AudioOutputDevice() : m_recordSilenceCount(0), m_audioFifos() { + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } AudioOutputDevice::~AudioOutputDevice() @@ -63,8 +68,12 @@ AudioOutputDevice::~AudioOutputDevice() bool AudioOutputDevice::start(int device, int rate) { + // if (m_audioOutput) { + // return true; + // } // if (m_audioUsageCount == 0) // { + qDebug("AudioOutputDevice::start: device: %d rate: %d thread: %p", device, rate, QThread::currentThread()); QMutexLocker mutexLocker(&m_mutex); AudioDeviceInfo devInfo; @@ -149,6 +158,7 @@ bool AudioOutputDevice::start(int device, int rate) m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false); m_wavFileRecord = new WavFileRecord(m_audioFormat.sampleRate()); m_audioOutput->setVolume(m_volume); + m_audioOutput->setBufferSize(m_audioFormat.sampleRate() / 5); // min 200ms m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms QIODevice::open(QIODevice::ReadOnly); @@ -157,6 +167,8 @@ bool AudioOutputDevice::start(int device, int rate) if (m_audioOutput->state() != QAudio::ActiveState) { qWarning() << "AudioOutputDevice::start: cannot start - " << m_audioOutput->error(); + } else { + qDebug("AudioOutputDevice::start: started buffer: %d bytes", m_audioOutput->bufferSize()); } // } // @@ -167,7 +179,11 @@ bool AudioOutputDevice::start(int device, int rate) void AudioOutputDevice::stop() { - qDebug("AudioOutputDevice::stop"); + if (!m_audioOutput) { + return; + } + + qDebug("AudioOutputDevice::stop: thread: %p", QThread::currentThread()); QMutexLocker mutexLocker(&m_mutex); m_audioOutput->stop(); @@ -327,6 +343,7 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) //#ifndef __APPLE__ // QMutexLocker mutexLocker(&m_mutex); //#endif + // qDebug("AudioOutputDevice::readData: thread %p (%s)", (void *) QThread::currentThread(), qPrintable(m_deviceName)); unsigned int samplesPerBuffer = maxLen / 4; @@ -522,3 +539,32 @@ qint64 AudioOutputDevice::bytesAvailable() const } return available * 2 * 2; // 2 Channels of 16-bit data } + +bool AudioOutputDevice::handleMessage(const Message& cmd) +{ + if (MsgStart::match(cmd)) + { + MsgStart ctl = (MsgStart&) cmd; + start(ctl.getDeviceIndex(), ctl.getSampleRate()); + return true; + } + else if (MsgStop::match(cmd)) + { + stop(); + return true; + } + + return false; +} + +void AudioOutputDevice::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} diff --git a/sdrbase/audio/audiooutputdevice.h b/sdrbase/audio/audiooutputdevice.h index 32e7be13d..5d8bab6eb 100644 --- a/sdrbase/audio/audiooutputdevice.h +++ b/sdrbase/audio/audiooutputdevice.h @@ -25,6 +25,8 @@ #include #include #include +#include "util/message.h" +#include "util/messagequeue.h" #include "export.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -37,8 +39,44 @@ class AudioOutputPipe; class AudioNetSink; class WavFileRecord; -class SDRBASE_API AudioOutputDevice : QIODevice { +class SDRBASE_API AudioOutputDevice : public QIODevice { + Q_OBJECT public: + class MsgStart : public Message { + MESSAGE_CLASS_DECLARATION + public: + int getDeviceIndex() const { return m_deviceIndex; } + int getSampleRate() const { return m_sampleRate; } + + static MsgStart* create(int deviceIndex, int sampleRate) { + return new MsgStart(deviceIndex, sampleRate); + } + + private: + int m_deviceIndex; + int m_sampleRate; + + MsgStart(int deviceIndex, int sampleRate) : + Message(), + m_deviceIndex(deviceIndex), + m_sampleRate(sampleRate) + { } + }; + + class MsgStop : public Message { + MESSAGE_CLASS_DECLARATION + public: + static MsgStop* create() { + return new MsgStop(); + } + + private: + + MsgStop() : + Message() + { } + }; + enum UDPChannelMode { UDPChannelLeft, @@ -60,9 +98,6 @@ public: AudioOutputDevice(); virtual ~AudioOutputDevice(); - bool start(int device, int rate); - void stop(); - void addFifo(AudioFifo* audioFifo); void removeFifo(AudioFifo* audioFifo); int getNbFifos() const { return m_audioFifos.size(); } @@ -80,6 +115,9 @@ public: void setFileRecordName(const QString& fileRecordName); void setRecordToFile(bool recordToFile); void setRecordSilenceTime(int recordSilenceTime); + void setDeviceName(const QString& deviceName) { m_deviceName = deviceName;} + + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } private: QRecursiveMutex m_mutex; @@ -106,14 +144,24 @@ private: std::vector m_mixBuffer; QAudioFormat m_audioFormat; + QString m_deviceName; + + MessageQueue m_inputMessageQueue; //virtual bool open(OpenMode mode); virtual qint64 readData(char* data, qint64 maxLen); virtual qint64 writeData(const char* data, qint64 len); virtual qint64 bytesAvailable() const override; void writeSampleToFile(qint16 lSample, qint16 rSample); + bool handleMessage(const Message& cmd); + + bool start(int device, int rate); + void stop(); friend class AudioOutputPipe; + +private slots: + void handleInputMessages(); }; #endif // INCLUDE_AUDIOOUTPUTDEVICE_H