mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 04:50:29 -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 "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
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user