kopia lustrzana https://github.com/f4exb/sdrangel
Run audio out on its own thread. Fixes #1717
rodzic
a8665ed898
commit
e79dfd4fee
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue