1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-08-01 13:32:33 -04:00

Run audio out on its own thread. Fixes #1717

This commit is contained in:
f4exb 2023-06-29 00:49:19 +02:00
parent a8665ed898
commit e79dfd4fee
4 changed files with 155 additions and 15 deletions

View File

@ -21,6 +21,7 @@
#include "util/messagequeue.h" #include "util/messagequeue.h"
#include "dsp/dspcommands.h" #include "dsp/dspcommands.h"
#include <QThread>
#include <QDataStream> #include <QDataStream>
#include <QSet> #include <QSet>
#include <QDebug> #include <QDebug>
@ -99,10 +100,18 @@ AudioDeviceManager::AudioDeviceManager()
AudioDeviceManager::~AudioDeviceManager() AudioDeviceManager::~AudioDeviceManager()
{ {
QMap<int, AudioOutputDevice*>::iterator it = m_audioOutputs.begin(); QMap<int, AudioOutputDevice*>::iterator ait = m_audioOutputs.begin();
for (; it != m_audioOutputs.end(); ++it) { for (; ait != m_audioOutputs.end(); ++ait) {
delete(*it); (*ait)->getInputMessageQueue()->push(AudioOutputDevice::MsgStop::create());
}
QMap<int, QThread*>::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); qDebug("AudioDeviceManager::addAudioSink: %d: %p", outputDeviceIndex, audioFifo);
if (m_audioOutputs.find(outputDeviceIndex) == m_audioOutputs.end()) { if (m_audioOutputs.find(outputDeviceIndex) == m_audioOutputs.end())
m_audioOutputs[outputDeviceIndex] = new AudioOutputDevice(); {
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) && if ((m_audioOutputs[outputDeviceIndex]->getNbFifos() == 0) &&
@ -405,8 +442,10 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
decimationFactor = m_audioOutputInfos[deviceName].udpDecimationFactor; decimationFactor = m_audioOutputInfos[deviceName].udpDecimationFactor;
} }
m_audioOutputs[outputDeviceIndex]->start(outputDeviceIndex, sampleRate); AudioOutputDevice::MsgStart *msg = AudioOutputDevice::MsgStart::create(outputDeviceIndex, sampleRate);
m_audioOutputInfos[deviceName].sampleRate = m_audioOutputs[outputDeviceIndex]->getRate(); // update with actual rate 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].udpAddress = udpAddress;
m_audioOutputInfos[deviceName].udpPort = udpPort; m_audioOutputInfos[deviceName].udpPort = udpPort;
m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP; m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP;
@ -424,7 +463,8 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
void AudioDeviceManager::stopAudioOutput(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) void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
@ -626,8 +666,12 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output
if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate) if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate)
{ {
audioOutput->stop(); AudioOutputDevice::MsgStop *msgStop = AudioOutputDevice::MsgStop::create();
audioOutput->start(outputDeviceIndex, deviceInfo.sampleRate); 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 m_audioOutputInfos[deviceName].sampleRate = audioOutput->getRate(); // store actual sample rate
// send message to attached channels // send message to attached channels

View File

@ -28,6 +28,7 @@
#include "audio/audiodeviceinfo.h" #include "audio/audiodeviceinfo.h"
#include "export.h" #include "export.h"
class QThread;
class QDataStream; class QDataStream;
class AudioFifo; class AudioFifo;
class MessageQueue; class MessageQueue;
@ -136,6 +137,7 @@ private:
QMap<AudioFifo*, MessageQueue*> m_audioFifoToSinkMessageQueues; //!< audio sink FIFO to attached sink message queue QMap<AudioFifo*, MessageQueue*> m_audioFifoToSinkMessageQueues; //!< audio sink FIFO to attached sink message queue
QMap<int, QList<MessageQueue*> > m_outputDeviceSinkMessageQueues; //!< sink message queues attached to device QMap<int, QList<MessageQueue*> > m_outputDeviceSinkMessageQueues; //!< sink message queues attached to device
QMap<int, AudioOutputDevice*> m_audioOutputs; //!< audio device index to audio output map (index -1 is default device) QMap<int, AudioOutputDevice*> m_audioOutputs; //!< audio device index to audio output map (index -1 is default device)
QMap<int, QThread*> m_audioOutputThreads; //!< audio device index to audio output threads map
QMap<QString, OutputDeviceInfo> m_audioOutputInfos; //!< audio device name to audio output info QMap<QString, OutputDeviceInfo> m_audioOutputInfos; //!< audio device name to audio output info
QMap<AudioFifo*, int> m_audioSourceFifos; //< audio source FIFO to audio input device index-1 map QMap<AudioFifo*, int> m_audioSourceFifos; //< audio source FIFO to audio input device index-1 map

View File

@ -17,6 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
#include <string.h> #include <string.h>
#include <QThread>
#include <QAudioFormat> #include <QAudioFormat>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSink> #include <QAudioSink>
@ -29,6 +30,9 @@
#include "audionetsink.h" #include "audionetsink.h"
#include "dsp/wavfilerecord.h" #include "dsp/wavfilerecord.h"
MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStart, Message)
MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStop, Message)
AudioOutputDevice::AudioOutputDevice() : AudioOutputDevice::AudioOutputDevice() :
m_audioOutput(nullptr), m_audioOutput(nullptr),
m_audioNetSink(nullptr), m_audioNetSink(nullptr),
@ -45,6 +49,7 @@ AudioOutputDevice::AudioOutputDevice() :
m_recordSilenceCount(0), m_recordSilenceCount(0),
m_audioFifos() m_audioFifos()
{ {
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
} }
AudioOutputDevice::~AudioOutputDevice() AudioOutputDevice::~AudioOutputDevice()
@ -63,8 +68,12 @@ AudioOutputDevice::~AudioOutputDevice()
bool AudioOutputDevice::start(int device, int rate) bool AudioOutputDevice::start(int device, int rate)
{ {
// if (m_audioOutput) {
// return true;
// }
// if (m_audioUsageCount == 0) // if (m_audioUsageCount == 0)
// { // {
qDebug("AudioOutputDevice::start: device: %d rate: %d thread: %p", device, rate, QThread::currentThread());
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
AudioDeviceInfo devInfo; AudioDeviceInfo devInfo;
@ -149,6 +158,7 @@ bool AudioOutputDevice::start(int device, int rate)
m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false); m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false);
m_wavFileRecord = new WavFileRecord(m_audioFormat.sampleRate()); m_wavFileRecord = new WavFileRecord(m_audioFormat.sampleRate());
m_audioOutput->setVolume(m_volume); 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 m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
QIODevice::open(QIODevice::ReadOnly); QIODevice::open(QIODevice::ReadOnly);
@ -157,6 +167,8 @@ bool AudioOutputDevice::start(int device, int rate)
if (m_audioOutput->state() != QAudio::ActiveState) { if (m_audioOutput->state() != QAudio::ActiveState) {
qWarning() << "AudioOutputDevice::start: cannot start - " << m_audioOutput->error(); 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() void AudioOutputDevice::stop()
{ {
qDebug("AudioOutputDevice::stop"); if (!m_audioOutput) {
return;
}
qDebug("AudioOutputDevice::stop: thread: %p", QThread::currentThread());
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
m_audioOutput->stop(); m_audioOutput->stop();
@ -327,6 +343,7 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen)
//#ifndef __APPLE__ //#ifndef __APPLE__
// QMutexLocker mutexLocker(&m_mutex); // QMutexLocker mutexLocker(&m_mutex);
//#endif //#endif
// qDebug("AudioOutputDevice::readData: thread %p (%s)", (void *) QThread::currentThread(), qPrintable(m_deviceName));
unsigned int samplesPerBuffer = maxLen / 4; unsigned int samplesPerBuffer = maxLen / 4;
@ -522,3 +539,32 @@ qint64 AudioOutputDevice::bytesAvailable() const
} }
return available * 2 * 2; // 2 Channels of 16-bit data 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;
}
}
}

