mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	Added baseband recording
This commit is contained in:
		| @@ -54,6 +54,10 @@ void SignalPath::setSampleRate(double sampleRate) { | ||||
|     dynSplit.start(); | ||||
| } | ||||
|  | ||||
| double SignalPath::getSampleRate() { | ||||
|     return sampleRate; | ||||
| } | ||||
|  | ||||
| void SignalPath::start() { | ||||
|     dcBiasRemover.start(); | ||||
|     split.start(); | ||||
| @@ -103,4 +107,16 @@ void SignalPath::setInput(dsp::stream<dsp::complex_t>* input) { | ||||
|     dcBiasRemover.stop(); | ||||
|     dcBiasRemover.setInput(input); | ||||
|     dcBiasRemover.start(); | ||||
| } | ||||
|  | ||||
| void SignalPath::bindIQStream(dsp::stream<dsp::complex_t>* stream) { | ||||
|     dynSplit.stop(); | ||||
|     dynSplit.bind(stream); | ||||
|     dynSplit.start(); | ||||
| } | ||||
|  | ||||
| void SignalPath::unbindIQStream(dsp::stream<dsp::complex_t>* stream) { | ||||
|     dynSplit.stop(); | ||||
|     dynSplit.unbind(stream); | ||||
|     dynSplit.start(); | ||||
| } | ||||
| @@ -19,9 +19,12 @@ public: | ||||
|     void setSampleRate(double sampleRate); | ||||
|     void setDCBiasCorrection(bool enabled); | ||||
|     void setFFTRate(double rate); | ||||
|     double getSampleRate(); | ||||
|     dsp::VFO* addVFO(std::string name, double outSampleRate, double bandwidth, double offset); | ||||
|     void removeVFO(std::string name); | ||||
|     void setInput(dsp::stream<dsp::complex_t>* input); | ||||
|     void bindIQStream(dsp::stream<dsp::complex_t>* stream); | ||||
|     void unbindIQStream(dsp::stream<dsp::complex_t>* stream); | ||||
|  | ||||
| private: | ||||
|     struct VFO_t { | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| #include <ctime> | ||||
| #include <signal_path/audio.h> | ||||
| #include <gui/gui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <config.h> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| @@ -75,56 +77,109 @@ private: | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ImGui::PushItemWidth(menuColumnWidth); | ||||
|         if (!_this->recording) { | ||||
|             if (ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str())) { | ||||
|                 _this->selectedStreamName = streamNames[_this->selectedStreamId]; | ||||
|         ImGui::BeginGroup(); | ||||
|  | ||||
|         // TODO: Change VFO ref in signal path | ||||
|  | ||||
|         ImGui::Columns(3, CONCAT("RecordModeColumns##_", _this->name), false); | ||||
|         if (ImGui::RadioButton(CONCAT("Baseband##_", _this->name), _this->recMode == 0) && _this->recMode != 0) {  | ||||
|             _this->recMode = 0; | ||||
|         } | ||||
|         ImGui::NextColumn(); | ||||
|         if (ImGui::RadioButton(CONCAT("Audio##_", _this->name), _this->recMode == 1) && _this->recMode != 1) { | ||||
|             _this->recMode = 1; | ||||
|         } | ||||
|         ImGui::NextColumn(); | ||||
|         if (ImGui::RadioButton(CONCAT("VFO##_", _this->name), _this->recMode == 2) && _this->recMode != 2) { | ||||
|             _this->recMode = 2; | ||||
|         } | ||||
|         ImGui::Columns(1, CONCAT("EndRecordModeColumns##_", _this->name), false); | ||||
|  | ||||
|         ImGui::EndGroup(); | ||||
|  | ||||
|         if (_this->recMode == 0) { | ||||
|             ImGui::PushItemWidth(menuColumnWidth); | ||||
|             if (!_this->recording) { | ||||
|                 if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { | ||||
|                     _this->samplesWritten = 0; | ||||
|                     _this->sampleRate = sigpath::signalPath.getSampleRate(); | ||||
|                     _this->writer = new WavWriter(ROOT_DIR "/recordings/" + genFileName("baseband_"), 16, 2, _this->sampleRate); | ||||
|                     _this->iqStream = new dsp::stream<dsp::complex_t>(); | ||||
|                     _this->iqStream->init(_this->sampleRate / 200.0); | ||||
|                     sigpath::signalPath.bindIQStream(_this->iqStream); | ||||
|                     _this->workerThread = std::thread(_iqWriteWorker, _this); | ||||
|                     _this->recording = true; | ||||
|                     _this->startTime = time(0); | ||||
|                 } | ||||
|                 ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); | ||||
|             } | ||||
|             else { | ||||
|                 if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { | ||||
|                     _this->iqStream->stopReader(); | ||||
|                     _this->workerThread.join(); | ||||
|                     _this->iqStream->clearReadStop(); | ||||
|                     sigpath::signalPath.unbindIQStream(_this->iqStream); | ||||
|                     _this->writer->close(); | ||||
|                     delete _this->writer; | ||||
|                     _this->recording = false; | ||||
|                 } | ||||
|                 uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; | ||||
|                 time_t diff = seconds; | ||||
|                 tm *dtm = gmtime(&diff); | ||||
|                 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); | ||||
|             ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f)); | ||||
|             ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f)); | ||||
|             ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f)); | ||||
|             ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str()); | ||||
|             ImGui::PopItemFlag(); | ||||
|             ImGui::PopStyleColor(3); | ||||
|         } | ||||
|          | ||||
|         if (!_this->recording) { | ||||
|             if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { | ||||
|                 _this->samplesWritten = 0; | ||||
|                 _this->sampleRate = 48000; | ||||
|                 _this->writer = new WavWriter("recordings/" + genFileName("audio_"), 16, 2, 48000); | ||||
|                 _this->stream = audio::bindToStreamStereo(_this->selectedStreamName, streamRemovedHandler, sampleRateChanged, _this); | ||||
|                 _this->workerThread = std::thread(_writeWorker, _this); | ||||
|                 _this->recording = true; | ||||
|                 _this->startTime = time(0); | ||||
|         else if (_this->recMode == 1) { | ||||
|             ImGui::PushItemWidth(menuColumnWidth); | ||||
|             if (!_this->recording) { | ||||
|                 if (ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str())) { | ||||
|                     _this->selectedStreamName = streamNames[_this->selectedStreamId]; | ||||
|                 } | ||||
|             } | ||||
|             ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); | ||||
|         } | ||||
|         else { | ||||
|             if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { | ||||
|                 _this->stream->stopReader(); | ||||
|                 _this->workerThread.join(); | ||||
|                 _this->stream->clearReadStop(); | ||||
|                 audio::unbindFromStreamStereo(_this->selectedStreamName, _this->stream); | ||||
|                 _this->writer->close(); | ||||
|                 delete _this->writer; | ||||
|                 _this->recording = false; | ||||
|             else { | ||||
|                 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); | ||||
|                 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f)); | ||||
|                 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f)); | ||||
|                 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f)); | ||||
|                 ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str()); | ||||
|                 ImGui::PopItemFlag(); | ||||
|                 ImGui::PopStyleColor(3); | ||||
|             } | ||||
|             if (!_this->recording) { | ||||
|                 if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { | ||||
|                     _this->samplesWritten = 0; | ||||
|                     _this->sampleRate = 48000; | ||||
|                     _this->writer = new WavWriter(ROOT_DIR "/recordings/" + genFileName("audio_"), 16, 2, 48000); | ||||
|                     _this->audioStream = audio::bindToStreamStereo(_this->selectedStreamName, streamRemovedHandler, sampleRateChanged, _this); | ||||
|                     _this->workerThread = std::thread(_audioWriteWorker, _this); | ||||
|                     _this->recording = true; | ||||
|                     _this->startTime = time(0); | ||||
|                 } | ||||
|                 ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); | ||||
|             } | ||||
|             else { | ||||
|                 if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { | ||||
|                     _this->audioStream->stopReader(); | ||||
|                     _this->workerThread.join(); | ||||
|                     _this->audioStream->clearReadStop(); | ||||
|                     audio::unbindFromStreamStereo(_this->selectedStreamName, _this->audioStream); | ||||
|                     _this->writer->close(); | ||||
|                     delete _this->writer; | ||||
|                     _this->recording = false; | ||||
|                 } | ||||
|                 uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; | ||||
|                 time_t diff = seconds; | ||||
|                 tm *dtm = gmtime(&diff); | ||||
|                 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|             } | ||||
|             uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; | ||||
|             time_t diff = seconds; | ||||
|             tm *dtm = gmtime(&diff); | ||||
|             ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static void _writeWorker(RecorderModule* _this) { | ||||
|     static void _audioWriteWorker(RecorderModule* _this) { | ||||
|         dsp::StereoFloat_t* floatBuf = new dsp::StereoFloat_t[1024]; | ||||
|         int16_t* sampleBuf = new int16_t[2048]; | ||||
|         while (true) { | ||||
|             if (_this->stream->read(floatBuf, 1024) < 0) { | ||||
|             if (_this->audioStream->read(floatBuf, 1024) < 0) { | ||||
|                 break; | ||||
|             } | ||||
|             for (int i = 0; i < 1024; i++) { | ||||
| @@ -138,8 +193,27 @@ private: | ||||
|         delete[] sampleBuf; | ||||
|     } | ||||
|  | ||||
|     static void _iqWriteWorker(RecorderModule* _this) { | ||||
|         dsp::complex_t* iqBuf = new dsp::complex_t[1024]; | ||||
|         int16_t* sampleBuf = new int16_t[2048]; | ||||
|         while (true) { | ||||
|             if (_this->iqStream->read(iqBuf, 1024) < 0) { | ||||
|                 break; | ||||
|             } | ||||
|             for (int i = 0; i < 1024; i++) { | ||||
|                 sampleBuf[(i * 2) + 0] = iqBuf[i].q * 0x7FFF; | ||||
|                 sampleBuf[(i * 2) + 1] = iqBuf[i].i * 0x7FFF; | ||||
|             } | ||||
|             _this->samplesWritten += 1024; | ||||
|             _this->writer->writeSamples(sampleBuf, 2048 * sizeof(int16_t)); | ||||
|         } | ||||
|         delete[] iqBuf; | ||||
|         delete[] sampleBuf; | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     dsp::stream<dsp::StereoFloat_t>* stream; | ||||
|     dsp::stream<dsp::StereoFloat_t>* audioStream; | ||||
|     dsp::stream<dsp::complex_t>* iqStream; | ||||
|     WavWriter* writer; | ||||
|     std::thread workerThread; | ||||
|     bool recording; | ||||
| @@ -149,6 +223,7 @@ private: | ||||
|     int selectedStreamId; | ||||
|     uint64_t samplesWritten; | ||||
|     float sampleRate; | ||||
|     int recMode = 0; | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,43 +1,43 @@ | ||||
| { | ||||
|     "audio": { | ||||
|         "Radio": { | ||||
|             "device": "Speakers (Realtek High Definiti", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 0.60546875 | ||||
|         }, | ||||
|         "Radio 1": { | ||||
|             "device": "Speakers (Realtek High Definition Audio)", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 0.609375 | ||||
|         }, | ||||
|         "Radio 2": { | ||||
|             "device": "CABLE Input (VB-Audio Virtual Cable)", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 1.0 | ||||
|         } | ||||
|     }, | ||||
|     "bandPlan": "General", | ||||
|     "bandPlanEnabled": true, | ||||
|     "fftHeight": 298, | ||||
|     "frequency": 100100000, | ||||
|     "max": 0.0, | ||||
|     "maximized": true, | ||||
|     "menuOrder": [ | ||||
|         "Source", | ||||
|         "Radio", | ||||
|         "Recorder", | ||||
|         "Audio", | ||||
|         "Scripting", | ||||
|         "Band Plan", | ||||
|         "Display" | ||||
|     ], | ||||
|     "menuWidth": 300, | ||||
|     "min": -53.676475524902344, | ||||
|     "showWaterfall": true, | ||||
|     "source": "", | ||||
|     "sourceSettings": {}, | ||||
|     "windowSize": { | ||||
|         "h": 720, | ||||
|         "w": 1280 | ||||
|     } | ||||
| { | ||||
|     "audio": { | ||||
|         "Radio": { | ||||
|             "device": "Speakers (Realtek High Definiti", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 0.60546875 | ||||
|         }, | ||||
|         "Radio 1": { | ||||
|             "device": "Speakers (Realtek High Definition Audio)", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 0.609375 | ||||
|         }, | ||||
|         "Radio 2": { | ||||
|             "device": "CABLE Input (VB-Audio Virtual Cable)", | ||||
|             "sampleRate": 48000.0, | ||||
|             "volume": 1.0 | ||||
|         } | ||||
|     }, | ||||
|     "bandPlan": "General", | ||||
|     "bandPlanEnabled": true, | ||||
|     "fftHeight": 298, | ||||
|     "frequency": 100100000, | ||||
|     "max": 0.0, | ||||
|     "maximized": true, | ||||
|     "menuOrder": [ | ||||
|         "Source", | ||||
|         "Radio", | ||||
|         "Recorder", | ||||
|         "Audio", | ||||
|         "Scripting", | ||||
|         "Band Plan", | ||||
|         "Display" | ||||
|     ], | ||||
|     "menuWidth": 300, | ||||
|     "min": -53.676475524902344, | ||||
|     "showWaterfall": true, | ||||
|     "source": "", | ||||
|     "sourceSettings": {}, | ||||
|     "windowSize": { | ||||
|         "h": 720, | ||||
|         "w": 1280 | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "Radio": "./radio/Release/radio.dll", | ||||
|     "Recorder": "./recorder/Release/recorder.dll", | ||||
|     "Soapy": "./soapy/Release/soapy.dll", | ||||
|     "RTLTCPSource": "./rtl_tcp_source/Release/rtl_tcp_source.dll" | ||||
|     "Radio": "./radio/radio.so", | ||||
|     "Recorder": "./recorder/recorder.so", | ||||
|     "Soapy": "./soapy/soapy.so", | ||||
|     "RTLTCPSource": "./rtl_tcp_source/rtl_tcp_source.so" | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user