mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 20:40:20 -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