View File

@ -25,6 +25,8 @@
#include <list> #include <list>
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
#include "util/message.h"
#include "util/messagequeue.h"
#include "export.h" #include "export.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@ -37,8 +39,44 @@ class AudioOutputPipe;
class AudioNetSink; class AudioNetSink;
class WavFileRecord; class WavFileRecord;
class SDRBASE_API AudioOutputDevice : QIODevice { class SDRBASE_API AudioOutputDevice : public QIODevice {
Q_OBJECT
public: 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 enum UDPChannelMode
{ {
UDPChannelLeft, UDPChannelLeft,
@ -60,9 +98,6 @@ public:
AudioOutputDevice(); AudioOutputDevice();
virtual ~AudioOutputDevice(); virtual ~AudioOutputDevice();
bool start(int device, int rate);
void stop();
void addFifo(AudioFifo* audioFifo); void addFifo(AudioFifo* audioFifo);
void removeFifo(AudioFifo* audioFifo); void removeFifo(AudioFifo* audioFifo);
int getNbFifos() const { return m_audioFifos.size(); } int getNbFifos() const { return m_audioFifos.size(); }
@ -80,6 +115,9 @@ public:
void setFileRecordName(const QString& fileRecordName); void setFileRecordName(const QString& fileRecordName);
void setRecordToFile(bool recordToFile); void setRecordToFile(bool recordToFile);
void setRecordSilenceTime(int recordSilenceTime); void setRecordSilenceTime(int recordSilenceTime);
void setDeviceName(const QString& deviceName) { m_deviceName = deviceName;}
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
private: private:
QRecursiveMutex m_mutex; QRecursiveMutex m_mutex;
@ -106,14 +144,24 @@ private:
std::vector<qint32> m_mixBuffer; std::vector<qint32> m_mixBuffer;
QAudioFormat m_audioFormat; QAudioFormat m_audioFormat;
QString m_deviceName;
MessageQueue m_inputMessageQueue;
//virtual bool open(OpenMode mode); //virtual bool open(OpenMode mode);
virtual qint64 readData(char* data, qint64 maxLen); virtual qint64 readData(char* data, qint64 maxLen);
virtual qint64 writeData(const char* data, qint64 len); virtual qint64 writeData(const char* data, qint64 len);
virtual qint64 bytesAvailable() const override; virtual qint64 bytesAvailable() const override;
void writeSampleToFile(qint16 lSample, qint16 rSample); void writeSampleToFile(qint16 lSample, qint16 rSample);
bool handleMessage(const Message& cmd);
bool start(int device, int rate);
void stop();
friend class AudioOutputPipe; friend class AudioOutputPipe;
private slots:
void handleInputMessages();
}; };
#endif // INCLUDE_AUDIOOUTPUTDEVICE_H #endif // INCLUDE_AUDIOOUTPUTDEVICE_H