Run audio in on its own thread. For #1731

This commit is contained in:
f4exb 2023-07-10 01:06:19 +02:00
parent 9a45a29ce2
commit e51e8c23b2
4 changed files with 244 additions and 67 deletions

View File

@ -102,18 +102,32 @@ AudioDeviceManager::AudioDeviceManager()
AudioDeviceManager::~AudioDeviceManager() AudioDeviceManager::~AudioDeviceManager()
{ {
QMap<int, AudioOutputDevice*>::iterator ait = m_audioOutputs.begin(); QMap<int, AudioOutputDevice*>::iterator aoit = m_audioOutputs.begin();
for (; ait != m_audioOutputs.end(); ++ait) { for (; aoit != m_audioOutputs.end(); ++aoit) {
(*ait)->getInputMessageQueue()->push(AudioOutputDevice::MsgStop::create()); (*aoit)->getInputMessageQueue()->push(AudioOutputDevice::MsgStop::create());
} }
QMap<int, QThread*>::iterator it = m_audioOutputThreads.begin(); QMap<int, QThread*>::iterator otit = m_audioOutputThreads.begin();
for (; it != m_audioOutputThreads.end(); ++it) for (; otit != m_audioOutputThreads.end(); ++otit)
{ {
(*it)->exit(); (*otit)->exit();
(*it)->wait(); (*otit)->wait();
}
QMap<int, AudioInputDevice*>::iterator aiit = m_audioInputs.begin();
for (; aiit != m_audioInputs.end(); ++aiit) {
(*aiit)->getInputMessageQueue()->push(AudioInputDevice::MsgStop::create());
}
QMap<int, QThread*>::iterator itit = m_audioInputThreads.begin();
for (; itit != m_audioInputThreads.end(); ++itit)
{
(*itit)->exit();
(*itit)->wait();
} }
} }
@ -356,8 +370,37 @@ void AudioDeviceManager::addAudioSource(AudioFifo* audioFifo, MessageQueue *samp
{ {
qDebug("AudioDeviceManager::addAudioSource: %d: %p", inputDeviceIndex, audioFifo); qDebug("AudioDeviceManager::addAudioSource: %d: %p", inputDeviceIndex, audioFifo);
if (m_audioInputs.find(inputDeviceIndex) == m_audioInputs.end()) { if (m_audioInputs.find(inputDeviceIndex) == m_audioInputs.end())
m_audioInputs[inputDeviceIndex] = new AudioInputDevice(); {
QThread *thread = new QThread();
AudioInputDevice *audioInputDevice = new AudioInputDevice();
m_audioInputs[inputDeviceIndex] = audioInputDevice;
m_audioInputThreads[inputDeviceIndex] = thread;
if (inputDeviceIndex < 0) {
audioInputDevice->setDeviceName("System default");
} else {
audioInputDevice->setDeviceName(m_outputDevicesInfo[inputDeviceIndex].deviceName());
}
qDebug("AudioDeviceManager::addAudioSource: new AudioInputDevice on thread: %p", thread);
audioInputDevice->setManagerMessageQueue(&m_inputMessageQueue);
audioInputDevice->moveToThread(thread);
QObject::connect(
thread,
&QThread::finished,
audioInputDevice,
&QObject::deleteLater
);
QObject::connect(
thread,
&QThread::finished,
thread,
&QThread::deleteLater
);
thread->start();
} }
if ((m_audioInputs[inputDeviceIndex]->getNbFifos() == 0) && if ((m_audioInputs[inputDeviceIndex]->getNbFifos() == 0) &&
@ -488,9 +531,10 @@ void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
volume = m_audioInputInfos[deviceName].volume; volume = m_audioInputInfos[deviceName].volume;
} }
m_audioInputs[inputDeviceIndex]->start(inputDeviceIndex, sampleRate); AudioInputDevice::MsgStart *msg = AudioInputDevice::MsgStart::create(inputDeviceIndex, sampleRate);
m_audioInputs[inputDeviceIndex]->getInputMessageQueue()->push(msg);
m_audioInputs[inputDeviceIndex]->setVolume(volume); m_audioInputs[inputDeviceIndex]->setVolume(volume);
m_audioInputInfos[deviceName].sampleRate = m_audioInputs[inputDeviceIndex]->getRate(); // FIXME
m_audioInputInfos[deviceName].volume = volume; m_audioInputInfos[deviceName].volume = volume;
m_defaultInputStarted = (inputDeviceIndex == -1); m_defaultInputStarted = (inputDeviceIndex == -1);
} }
@ -502,7 +546,8 @@ void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
void AudioDeviceManager::stopAudioInput(int inputDeviceIndex) void AudioDeviceManager::stopAudioInput(int inputDeviceIndex)
{ {
m_audioInputs[inputDeviceIndex]->stop(); AudioInputDevice::MsgStop *msg = AudioInputDevice::MsgStop::create();
m_audioInputs[inputDeviceIndex]->getInputMessageQueue()->push(msg);
} }
bool AudioDeviceManager::getInputDeviceInfo(const QString& deviceName, InputDeviceInfo& deviceInfo) const bool AudioDeviceManager::getInputDeviceInfo(const QString& deviceName, InputDeviceInfo& deviceInfo) const
@ -621,18 +666,11 @@ void AudioDeviceManager::setInputDeviceInfo(int inputDeviceIndex, const InputDev
if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate) if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate)
{ {
audioInput->stop(); AudioInputDevice::MsgStop *msgStop = AudioInputDevice::MsgStop::create();
audioInput->start(inputDeviceIndex, deviceInfo.sampleRate); audioInput->getInputMessageQueue()->push(msgStop);
m_audioInputInfos[deviceName].sampleRate = audioInput->getRate(); // store actual sample rate
// send message to attached channels AudioInputDevice::MsgStart *msgStart = AudioInputDevice::MsgStart::create(inputDeviceIndex, deviceInfo.sampleRate);
QList<MessageQueue *>::const_iterator it = m_inputDeviceSourceMessageQueues[inputDeviceIndex].begin(); audioInput->getInputMessageQueue()->push(msgStart);
for (; it != m_inputDeviceSourceMessageQueues[inputDeviceIndex].end(); ++it)
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioInputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioInput);
(*it)->push(msg);
}
} }
audioInput->setVolume(deviceInfo.volume); audioInput->setVolume(deviceInfo.volume);
@ -743,18 +781,6 @@ void AudioDeviceManager::unsetInputDeviceInfo(int inputDeviceIndex)
stopAudioInput(inputDeviceIndex); stopAudioInput(inputDeviceIndex);
startAudioInput(inputDeviceIndex); startAudioInput(inputDeviceIndex);
if (oldDeviceInfo.sampleRate != m_audioInputInfos[deviceName].sampleRate)
{
// send message to attached channels
QList<MessageQueue *>::const_iterator it = m_inputDeviceSourceMessageQueues[inputDeviceIndex].begin();
for (; it != m_inputDeviceSourceMessageQueues[inputDeviceIndex].end(); ++it)
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioInputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioInput);
(*it)->push(msg);
}
}
} }
void AudioDeviceManager::inputInfosCleanup() void AudioDeviceManager::inputInfosCleanup()
@ -866,6 +892,25 @@ bool AudioDeviceManager::handleMessage(const Message& msg)
return true; return true;
} }
else if (AudioInputDevice::MsgReportSampleRate::match(msg))
{
AudioInputDevice::MsgReportSampleRate& report = (AudioInputDevice::MsgReportSampleRate&) msg;
int deviceIndex = report.getDeviceIndex();
const QString& deviceName = report.getDeviceName();
int sampleRate = report.getSampleRate();
qDebug("AudioDeviceManager::handleMessage: AudioInputDevice::MsgReportSampleRate: device(%d) %s: rate: %d",
deviceIndex, qPrintable(deviceName), sampleRate);
m_audioInputInfos[deviceName].sampleRate = sampleRate;
// send message to attached channels
for (auto& messageQueue : m_inputDeviceSourceMessageQueues[deviceIndex])
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioInputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioInput);
messageQueue->push(msg);
}
return true;
}
return false; return false;
} }

