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 "dsp/dspcommands.h"
#include <QThread>
#include <QDataStream>
#include <QSet>
#include <QDebug>
@ -99,10 +100,18 @@ 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) {
delete(*it);
for (; ait != m_audioOutputs.end(); ++ait) {
(*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);
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

View File

@ -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<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, 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<AudioFifo*, int> m_audioSourceFifos; //< audio source FIFO to audio input device index-1 map

View File

@ -17,6 +17,7 @@
///////////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <QThread>
#include <QAudioFormat>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSink>
@ -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;
}
}
}

View File

@ -25,6 +25,8 @@
#include <list>
#include <vector>
#include <stdint.h>
#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<qint32> 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