diff --git a/src/audio/AudioSinkFileThread.cpp b/src/audio/AudioSinkFileThread.cpp index 93fc182..2b00cdb 100644 --- a/src/audio/AudioSinkFileThread.cpp +++ b/src/audio/AudioSinkFileThread.cpp @@ -8,27 +8,28 @@ AudioSinkFileThread::AudioSinkFileThread() : AudioSinkThread() { } AudioSinkFileThread::~AudioSinkFileThread() { - if (outputFileHandler != nullptr) { - outputFileHandler->closeFile(); + if (audioFileHandler != nullptr) { + audioFileHandler->closeFile(); } } void AudioSinkFileThread::sink(AudioThreadInputPtr input) { - if (!outputFileHandler) { + if (!audioFileHandler) { return; } // forward to output file handler - outputFileHandler->writeToFile(input); + audioFileHandler->writeToFile(input); } void AudioSinkFileThread::inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) { // close, set new parameters, adjust file name sequence and re-open? - if (!outputFileHandler) { + if (!audioFileHandler) { return; } + + audioFileHandler->closeFile(); } -void AudioSinkFileThread::setOutput(AudioFile * output) { - outputFileHandler = output; - outputFileHandler->setOutputFileName(sinkName); +void AudioSinkFileThread::setAudioFileHandler(AudioFile * output) { + audioFileHandler = output; } diff --git a/src/audio/AudioSinkFileThread.h b/src/audio/AudioSinkFileThread.h index 4e3930e..1ba4c4c 100644 --- a/src/audio/AudioSinkFileThread.h +++ b/src/audio/AudioSinkFileThread.h @@ -15,10 +15,10 @@ public: void sink(AudioThreadInputPtr input); void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps); - void setOutput(AudioFile *output); + void setAudioFileHandler(AudioFile *output); protected: - AudioFile *outputFileHandler = nullptr; + AudioFile *audioFileHandler = nullptr; }; diff --git a/src/audio/AudioSinkThread.cpp b/src/audio/AudioSinkThread.cpp index 14c58ed..11e9009 100644 --- a/src/audio/AudioSinkThread.cpp +++ b/src/audio/AudioSinkThread.cpp @@ -42,6 +42,8 @@ void AudioSinkThread::run() { inputRef.inputRate = inp->inputRate; inputRef.sampleRate = inp->sampleRate; } + + sink(inp); } } @@ -49,12 +51,3 @@ void AudioSinkThread::terminate() { IOThread::terminate(); inputQueuePtr->flush(); } - -void AudioSinkThread::setSinkName(std::string sinkName_in) { - sinkName = sinkName_in; -} - -std::string AudioSinkThread::getSinkName() { - return sinkName; -} - diff --git a/src/audio/AudioSinkThread.h b/src/audio/AudioSinkThread.h index d01fa64..b862b0d 100644 --- a/src/audio/AudioSinkThread.h +++ b/src/audio/AudioSinkThread.h @@ -18,12 +18,8 @@ public: virtual void sink(AudioThreadInputPtr input) = 0; virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) = 0; - virtual void setSinkName(std::string sinkName_in); - virtual std::string getSinkName(); - protected: std::recursive_mutex m_mutex; AudioThreadInputQueuePtr inputQueuePtr; - std::string sinkName; }; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 6886b9f..f63d196 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -2,12 +2,15 @@ // SPDX-License-Identifier: GPL-2.0+ #include +#include + #include "DemodulatorInstance.h" #include "CubicSDR.h" #include "DemodulatorThread.h" #include "DemodulatorPreThread.h" - +#include "AudioSinkFileThread.h" +#include "AudioFileWAV.h" #if USE_HAMLIB #include "RigThread.h" @@ -47,6 +50,7 @@ DemodulatorInstance::DemodulatorInstance() { active.store(false); squelch.store(false); muted.store(false); + recording.store(false); deltaLock.store(false); deltaLockOfs.store(0); currentOutputDevice.store(-1); @@ -542,6 +546,21 @@ void DemodulatorInstance::setMuted(bool muted) { wxGetApp().getDemodMgr().setLastMuted(muted); } +bool DemodulatorInstance::isRecording() +{ + return recording.load(); +} + +void DemodulatorInstance::setRecording(bool recording_in) +{ + if (!recording.load() && recording_in) { + startRecording(); + } + else if (recording.load() && !recording_in) { + stopRecording(); + } +} + DemodVisualCue *DemodulatorInstance::getVisualCue() { return &visualCue; } @@ -599,6 +618,50 @@ ModemSettings DemodulatorInstance::getLastModemSettings(std::string demodType) { } } + +void DemodulatorInstance::startRecording() { + if (recording.load()) { + return; + } + + AudioSinkFileThread *newSinkThread = new AudioSinkFileThread(); + AudioFileWAV *afHandler = new AudioFileWAV(); + + std::stringstream fileName; + fileName << getLabel() << "_" << std::time(nullptr); + + afHandler->setOutputFileName(fileName.str()); + newSinkThread->setAudioFileHandler(afHandler); + + audioSinkThread = newSinkThread; + t_AudioSink = new std::thread(&AudioSinkThread::threadMain, audioSinkThread); + + demodulatorThread->setOutputQueue("AudioSink", audioSinkThread->getInputQueue("input")); + + recording.store(true); +} + + +void DemodulatorInstance::stopRecording() { + if (!recording.load()) { + return; + } + + demodulatorThread->setOutputQueue("AudioSink", nullptr); + audioSinkThread->terminate(); + + t_AudioSink->join(); + + delete t_AudioSink; + delete audioSinkThread; + + t_AudioSink = nullptr; + audioSinkThread = nullptr; + + recording.store(false); +} + + #if ENABLE_DIGITAL_LAB ModemDigitalOutput *DemodulatorInstance::getOutput() { if (activeOutput == nullptr) { diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index f36d635..297df69 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -111,6 +111,9 @@ public: bool isMuted(); void setMuted(bool muted); + bool isRecording(); + void setRecording(bool recording); + DemodVisualCue *getVisualCue(); DemodulatorThreadInputQueuePtr getIQInputDataPipe(); @@ -132,6 +135,10 @@ public: void closeOutput(); #endif +protected: + void startRecording(); + void stopRecording(); + private: DemodulatorThreadInputQueuePtr pipeIQInputData; DemodulatorThreadPostInputQueuePtr pipeIQDemodData; @@ -155,6 +162,8 @@ private: std::atomic_bool squelch; std::atomic_bool muted; std::atomic_bool deltaLock; + std::atomic_bool recording; + std::atomic_int deltaLockOfs; std::atomic_int currentOutputDevice; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index d1bfb56..9b9d5c9 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -43,6 +43,12 @@ void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBasePtr thread audioVisOutputQueue = std::static_pointer_cast(threadQueue); } + + if (name == "AudioSinkOutput") { + std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); + + audioSinkOutputQueue = std::static_pointer_cast(threadQueue); + } } double DemodulatorThread::abMagnitude(float inphase, float quadrature) { @@ -310,6 +316,13 @@ void DemodulatorThread::run() { } } + // Capture audioSinkOutputQueue state in a local + DemodulatorThreadOutputQueuePtr localAudioSinkOutputQueue = nullptr; + { + std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); + localAudioSinkOutputQueue = audioSinkOutputQueue; + } + if (ati != nullptr) { if (!muted.load() && (!wxGetApp().getSoloMode() || (demodInstance == wxGetApp().getDemodMgr().getLastActiveDemodulator().get()))) { //non-blocking push needed for audio out @@ -318,6 +331,13 @@ void DemodulatorThread::run() { std::cout << "DemodulatorThread::run() cannot push ati into audioOutputQueue, is full !" << std::endl; std::this_thread::yield(); } + + if (localAudioSinkOutputQueue != nullptr) { + if (!audioSinkOutputQueue->try_push(ati)) { + std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl; + std::this_thread::yield(); + } + } } } diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index bc4e2b0..fbd265a 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -67,6 +67,8 @@ protected: DemodulatorThreadOutputQueuePtr audioVisOutputQueue = nullptr; DemodulatorThreadControlCommandQueuePtr threadQueueControl = nullptr; + DemodulatorThreadOutputQueuePtr audioSinkOutputQueue = nullptr; + //protects the audioVisOutputQueue dynamic binding change at runtime (in DemodulatorMgr) std::mutex m_mutexAudioVisOutputQueue; };