View File

@ -146,6 +146,7 @@ private:
QMap<AudioFifo*, MessageQueue*> m_audioFifoToSourceMessageQueues; //!< audio source FIFO to attached source message queue QMap<AudioFifo*, MessageQueue*> m_audioFifoToSourceMessageQueues; //!< audio source FIFO to attached source message queue
QMap<int, QList<MessageQueue*> > m_inputDeviceSourceMessageQueues; //!< sink message queues attached to device QMap<int, QList<MessageQueue*> > m_inputDeviceSourceMessageQueues; //!< sink message queues attached to device
QMap<int, AudioInputDevice*> m_audioInputs; //!< audio device index to audio input map (index -1 is default device) QMap<int, AudioInputDevice*> m_audioInputs; //!< audio device index to audio input map (index -1 is default device)
QMap<int, QThread*> m_audioInputThreads; //!< audio device index to audio input threads map
QMap<QString, InputDeviceInfo> m_audioInputInfos; //!< audio device name to audio input device info QMap<QString, InputDeviceInfo> m_audioInputInfos; //!< audio device name to audio input device info
bool m_defaultOutputStarted; //!< True if the default audio output (-1) has already been started bool m_defaultOutputStarted; //!< True if the default audio output (-1) has already been started

View File

@ -18,6 +18,7 @@
#include <string.h> #include <string.h>
#include <QDebug> #include <QDebug>
#include <QAudioFormat> #include <QAudioFormat>
#include <QThread>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSource> #include <QAudioSource>
#else #else
@ -27,6 +28,10 @@
#include "audio/audiodeviceinfo.h" #include "audio/audiodeviceinfo.h"
#include "audio/audiofifo.h" #include "audio/audiofifo.h"
MESSAGE_CLASS_DEFINITION(AudioInputDevice::MsgStart, Message)
MESSAGE_CLASS_DEFINITION(AudioInputDevice::MsgStop, Message)
MESSAGE_CLASS_DEFINITION(AudioInputDevice::MsgReportSampleRate, Message)
AudioInputDevice::AudioInputDevice() : AudioInputDevice::AudioInputDevice() :
m_audioInput(0), m_audioInput(0),
m_audioUsageCount(0), m_audioUsageCount(0),
@ -34,26 +39,28 @@ AudioInputDevice::AudioInputDevice() :
m_volume(0.5f), m_volume(0.5f),
m_audioFifos() m_audioFifos()
{ {
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
} }
AudioInputDevice::~AudioInputDevice() AudioInputDevice::~AudioInputDevice()
{ {
stop(); // stop();
QMutexLocker mutexLocker(&m_mutex); // QMutexLocker mutexLocker(&m_mutex);
for (std::list<AudioFifo*>::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) // for (std::list<AudioFifo*>::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
{ // {
delete *it; // delete *it;
} // }
m_audioFifos.clear(); // m_audioFifos.clear();
} }
bool AudioInputDevice::start(int device, int rate) bool AudioInputDevice::start(int device, int rate)
{ {
if (m_audioUsageCount == 0) // if (m_audioUsageCount == 0)
{ // {
qDebug("AudioInputDevice::start: device: %d rate: %d thread: %p", device, rate, QThread::currentThread());
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
AudioDeviceInfo devInfo; AudioDeviceInfo devInfo;
@ -127,40 +134,52 @@ bool AudioInputDevice::start(int device, int rate)
#endif #endif
m_audioInput->setVolume(m_volume); m_audioInput->setVolume(m_volume);
QIODevice::open(QIODevice::ReadWrite); QIODevice::open(QIODevice::ReadWrite | QIODevice::Unbuffered);
m_audioInput->start(this); m_audioInput->start(this);
if (m_audioInput->state() != QAudio::ActiveState) if (m_audioInput->state() != QAudio::ActiveState) {
{
qWarning("AudioInputDevice::start: cannot start"); qWarning("AudioInputDevice::start: cannot start");
} else {
qDebug("AudioInputDevice::start: started buffer: %d bytes", m_audioInput->bufferSize());
} }
} // }
m_audioUsageCount++; // m_audioUsageCount++;
return true; return true;
} }
void AudioInputDevice::stop() void AudioInputDevice::stop()
{ {
qDebug("AudioInputDevice::stop"); if (!m_audioInput) {
return;
if (m_audioUsageCount > 0)
{
m_audioUsageCount--;
if (m_audioUsageCount == 0)
{
qDebug("AudioInputDevice::stop: effectively close QIODevice");
QMutexLocker mutexLocker(&m_mutex);
QIODevice::close();
if (!m_onExit) {
delete m_audioInput;
}
}
} }
qDebug("AudioInputDevice::stop: thread: %p", QThread::currentThread());
QMutexLocker mutexLocker(&m_mutex);
m_audioInput->stop();
QIODevice::close();
delete m_audioInput;
m_audioInput = nullptr;
// if (m_audioUsageCount > 0)
// {
// m_audioUsageCount--;
// if (m_audioUsageCount == 0)
// {
// qDebug("AudioInputDevice::stop: effectively close QIODevice");
// QMutexLocker mutexLocker(&m_mutex);
// QIODevice::close();
// if (!m_onExit)
// {
// delete m_audioInput;
// m_audioInput = nullptr;
// }
// }
// }
} }
void AudioInputDevice::addFifo(AudioFifo* audioFifo) void AudioInputDevice::addFifo(AudioFifo* audioFifo)
@ -222,3 +241,32 @@ void AudioInputDevice::setVolume(float volume)
if (m_audioInput != nullptr) if (m_audioInput != nullptr)
m_audioInput->setVolume(m_volume); m_audioInput->setVolume(m_volume);
} }
bool AudioInputDevice::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 AudioInputDevice::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}

