diff --git a/sdrbase/audio/audiodevicemanager.cpp b/sdrbase/audio/audiodevicemanager.cpp index 11fff7ca4..16a3b4cd8 100644 --- a/sdrbase/audio/audiodevicemanager.cpp +++ b/sdrbase/audio/audiodevicemanager.cpp @@ -641,6 +641,9 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode); audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutputDevice::UDPChannelStereo, deviceInfo.sampleRate); audioOutput->setUdpDecimation(deviceInfo.udpDecimationFactor); + audioOutput->setFileRecordName(deviceInfo.fileRecordName); + audioOutput->setRecordToFile(deviceInfo.recordToFile); + audioOutput->setRecordSilenceTime(deviceInfo.recordSilenceTime); qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated", outputDeviceIndex, qPrintable(deviceName)); diff --git a/sdrbase/audio/audiooutputdevice.cpp b/sdrbase/audio/audiooutputdevice.cpp index 0408c868a..006059743 100644 --- a/sdrbase/audio/audiooutputdevice.cpp +++ b/sdrbase/audio/audiooutputdevice.cpp @@ -23,16 +23,22 @@ #include "audiooutputdevice.h" #include "audiofifo.h" #include "audionetsink.h" +#include "dsp/wavfilerecord.h" AudioOutputDevice::AudioOutputDevice() : - m_audioOutput(0), - m_audioNetSink(0), - m_copyAudioToUdp(false), + m_audioOutput(nullptr), + m_audioNetSink(nullptr), + m_wavFileRecord(nullptr), + m_copyAudioToUdp(false), m_udpChannelMode(UDPChannelLeft), m_udpChannelCodec(UDPCodecL16), m_audioUsageCount(0), m_onExit(false), m_volume(1.0), + m_recordToFile(false), + m_recordSilenceTime(0), + m_recordSilenceNbSamples(0), + m_recordSilenceCount(0), m_audioFifos() { } @@ -114,14 +120,15 @@ bool AudioOutputDevice::start(int device, int rate) m_audioOutput = new QAudioOutput(devInfo, m_audioFormat); m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false); + m_wavFileRecord = new WavFileRecord(m_audioFormat.sampleRate()); m_audioOutput->setVolume(m_volume); + m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms QIODevice::open(QIODevice::ReadOnly); m_audioOutput->start(this); - if (m_audioOutput->state() != QAudio::ActiveState) - { + if (m_audioOutput->state() != QAudio::ActiveState) { qWarning("AudioOutputDevice::start: cannot start"); } // } @@ -139,8 +146,11 @@ void AudioOutputDevice::stop() m_audioOutput->stop(); QIODevice::close(); delete m_audioNetSink; - m_audioNetSink = 0; + m_audioNetSink = nullptr; + delete m_wavFileRecord; + m_wavFileRecord = nullptr; delete m_audioOutput; + m_audioOutput = nullptr; // if (m_audioUsageCount > 0) // { @@ -161,14 +171,12 @@ void AudioOutputDevice::stop() void AudioOutputDevice::addFifo(AudioFifo* audioFifo) { QMutexLocker mutexLocker(&m_mutex); - m_audioFifos.push_back(audioFifo); } void AudioOutputDevice::removeFifo(AudioFifo* audioFifo) { QMutexLocker mutexLocker(&m_mutex); - 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) { //qDebug("AudioOutputDevice::readData: %lld", maxLen); @@ -331,6 +396,36 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) 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; diff --git a/sdrbase/audio/audiooutputdevice.h b/sdrbase/audio/audiooutputdevice.h index 572ed2ee1..f0eb86787 100644 --- a/sdrbase/audio/audiooutputdevice.h +++ b/sdrbase/audio/audiooutputdevice.h @@ -31,6 +31,7 @@ class QAudioOutput; class AudioFifo; class AudioOutputPipe; class AudioNetSink; +class WavFileRecord; class SDRBASE_API AudioOutputDevice : QIODevice { public: @@ -72,17 +73,26 @@ public: void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate); void setUdpDecimation(uint32_t decimation); void setVolume(float volume); + void setFileRecordName(const QString& fileRecordName); + void setRecordToFile(bool recordToFile); + void setRecordSilenceTime(int recordSilenceTime); private: QRecursiveMutex m_mutex; QAudioOutput* m_audioOutput; AudioNetSink* m_audioNetSink; + WavFileRecord* m_wavFileRecord; bool m_copyAudioToUdp; UDPChannelMode m_udpChannelMode; UDPChannelCodec m_udpChannelCodec; uint m_audioUsageCount; bool m_onExit; float m_volume; + QString m_fileRecordName; + bool m_recordToFile; + int m_recordSilenceTime; + int m_recordSilenceNbSamples; + int m_recordSilenceCount; std::list m_audioFifos; std::vector m_mixBuffer; diff --git a/sdrbase/dsp/wavfilerecord.cpp b/sdrbase/dsp/wavfilerecord.cpp index d2737e29f..23940792a 100644 --- a/sdrbase/dsp/wavfilerecord.cpp +++ b/sdrbase/dsp/wavfilerecord.cpp @@ -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(&lSample), 2); + m_sampleFile.write(reinterpret_cast(&rSample), 2); + m_byteCount += 4; +} + void WavFileRecord::start() { } diff --git a/sdrbase/dsp/wavfilerecord.h b/sdrbase/dsp/wavfilerecord.h index 49e21298f..ce1890d90 100644 --- a/sdrbase/dsp/wavfilerecord.h +++ b/sdrbase/dsp/wavfilerecord.h @@ -97,6 +97,7 @@ public: void genUniqueFileName(uint deviceUID, int istream = -1); 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 stop() override; virtual bool handleMessage(const Message& message) override;