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()
{
QMap<int, AudioOutputDevice*>::iterator ait = m_audioOutputs.begin();
QMap<int, AudioOutputDevice*>::iterator aoit = m_audioOutputs.begin();
for (; ait != m_audioOutputs.end(); ++ait) {
(*ait)->getInputMessageQueue()->push(AudioOutputDevice::MsgStop::create());
for (; aoit != m_audioOutputs.end(); ++aoit) {
(*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();
(*it)->wait();
(*otit)->exit();
(*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);
if (m_audioInputs.find(inputDeviceIndex) == m_audioInputs.end()) {
m_audioInputs[inputDeviceIndex] = new AudioInputDevice();
if (m_audioInputs.find(inputDeviceIndex) == m_audioInputs.end())
{
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) &&
@ -488,9 +531,10 @@ void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
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_audioInputInfos[deviceName].sampleRate = m_audioInputs[inputDeviceIndex]->getRate(); // FIXME
m_audioInputInfos[deviceName].volume = volume;
m_defaultInputStarted = (inputDeviceIndex == -1);
}
@ -502,7 +546,8 @@ void AudioDeviceManager::startAudioInput(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
@ -621,18 +666,11 @@ void AudioDeviceManager::setInputDeviceInfo(int inputDeviceIndex, const InputDev
if (oldDeviceInfo.sampleRate != deviceInfo.sampleRate)
{
audioInput->stop();
audioInput->start(inputDeviceIndex, deviceInfo.sampleRate);
m_audioInputInfos[deviceName].sampleRate = audioInput->getRate(); // store actual sample rate
AudioInputDevice::MsgStop *msgStop = AudioInputDevice::MsgStop::create();
audioInput->getInputMessageQueue()->push(msgStop);
// 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);
}
AudioInputDevice::MsgStart *msgStart = AudioInputDevice::MsgStart::create(inputDeviceIndex, deviceInfo.sampleRate);
audioInput->getInputMessageQueue()->push(msgStart);
}
audioInput->setVolume(deviceInfo.volume);
@ -743,18 +781,6 @@ void AudioDeviceManager::unsetInputDeviceInfo(int inputDeviceIndex)
stopAudioInput(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()
@ -866,6 +892,25 @@ bool AudioDeviceManager::handleMessage(const Message& msg)
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;
}

View File

@ -146,6 +146,7 @@ private:
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, 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
bool m_defaultOutputStarted; //!< True if the default audio output (-1) has already been started

View File

@ -18,6 +18,7 @@
#include <string.h>
#include <QDebug>
#include <QAudioFormat>
#include <QThread>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSource>
#else
@ -27,6 +28,10 @@
#include "audio/audiodeviceinfo.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() :
m_audioInput(0),
m_audioUsageCount(0),
@ -34,26 +39,28 @@ AudioInputDevice::AudioInputDevice() :
m_volume(0.5f),
m_audioFifos()
{
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
}
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)
{
delete *it;
}
// for (std::list<AudioFifo*>::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
// {
// delete *it;
// }
m_audioFifos.clear();
// m_audioFifos.clear();
}
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);
AudioDeviceInfo devInfo;
@ -127,40 +134,52 @@ bool AudioInputDevice::start(int device, int rate)
#endif
m_audioInput->setVolume(m_volume);
QIODevice::open(QIODevice::ReadWrite);
QIODevice::open(QIODevice::ReadWrite | QIODevice::Unbuffered);
m_audioInput->start(this);
if (m_audioInput->state() != QAudio::ActiveState)
{
if (m_audioInput->state() != QAudio::ActiveState) {
qWarning("AudioInputDevice::start: cannot start");
} else {
qDebug("AudioInputDevice::start: started buffer: %d bytes", m_audioInput->bufferSize());
}
}
// }
m_audioUsageCount++;
// m_audioUsageCount++;
return true;
}
void AudioInputDevice::stop()
{
qDebug("AudioInputDevice::stop");
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;
}
}
if (!m_audioInput) {
return;
}
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)
@ -222,3 +241,32 @@ void AudioInputDevice::setVolume(float volume)
if (m_audioInput != nullptr)
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 <vector>
#include "export.h"
#include "util/message.h"
#include "util/messagequeue.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
class QAudioSource;
@ -35,12 +37,76 @@ class AudioOutputPipe;
class SDRBASE_API AudioInputDevice : 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()
{ }
};
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();
virtual ~AudioInputDevice();
bool start(int device, int rate);
void stop();
bool startDirect(int deviceIndex, int sampleRate) {
return start(deviceIndex, sampleRate);
}
void stopDirect() {
stop();
}
void addFifo(AudioFifo* audioFifo);
void removeFifo(AudioFifo* audioFifo);
@ -49,6 +115,10 @@ public:
uint getRate() const { return m_audioFormat.sampleRate(); }
void setOnExit(bool onExit) { m_onExit = onExit; }
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:
QRecursiveMutex m_mutex;
@ -65,12 +135,25 @@ private:
std::vector<qint32> m_mixBuffer;
QAudioFormat m_audioFormat;
QString m_deviceName;
MessageQueue m_inputMessageQueue;
MessageQueue *m_managerMessageQueue;
//virtual bool open(OpenMode mode);
virtual qint64 readData(char* data, qint64 maxLen);
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 AudioInput;
friend class FCDProInput;
friend class FCDProPlusInput;
private slots:
void handleInputMessages();
};