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:
parent
a8665ed898
commit
e79dfd4fee
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user