View File

@ -24,6 +24,8 @@
#include <list> #include <list>
#include <vector> #include <vector>
#include "export.h" #include "export.h"
#include "util/message.h"
#include "util/messagequeue.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
class QAudioSource; class QAudioSource;
@ -35,12 +37,76 @@ class AudioOutputPipe;
class SDRBASE_API AudioInputDevice : public QIODevice { class SDRBASE_API AudioInputDevice : 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()
{ }
};
class MsgReportSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getDeviceIndex() const { return m_deviceIndex; }
const QString& getDeviceName() const { return m_deviceName; }
int getSampleRate() const { return m_sampleRate; }
static MsgReportSampleRate* create(int deviceIndex, const QString& deviceName, int sampleRate) {
return new MsgReportSampleRate(deviceIndex, deviceName, sampleRate);
}
private:
int m_deviceIndex;
QString m_deviceName;
int m_sampleRate;
MsgReportSampleRate(int deviceIndex, const QString& deviceName, int sampleRate) :
Message(),
m_deviceIndex(deviceIndex),
m_deviceName(deviceName),
m_sampleRate(sampleRate)
{ }
};
AudioInputDevice(); AudioInputDevice();
virtual ~AudioInputDevice(); virtual ~AudioInputDevice();
bool start(int device, int rate); bool startDirect(int deviceIndex, int sampleRate) {
void stop(); return start(deviceIndex, sampleRate);
}
void stopDirect() {
stop();
}
void addFifo(AudioFifo* audioFifo); void addFifo(AudioFifo* audioFifo);
void removeFifo(AudioFifo* audioFifo); void removeFifo(AudioFifo* audioFifo);
@ -49,6 +115,10 @@ public:
uint getRate() const { return m_audioFormat.sampleRate(); } uint getRate() const { return m_audioFormat.sampleRate(); }
void setOnExit(bool onExit) { m_onExit = onExit; } void setOnExit(bool onExit) { m_onExit = onExit; }
void setVolume(float volume); void setVolume(float volume);
void setDeviceName(const QString& deviceName) { m_deviceName = deviceName;}
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setManagerMessageQueue(MessageQueue *messageQueue) { m_managerMessageQueue = messageQueue; }
private: private:
QRecursiveMutex m_mutex; QRecursiveMutex m_mutex;
@ -65,12 +135,25 @@ private:
std::vector<qint32> m_mixBuffer; std::vector<qint32> m_mixBuffer;
QAudioFormat m_audioFormat; QAudioFormat m_audioFormat;
QString m_deviceName;
MessageQueue m_inputMessageQueue;
MessageQueue *m_managerMessageQueue;
//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);
bool handleMessage(const Message& cmd);
bool start(int deviceIndex, int sampleRate);
void stop();
friend class AudioOutputPipe; friend class AudioOutputPipe;
friend class AudioInput;
friend class FCDProInput;
friend class FCDProPlusInput;
private slots:
void handleInputMessages();
}; };