mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	Audio output device recording: implementation (1)
This commit is contained in:
		
							parent
							
								
									e89331f58f
								
							
						
					
					
						commit
						4a383e439b
					
				| @ -641,6 +641,9 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output | |||||||
|     audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode); |     audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode); | ||||||
|     audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutputDevice::UDPChannelStereo, deviceInfo.sampleRate); |     audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutputDevice::UDPChannelStereo, deviceInfo.sampleRate); | ||||||
|     audioOutput->setUdpDecimation(deviceInfo.udpDecimationFactor); |     audioOutput->setUdpDecimation(deviceInfo.udpDecimationFactor); | ||||||
|  |     audioOutput->setFileRecordName(deviceInfo.fileRecordName); | ||||||
|  |     audioOutput->setRecordToFile(deviceInfo.recordToFile); | ||||||
|  |     audioOutput->setRecordSilenceTime(deviceInfo.recordSilenceTime); | ||||||
| 
 | 
 | ||||||
|     qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated", |     qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated", | ||||||
|             outputDeviceIndex, qPrintable(deviceName)); |             outputDeviceIndex, qPrintable(deviceName)); | ||||||
|  | |||||||
| @ -23,16 +23,22 @@ | |||||||
| #include "audiooutputdevice.h" | #include "audiooutputdevice.h" | ||||||
| #include "audiofifo.h" | #include "audiofifo.h" | ||||||
| #include "audionetsink.h" | #include "audionetsink.h" | ||||||
|  | #include "dsp/wavfilerecord.h" | ||||||
| 
 | 
 | ||||||
| AudioOutputDevice::AudioOutputDevice() : | AudioOutputDevice::AudioOutputDevice() : | ||||||
| 	m_audioOutput(0), | 	m_audioOutput(nullptr), | ||||||
| 	m_audioNetSink(0), | 	m_audioNetSink(nullptr), | ||||||
| 	m_copyAudioToUdp(false), |     m_wavFileRecord(nullptr), | ||||||
|  |     m_copyAudioToUdp(false), | ||||||
| 	m_udpChannelMode(UDPChannelLeft), | 	m_udpChannelMode(UDPChannelLeft), | ||||||
| 	m_udpChannelCodec(UDPCodecL16), | 	m_udpChannelCodec(UDPCodecL16), | ||||||
| 	m_audioUsageCount(0), | 	m_audioUsageCount(0), | ||||||
| 	m_onExit(false), | 	m_onExit(false), | ||||||
| 	m_volume(1.0), | 	m_volume(1.0), | ||||||
|  |     m_recordToFile(false), | ||||||
|  |     m_recordSilenceTime(0), | ||||||
|  |     m_recordSilenceNbSamples(0), | ||||||
|  |     m_recordSilenceCount(0), | ||||||
| 	m_audioFifos() | 	m_audioFifos() | ||||||
| { | { | ||||||
| } | } | ||||||
| @ -114,14 +120,15 @@ bool AudioOutputDevice::start(int device, int rate) | |||||||
| 
 | 
 | ||||||
|         m_audioOutput = new QAudioOutput(devInfo, m_audioFormat); |         m_audioOutput = new QAudioOutput(devInfo, m_audioFormat); | ||||||
|         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_audioOutput->setVolume(m_volume); | 		m_audioOutput->setVolume(m_volume); | ||||||
|  |         m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
 | ||||||
| 
 | 
 | ||||||
|         QIODevice::open(QIODevice::ReadOnly); |         QIODevice::open(QIODevice::ReadOnly); | ||||||
| 
 | 
 | ||||||
|         m_audioOutput->start(this); |         m_audioOutput->start(this); | ||||||
| 
 | 
 | ||||||
|         if (m_audioOutput->state() != QAudio::ActiveState) |         if (m_audioOutput->state() != QAudio::ActiveState) { | ||||||
|         { |  | ||||||
|             qWarning("AudioOutputDevice::start: cannot start"); |             qWarning("AudioOutputDevice::start: cannot start"); | ||||||
|         } |         } | ||||||
| //	}
 | //	}
 | ||||||
| @ -139,8 +146,11 @@ void AudioOutputDevice::stop() | |||||||
|     m_audioOutput->stop(); |     m_audioOutput->stop(); | ||||||
|     QIODevice::close(); |     QIODevice::close(); | ||||||
|     delete m_audioNetSink; |     delete m_audioNetSink; | ||||||
|     m_audioNetSink = 0; |     m_audioNetSink = nullptr; | ||||||
|  |     delete m_wavFileRecord; | ||||||
|  |     m_wavFileRecord = nullptr; | ||||||
|     delete m_audioOutput; |     delete m_audioOutput; | ||||||
|  |     m_audioOutput = nullptr; | ||||||
| 
 | 
 | ||||||
| //    if (m_audioUsageCount > 0)
 | //    if (m_audioUsageCount > 0)
 | ||||||
| //    {
 | //    {
 | ||||||
| @ -161,14 +171,12 @@ void AudioOutputDevice::stop() | |||||||
| void AudioOutputDevice::addFifo(AudioFifo* audioFifo) | void AudioOutputDevice::addFifo(AudioFifo* audioFifo) | ||||||
| { | { | ||||||
| 	QMutexLocker mutexLocker(&m_mutex); | 	QMutexLocker mutexLocker(&m_mutex); | ||||||
| 
 |  | ||||||
| 	m_audioFifos.push_back(audioFifo); | 	m_audioFifos.push_back(audioFifo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AudioOutputDevice::removeFifo(AudioFifo* audioFifo) | void AudioOutputDevice::removeFifo(AudioFifo* audioFifo) | ||||||
| { | { | ||||||
| 	QMutexLocker mutexLocker(&m_mutex); | 	QMutexLocker mutexLocker(&m_mutex); | ||||||
| 
 |  | ||||||
| 	m_audioFifos.remove(audioFifo); | 	m_audioFifos.remove(audioFifo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -219,6 +227,63 @@ void AudioOutputDevice::setUdpDecimation(uint32_t decimation) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AudioOutputDevice::setFileRecordName(const QString& fileRecordName) | ||||||
|  | { | ||||||
|  |     if (!m_wavFileRecord) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QStringList dotBreakout = fileRecordName.split(QLatin1Char('.')); | ||||||
|  | 
 | ||||||
|  |     if (dotBreakout.size() > 1) { | ||||||
|  |         QString extension = dotBreakout.last(); | ||||||
|  | 
 | ||||||
|  |         if (extension != "wav") { | ||||||
|  |             dotBreakout.last() = "wav"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         dotBreakout.append("wav"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QString newFileRecordName = dotBreakout.join(QLatin1Char('.')); | ||||||
|  |     QString fileBase; | ||||||
|  |     FileRecordInterface::guessTypeFromFileName(newFileRecordName, fileBase); | ||||||
|  |     qDebug("AudioOutputDevice::setFileRecordName: newFileRecordName: %s fileBase: %s", qPrintable(newFileRecordName), qPrintable(fileBase)); | ||||||
|  |     m_wavFileRecord->setFileName(fileBase); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AudioOutputDevice::setRecordToFile(bool recordToFile) | ||||||
|  | { | ||||||
|  |     if (!m_wavFileRecord) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (recordToFile) | ||||||
|  |     { | ||||||
|  |         if (!m_wavFileRecord->isRecording()) { | ||||||
|  |             m_wavFileRecord->startRecording(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         if (m_wavFileRecord->isRecording()) { | ||||||
|  |             m_wavFileRecord->stopRecording(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_recordToFile = recordToFile; | ||||||
|  |     m_recordSilenceCount = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AudioOutputDevice::setRecordSilenceTime(int recordSilenceTime) | ||||||
|  | { | ||||||
|  |     m_recordSilenceNbSamples = (recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
 | ||||||
|  |     m_recordSilenceCount = 0; | ||||||
|  |     m_recordSilenceTime = recordSilenceTime; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) | qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) | ||||||
| { | { | ||||||
|     //qDebug("AudioOutputDevice::readData: %lld", maxLen);
 |     //qDebug("AudioOutputDevice::readData: %lld", maxLen);
 | ||||||
| @ -331,6 +396,36 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) | |||||||
| 	            break; | 	            break; | ||||||
| 	        } | 	        } | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  |         if ((m_recordToFile) && (m_wavFileRecord)) | ||||||
|  |         { | ||||||
|  |             if ((sr == 0) && (sl == 0)) | ||||||
|  |             { | ||||||
|  |                 if (m_recordSilenceNbSamples <= 0) | ||||||
|  |                 { | ||||||
|  |                     m_wavFileRecord->write(sl, sr); | ||||||
|  |                     m_recordSilenceCount = 0; | ||||||
|  |                 } | ||||||
|  |                 else if (m_recordSilenceCount < m_recordSilenceNbSamples) | ||||||
|  |                 { | ||||||
|  |                     m_wavFileRecord->write(sl, sr); | ||||||
|  |                     m_recordSilenceCount++; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     m_wavFileRecord->stopRecording(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (!m_wavFileRecord->isRecording()) { | ||||||
|  |                     m_wavFileRecord->startRecording(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 m_wavFileRecord->write(sl, sr); | ||||||
|  |                 m_recordSilenceCount = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return samplesPerBuffer * 4; | 	return samplesPerBuffer * 4; | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ class QAudioOutput; | |||||||
| class AudioFifo; | class AudioFifo; | ||||||
| class AudioOutputPipe; | class AudioOutputPipe; | ||||||
| class AudioNetSink; | class AudioNetSink; | ||||||
|  | class WavFileRecord; | ||||||
| 
 | 
 | ||||||
| class SDRBASE_API AudioOutputDevice : QIODevice { | class SDRBASE_API AudioOutputDevice : QIODevice { | ||||||
| public: | public: | ||||||
| @ -72,17 +73,26 @@ public: | |||||||
| 	void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate); | 	void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate); | ||||||
| 	void setUdpDecimation(uint32_t decimation); | 	void setUdpDecimation(uint32_t decimation); | ||||||
| 	void setVolume(float volume); | 	void setVolume(float volume); | ||||||
|  |     void setFileRecordName(const QString& fileRecordName); | ||||||
|  |     void setRecordToFile(bool recordToFile); | ||||||
|  |     void setRecordSilenceTime(int recordSilenceTime); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	QRecursiveMutex m_mutex; | 	QRecursiveMutex m_mutex; | ||||||
| 	QAudioOutput* m_audioOutput; | 	QAudioOutput* m_audioOutput; | ||||||
| 	AudioNetSink* m_audioNetSink; | 	AudioNetSink* m_audioNetSink; | ||||||
|  |     WavFileRecord* m_wavFileRecord; | ||||||
| 	bool m_copyAudioToUdp; | 	bool m_copyAudioToUdp; | ||||||
| 	UDPChannelMode m_udpChannelMode; | 	UDPChannelMode m_udpChannelMode; | ||||||
| 	UDPChannelCodec m_udpChannelCodec; | 	UDPChannelCodec m_udpChannelCodec; | ||||||
| 	uint m_audioUsageCount; | 	uint m_audioUsageCount; | ||||||
| 	bool m_onExit; | 	bool m_onExit; | ||||||
| 	float m_volume; | 	float m_volume; | ||||||
|  |     QString m_fileRecordName; | ||||||
|  |     bool m_recordToFile; | ||||||
|  |     int m_recordSilenceTime; | ||||||
|  |     int m_recordSilenceNbSamples; | ||||||
|  |     int m_recordSilenceCount; | ||||||
| 
 | 
 | ||||||
| 	std::list<AudioFifo*> m_audioFifos; | 	std::list<AudioFifo*> m_audioFifos; | ||||||
| 	std::vector<qint32> m_mixBuffer; | 	std::vector<qint32> m_mixBuffer; | ||||||
|  | |||||||
| @ -111,6 +111,19 @@ void WavFileRecord::feed(const SampleVector::const_iterator& begin, const Sample | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void WavFileRecord::write(qint16 lSample, qint16 rSample) | ||||||
|  | { | ||||||
|  |     if (m_recordStart) | ||||||
|  |     { | ||||||
|  |         writeHeader(); | ||||||
|  |         m_recordStart = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_sampleFile.write(reinterpret_cast<const char*>(&lSample), 2); | ||||||
|  |     m_sampleFile.write(reinterpret_cast<const char*>(&rSample), 2); | ||||||
|  |     m_byteCount += 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void WavFileRecord::start() | void WavFileRecord::start() | ||||||
| { | { | ||||||
| } | } | ||||||
|  | |||||||
| @ -97,6 +97,7 @@ public: | |||||||
|     void genUniqueFileName(uint deviceUID, int istream = -1); |     void genUniqueFileName(uint deviceUID, int istream = -1); | ||||||
| 
 | 
 | ||||||
|     virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override; |     virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override; | ||||||
|  |     void write(qint16 lSample, qint16 rSample); //!< write a single sample
 | ||||||
|     virtual void start() override; |     virtual void start() override; | ||||||
|     virtual void stop() override; |     virtual void stop() override; | ||||||
|     virtual bool handleMessage(const Message& message) override; |     virtual bool handleMessage(const Message& message) override; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user