diff --git a/airspy_source/src/main.cpp b/airspy_source/src/main.cpp index 6536879c..34be2a5c 100644 --- a/airspy_source/src/main.cpp +++ b/airspy_source/src/main.cpp @@ -50,15 +50,18 @@ public: std::string devSerial = config.conf["device"]; config.release(); selectByString(devSerial); - core::setInputSampleRate(sampleRate); sigpath::sourceManager.registerSource("Airspy", &handler); } ~AirspySourceModule() { + stop(this); + sigpath::sourceManager.unregisterSource("Airspy"); airspy_exit(); } + void postInit() {} + void enable() { enabled = true; } diff --git a/airspyhf_source/src/main.cpp b/airspyhf_source/src/main.cpp index 58a73cbd..a0da5beb 100644 --- a/airspyhf_source/src/main.cpp +++ b/airspyhf_source/src/main.cpp @@ -46,15 +46,17 @@ public: std::string devSerial = config.conf["device"]; config.release(); selectByString(devSerial); - core::setInputSampleRate(sampleRate); sigpath::sourceManager.registerSource("Airspy HF+", &handler); } ~AirspyHFSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("Airspy HF+"); } + void postInit() {} + enum AGCMode { AGC_MODE_OFF, AGC_MODE_LOW, diff --git a/audio_sink/src/main.cpp b/audio_sink/src/main.cpp index ad9510df..37b754ef 100644 --- a/audio_sink/src/main.cpp +++ b/audio_sink/src/main.cpp @@ -251,6 +251,8 @@ public: } + void postInit() {} + void enable() { enabled = true; } diff --git a/bladerf_source/src/main.cpp b/bladerf_source/src/main.cpp index 8d0f4136..16c1e542 100644 --- a/bladerf_source/src/main.cpp +++ b/bladerf_source/src/main.cpp @@ -50,15 +50,17 @@ public: std::string serial = config.conf["device"]; config.release(); selectBySerial(serial); - core::setInputSampleRate(sampleRate); sigpath::sourceManager.registerSource("BladeRF", &handler); } ~BladeRFSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("BladeRF"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/core/src/dsp/routing.h b/core/src/dsp/routing.h index 3e57160b..843dc789 100644 --- a/core/src/dsp/routing.h +++ b/core/src/dsp/routing.h @@ -77,6 +77,7 @@ namespace dsp { // NOTE: For some reason, the base class destrcutor doesn't get called.... this is a temporary fix I guess // I also don't check for _block_init for the exact sample reason, something's weird ~Reshaper() { + if (!generic_block>::_block_init) { return; } generic_block>::stop(); } diff --git a/core/src/dsp/stereo_fm.h b/core/src/dsp/stereo_fm.h index a71cbde1..0ab00a3b 100644 --- a/core/src/dsp/stereo_fm.h +++ b/core/src/dsp/stereo_fm.h @@ -47,6 +47,7 @@ namespace dsp { void updateWindow(dsp::filter_window::generic_complex_window* window) { assert(generic_block::_block_init); std::lock_guard lck(generic_block::ctrlMtx); + std::lock_guard lck2(bufMtx); _window = window; volk_free(taps); tapCount = window->getTapCount(); @@ -59,7 +60,7 @@ namespace dsp { int count = _in->read(); if (count < 0) { return -1; } - generic_block::ctrlMtx.lock(); + bufMtx.lock(); memcpy(bufStart, _in->readBuf, count * sizeof(complex_t)); _in->flush(); @@ -70,12 +71,14 @@ namespace dsp { memcpy(dataOut.writeBuf, &buffer[tapCount - ((tapCount-1)/2)], count * sizeof(complex_t)); - if (!dataOut.swap(count)) { return -1; } - if (!pilotOut.swap(count)) { return -1; } + if (!pilotOut.swap(count) || !dataOut.swap(count)) { + bufMtx.unlock(); + return -1; + } memmove(buffer, &buffer[count], tapCount * sizeof(complex_t)); - generic_block::ctrlMtx.unlock(); + bufMtx.unlock(); return count; } @@ -89,6 +92,8 @@ namespace dsp { dsp::filter_window::generic_complex_window* _window; + std::mutex bufMtx; + complex_t* bufStart; complex_t* buffer; int tapCount; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 060e578b..513a6468 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -212,7 +212,7 @@ void MainWindow::init() { initComplete = true; - onInitComplete.emit(true); + core::moduleManager.doPostInitAll(); } void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { diff --git a/core/src/gui/main_window.h b/core/src/gui/main_window.h index ec569c08..a04f9361 100644 --- a/core/src/gui/main_window.h +++ b/core/src/gui/main_window.h @@ -35,7 +35,6 @@ public: bool playButtonLocked = false; Event onPlayStateChange; - Event onInitComplete; private: void generateFFTWindow(int win, int size); diff --git a/core/src/gui/menus/module_manager.cpp b/core/src/gui/menus/module_manager.cpp index c64c99e1..e010f35d 100644 --- a/core/src/gui/menus/module_manager.cpp +++ b/core/src/gui/menus/module_manager.cpp @@ -16,11 +16,6 @@ namespace module_manager_menu { modTypes.clear(); modTypesTxt = ""; for (auto& [name, mod] : core::moduleManager.modules) { - // TEMPORARY EXCLUSION FOR SOURCES AND SINKS - if (name.find("source") != std::string::npos) { continue; } - if (name.find("sink") != std::string::npos) { continue; } - if (name.find("recorder") != std::string::npos) { continue; } - if (name.find("discord") != std::string::npos) { continue; } modTypes.push_back(name); modTypesTxt += name; modTypesTxt += '\0'; @@ -38,13 +33,6 @@ namespace module_manager_menu { float height = ImGui::CalcTextSize("-").y; for (auto& [name, inst] : core::moduleManager.instances) { - // TEMPORARY EXCLUSION FOR SOURCES AND SINKS - std::string type = inst.module.info->name; - if (type.find("source") != std::string::npos) { continue; } - if (type.find("sink") != std::string::npos) { continue; } - if (type.find("recorder") != std::string::npos) { continue; } - if (type.find("discord") != std::string::npos) { continue; } - ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -83,6 +71,7 @@ namespace module_manager_menu { if (strlen(modName) == 0) { style::beginDisabled(); } if (ImGui::Button("+##module_mgr_add_btn", ImVec2(16,0))) { core::moduleManager.createInstance(modName, modTypes[modTypeId]); + core::moduleManager.postInit(modName); } if (strlen(modName) == 0) { style::endDisabled(); } ImGui::EndTable(); diff --git a/core/src/gui/menus/source.cpp b/core/src/gui/menus/source.cpp index 7b9e1987..ce38948b 100644 --- a/core/src/gui/menus/source.cpp +++ b/core/src/gui/menus/source.cpp @@ -12,6 +12,14 @@ namespace sourecmenu { double customOffset = 0.0; double effectiveOffset = 0.0; + EventHandler sourceRegisteredHandler; + EventHandler sourceUnregisterHandler; + EventHandler sourceUnregisteredHandler; + + std::vector sourceNames; + std::string sourceNamesTxt; + std::string selectedSource; + enum { OFFSET_MODE_NONE, OFFSET_MODE_CUSTOM, @@ -42,41 +50,94 @@ namespace sourecmenu { sigpath::sourceManager.setTuningOffset(effectiveOffset); } + void refreshSources() { + sourceNames = sigpath::sourceManager.getSourceNames(); + sourceNamesTxt.clear(); + for (auto name : sourceNames) { + sourceNamesTxt += name; + sourceNamesTxt += '\0'; + } + } + + void selectSource(std::string name) { + if (sourceNames.empty()) { + selectedSource.clear(); + return; + } + auto it = std::find(sourceNames.begin(), sourceNames.end(), name); + if (it == sourceNames.end()) { + selectSource(sourceNames[0]); + } + sourceId = std::distance(sourceNames.begin(), it); + selectedSource = sourceNames[sourceId]; + sigpath::sourceManager.selectSource(sourceNames[sourceId]); + } + + void onSourceRegistered(std::string name, void* ctx) { + refreshSources(); + + if (selectedSource.empty()) { + sourceId = 0; + selectSource(sourceNames[0]); + return; + } + + sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); + } + + void onSourceUnregister(std::string name, void* ctx) { + if (name != selectedSource) { return; } + + // TODO: Stop everything + } + + void onSourceUnregistered(std::string name, void* ctx) { + refreshSources(); + + if (sourceNames.empty()) { + selectedSource = ""; + return; + } + + if (name == selectedSource) { + sourceId = std::clamp(sourceId, 0, sourceNames.size() - 1); + selectSource(sourceNames[sourceId]); + return; + } + + sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); + } + void init() { core::configManager.acquire(); - std::string name = core::configManager.conf["source"]; - auto it = std::find(sigpath::sourceManager.sourceNames.begin(), sigpath::sourceManager.sourceNames.end(), name); - if (it != sigpath::sourceManager.sourceNames.end()) { - sigpath::sourceManager.selectSource(name); - sourceId = std::distance(sigpath::sourceManager.sourceNames.begin(), it); - } - else if (sigpath::sourceManager.sourceNames.size() > 0) { - sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[0]); - } - else { - spdlog::warn("No source available..."); - } + std::string selected = core::configManager.conf["source"]; customOffset = core::configManager.conf["offset"]; offsetMode = core::configManager.conf["offsetMode"]; updateOffset(); + + refreshSources(); + selectSource(selected); + + sourceRegisteredHandler.handler = onSourceRegistered; + sourceUnregisterHandler.handler = onSourceUnregister; + sourceUnregisteredHandler.handler = onSourceUnregistered; + sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler); + sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler); + sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler); + core::configManager.release(); } void draw(void* ctx) { - std::string items = ""; - for (std::string name : sigpath::sourceManager.sourceNames) { - items += name; - items += '\0'; - } float itemWidth = ImGui::GetContentRegionAvailWidth(); if (gui::mainWindow.sdrIsRunning()) { style::beginDisabled(); } ImGui::SetNextItemWidth(itemWidth); - if (ImGui::Combo("##source", &sourceId, items.c_str())) { - sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[sourceId]); + if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) { + selectSource(sourceNames[sourceId]); core::configManager.acquire(); - core::configManager.conf["source"] = sigpath::sourceManager.sourceNames[sourceId]; + core::configManager.conf["source"] = sourceNames[sourceId]; core::configManager.release(true); } @@ -110,6 +171,5 @@ namespace sourecmenu { ImGui::InputDouble("##freq_offset", &effectiveOffset, 1.0, 100.0); style::endDisabled(); } - } } diff --git a/core/src/module.cpp b/core/src/module.cpp index b35ff17a..9ef41531 100644 --- a/core/src/module.cpp +++ b/core/src/module.cpp @@ -140,6 +140,14 @@ bool ModuleManager::instanceEnabled(std::string name) { return instances[name].instance->isEnabled(); } +void ModuleManager::postInit(std::string name) { + if (instances.find(name) == instances.end()) { + spdlog::error("Cannot post-init '{0}', instance doesn't exist", name); + return; + } + instances[name].instance->postInit(); +} + std::string ModuleManager::getInstanceModuleName(std::string name) { if (instances.find(name) == instances.end()) { spdlog::error("Cannot get module name of'{0}', instance doesn't exist", name); @@ -159,4 +167,11 @@ int ModuleManager::countModuleInstances(std::string module) { if (instance.module == mod) { count++; } } return count; +} + +void ModuleManager::doPostInitAll() { + for (auto& [name, inst] : instances) { + spdlog::info("Running post-init for {0}", name); + inst.instance->postInit(); + } } \ No newline at end of file diff --git a/core/src/module.h b/core/src/module.h index 7dd58b96..db6d97c7 100644 --- a/core/src/module.h +++ b/core/src/module.h @@ -42,6 +42,7 @@ public: class Instance { public: + virtual void postInit() = 0; virtual void enable() = 0; virtual void disable() = 0; virtual bool isEnabled() = 0; @@ -84,10 +85,13 @@ public: void enableInstance(std::string name); void disableInstance(std::string name); bool instanceEnabled(std::string name); + void postInit(std::string name); std::string getInstanceModuleName(std::string name); int countModuleInstances(std::string module); + void doPostInitAll(); + Event onInstanceCreated; Event onInstanceDelete; Event onInstanceDeleted; diff --git a/core/src/signal_path/sink.cpp b/core/src/signal_path/sink.cpp index 169ea7c5..ce850532 100644 --- a/core/src/signal_path/sink.cpp +++ b/core/src/signal_path/sink.cpp @@ -115,7 +115,7 @@ void SinkManager::registerStream(std::string name, SinkManager::Stream* stream) core::configManager.release(); if (available) { loadStreamConfig(name); } - streamRegisteredEvnt.emit(name); + onStreamRegistered.emit(name); } void SinkManager::unregisterStream(std::string name) { @@ -123,12 +123,13 @@ void SinkManager::unregisterStream(std::string name) { spdlog::error("Cannot unregister stream '{0}', this stream doesn't exist", name); return; } - streamUnregisteredEvnt.emit(name); + onStreamUnregister.emit(name); SinkManager::Stream* stream = streams[name]; stream->stop(); delete stream->sink; streams.erase(name); streamNames.erase(std::remove(streamNames.begin(), streamNames.end(), name), streamNames.end()); + onStreamUnregistered.emit(name); } void SinkManager::startStream(std::string name) { diff --git a/core/src/signal_path/sink.h b/core/src/signal_path/sink.h index 766ed490..950c7008 100644 --- a/core/src/signal_path/sink.h +++ b/core/src/signal_path/sink.h @@ -110,8 +110,9 @@ public: std::vector getStreamNames(); - Event streamRegisteredEvnt; - Event streamUnregisteredEvnt; + Event onStreamRegistered; + Event onStreamUnregister; + Event onStreamUnregistered; private: void loadStreamConfig(std::string name); diff --git a/core/src/signal_path/source.cpp b/core/src/signal_path/source.cpp index 8e734a56..a66cefea 100644 --- a/core/src/signal_path/source.cpp +++ b/core/src/signal_path/source.cpp @@ -12,7 +12,27 @@ void SourceManager::registerSource(std::string name, SourceHandler* handler) { return; } sources[name] = handler; - sourceNames.push_back(name); + onSourceRegistered.emit(name); +} + +void SourceManager::unregisterSource(std::string name) { + if (sources.find(name) == sources.end()) { + spdlog::error("Tried to unregister non existant source: {0}", name); + return; + } + onSourceUnregister.emit(name); + if (name == selectedName) { + sigpath::signalPath.setInput(&nullSource); + selectedHandler = NULL; + } + sources.erase(name); + onSourceUnregistered.emit(name); +} + +std::vector SourceManager::getSourceNames() { + std::vector names; + for (auto const& [name, src] : sources) { names.push_back(name); } + return names; } void SourceManager::selectSource(std::string name) { @@ -20,7 +40,7 @@ void SourceManager::selectSource(std::string name) { spdlog::error("Tried to select non existant source: {0}", name); return; } - if (selectedName != "") { + if (selectedHandler != NULL) { sources[selectedName]->deselectHandler(sources[selectedName]->ctx); } selectedHandler = sources[name]; diff --git a/core/src/signal_path/source.h b/core/src/signal_path/source.h index ce5d0bdf..1d692fa5 100644 --- a/core/src/signal_path/source.h +++ b/core/src/signal_path/source.h @@ -4,6 +4,7 @@ #include #include #include +#include class SourceManager { public: @@ -21,6 +22,7 @@ public: }; void registerSource(std::string name, SourceHandler* handler); + void unregisterSource(std::string name); void selectSource(std::string name); void showSelectedMenu(); void start(); @@ -28,7 +30,11 @@ public: void tune(double freq); void setTuningOffset(double offset); - std::vector sourceNames; + std::vector getSourceNames(); + + Event onSourceRegistered; + Event onSourceUnregister; + Event onSourceUnregistered; private: std::map sources; @@ -36,5 +42,6 @@ private: SourceHandler* selectedHandler = NULL; double tuneOffset; double currentFreq; + dsp::stream nullSource; }; \ No newline at end of file diff --git a/demo_module/src/main.cpp b/demo_module/src/main.cpp index daf10a31..2f31504c 100644 --- a/demo_module/src/main.cpp +++ b/demo_module/src/main.cpp @@ -21,6 +21,8 @@ public: gui::menu.removeEntry(name); } + void postInit() {} + void enable() { enabled = true; } diff --git a/discord_integration/src/main.cpp b/discord_integration/src/main.cpp index ae2b5f38..ec22c659 100644 --- a/discord_integration/src/main.cpp +++ b/discord_integration/src/main.cpp @@ -34,8 +34,12 @@ public: // Change to timer stop later on workerRunning = false; if (workerThread.joinable()) { workerThread.join(); } + Discord_ClearPresence(); + Discord_Shutdown(); } + void postInit() {} + void enable() { // Change to timer start later on workerRunning = true; diff --git a/falcon9_decoder/src/main.cpp b/falcon9_decoder/src/main.cpp index 81c9fd4a..160c5967 100644 --- a/falcon9_decoder/src/main.cpp +++ b/falcon9_decoder/src/main.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include #include #include #include @@ -87,6 +87,8 @@ public: } + void postInit() {} + void enable() { vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 4000000, INPUT_SAMPLE_RATE, 4000000, 4000000, true); @@ -155,7 +157,7 @@ private: if (ImGui::Button("Clear logs##GPSClear")) { _this->gpsLogs.clear(); } ImGui::BeginChild(ImGuiID("GPSChild")); ImGui::TextUnformatted(_this->gpsLogs.c_str()); - ImGui::SetScrollHere(1.0f); + ImGui::SetScrollHereY(1.0f); ImGui::EndChild(); ImGui::EndTabItem(); diff --git a/file_source/src/main.cpp b/file_source/src/main.cpp index 837f3d20..d6a1a5cf 100644 --- a/file_source/src/main.cpp +++ b/file_source/src/main.cpp @@ -41,15 +41,15 @@ public: handler.tuneHandler = tune; handler.stream = &stream; sigpath::sourceManager.registerSource("File", &handler); - - spdlog::info("FileSourceModule '{0}': Instance created!", name); } ~FileSourceModule() { - - spdlog::info("FileSourceModule '{0}': Instance deleted!", name); + stop(this); + sigpath::sourceManager.unregisterSource("File"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/frequency_manager/src/main.cpp b/frequency_manager/src/main.cpp index 4070d79e..7868ab04 100644 --- a/frequency_manager/src/main.cpp +++ b/frequency_manager/src/main.cpp @@ -87,6 +87,8 @@ public: gui::waterfall.onInputProcess.unbindHandler(&inputHandler); } + void postInit() {} + void enable() { enabled = true; } diff --git a/hackrf_source/src/main.cpp b/hackrf_source/src/main.cpp index ea8f56fb..b778a181 100644 --- a/hackrf_source/src/main.cpp +++ b/hackrf_source/src/main.cpp @@ -104,9 +104,12 @@ public: } ~HackRFSourceModule() { + stop(this); hackrf_exit(); + sigpath::sourceManager.unregisterSource("HackRF"); } + void postInit() {} void enable() { enabled = true; diff --git a/limesdr_source/src/main.cpp b/limesdr_source/src/main.cpp index d4b5fbab..2224ede9 100644 --- a/limesdr_source/src/main.cpp +++ b/limesdr_source/src/main.cpp @@ -44,15 +44,17 @@ public: // Select device from config selectFirst(); - core::setInputSampleRate(sampleRate); sigpath::sourceManager.registerSource("LimeSDR", &handler); } ~LimeSDRSourceModule() { - // Exit limesuite if needed + stop(this); + sigpath::sourceManager.unregisterSource("LimeSDR"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/meteor_demodulator/src/main.cpp b/meteor_demodulator/src/main.cpp index 03ed4ae8..0e889f84 100644 --- a/meteor_demodulator/src/main.cpp +++ b/meteor_demodulator/src/main.cpp @@ -94,6 +94,8 @@ public: gui::menu.removeEntry(name); } + void postInit() {} + void enable() { double bw = gui::waterfall.getBandwidth(); vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp(0, -bw/2.0, bw/2.0), 150000, INPUT_SAMPLE_RATE, 150000, 150000, true); diff --git a/new_portaudio_sink/src/main.cpp b/new_portaudio_sink/src/main.cpp index 8e7b1cfd..7794f07b 100644 --- a/new_portaudio_sink/src/main.cpp +++ b/new_portaudio_sink/src/main.cpp @@ -398,6 +398,8 @@ public: Pa_Terminate(); } + void postInit() {} + void enable() { enabled = true; } diff --git a/plutosdr_source/src/main.cpp b/plutosdr_source/src/main.cpp index 2241f230..61d5d522 100644 --- a/plutosdr_source/src/main.cpp +++ b/plutosdr_source/src/main.cpp @@ -50,14 +50,15 @@ public: handler.tuneHandler = tune; handler.stream = &stream; sigpath::sourceManager.registerSource("PlutoSDR", &handler); - - spdlog::info("PlutoSDRSourceModule '{0}': Instance created!", name); } ~PlutoSDRSourceModule() { - spdlog::info("PlutoSDRSourceModule '{0}': Instance deleted!", name); + stop(this); + sigpath::sourceManager.unregisterSource("PlutoSDR"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/portaudio_sink/src/main.cpp b/portaudio_sink/src/main.cpp index 4d9b155e..3f606f3b 100644 --- a/portaudio_sink/src/main.cpp +++ b/portaudio_sink/src/main.cpp @@ -296,6 +296,8 @@ public: Pa_Terminate(); } + void postInit() {} + void enable() { enabled = true; } diff --git a/radio/src/main.cpp b/radio/src/main.cpp index efa76d33..053e2601 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -79,6 +79,8 @@ public: sigpath::sinkManager.unregisterStream(name); } + void postInit() {} + void enable() { double bw = gui::waterfall.getBandwidth(); vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp(0, -bw/2.0, bw/2.0), 200000, 200000, 50000, 200000, false); diff --git a/recorder/src/main.cpp b/recorder/src/main.cpp index e3a98d50..95a05115 100644 --- a/recorder/src/main.cpp +++ b/recorder/src/main.cpp @@ -84,40 +84,51 @@ public: wavSampleBuf = new int16_t[2 * STREAM_BUFFER_SIZE]; - refreshStreams(); - gui::menu.registerEntry(name, menuHandler, this); core::modComManager.registerInterface("recorder", name, moduleInterfaceHandler, this); + + streamRegisteredHandler.handler = onStreamRegistered; + streamRegisteredHandler.ctx = this; + streamUnregisterHandler.handler = onStreamUnregister; + streamUnregisterHandler.ctx = this; + streamUnregisteredHandler.handler = onStreamUnregistered; + streamUnregisteredHandler.ctx = this; + sigpath::sinkManager.onStreamRegistered.bindHandler(&streamRegisteredHandler); + sigpath::sinkManager.onStreamUnregister.bindHandler(&streamUnregisterHandler); + sigpath::sinkManager.onStreamUnregistered.bindHandler(&streamUnregisteredHandler); } ~RecorderModule() { + std::lock_guard lck(recMtx); + gui::menu.removeEntry(name); core::modComManager.unregisterInterface(name); // Stop recording - if (recording) { - if (recMode == RECORDER_MODE_AUDIO) { - audioSplit.unbindStream(&audioHandlerStream); - audioHandler.stop(); - audioWriter->close(); - delete audioWriter; - } - else { - sigpath::signalPath.unbindIQStream(&basebandStream); - basebandHandler.stop(); - basebandWriter->close(); - delete basebandWriter; - } - } + if (recording) { stopRecording(); } + + if (audioInput != NULL) { sigpath::sinkManager.unbindStream(selectedStreamName, audioInput); } + + sigpath::sinkManager.onStreamRegistered.unbindHandler(&streamRegisteredHandler); + sigpath::sinkManager.onStreamUnregister.unbindHandler(&streamUnregisterHandler); + sigpath::sinkManager.onStreamUnregistered.unbindHandler(&streamUnregisteredHandler); vol.stop(); audioSplit.stop(); meter.stop(); - gui::menu.removeEntry(name); - delete[] wavSampleBuf; } + void postInit() { + refreshStreams(); + if (selectedStreamName == "") { + selectStream(streamNames[0]); + } + else { + selectStream(selectedStreamName); + } + } + void enable() { enabled = true; } @@ -134,35 +145,39 @@ private: void refreshStreams() { std::vector names = sigpath::sinkManager.getStreamNames(); - // If there are no stream, cancel - if (names.size() == 0) { return; } - - // List streams streamNames.clear(); streamNamesTxt = ""; + + // If there are no stream, cancel + if (names.size() == 0) {return; } + + // List streams for (auto const& name : names) { streamNames.push_back(name); streamNamesTxt += name; streamNamesTxt += '\0'; } - - if (selectedStreamName == "") { - selectStream(streamNames[0]); - } - else { - selectStream(selectedStreamName); - } } void selectStream(std::string name) { + if (streamNames.empty()) { + selectedStreamName = ""; + return; + } auto it = std::find(streamNames.begin(), streamNames.end(), name); - if (it == streamNames.end()) { return; } + if (it == streamNames.end()) { + selectStream(streamNames[0]); + return; + } streamId = std::distance(streamNames.begin(), it); vol.stop(); if (audioInput != NULL) { sigpath::sinkManager.unbindStream(selectedStreamName, audioInput); } audioInput = sigpath::sinkManager.bindStream(name); - if (audioInput == NULL) { return; } + if (audioInput == NULL) { + selectedStreamName = ""; + return; + } selectedStreamName = name; vol.setInput(audioInput); vol.start(); @@ -237,8 +252,6 @@ private: ImGui::PushItemWidth(menuColumnWidth); if (streamNames.size() == 0) { - refreshStreams(); - ImGui::PopItemWidth(); return; } @@ -339,6 +352,9 @@ private: } } else if (recMode == RECORDER_MODE_AUDIO) { + if (selectedStreamName.empty()) { + spdlog::error("Cannot record with no selected stream"); + } samplesWritten = 0; std::string expandedPath = expandString(folderSelect.path + genFileName("/audio_", true, selectedStreamName)); sampleRate = sigpath::sinkManager.getStreamSampleRate(selectedStreamName); @@ -372,6 +388,69 @@ private: } } + static void onStreamRegistered(std::string name, void* ctx){ + RecorderModule* _this = (RecorderModule*)ctx; + _this->refreshStreams(); + + if (_this->streamNames.empty()) { + _this->selectedStreamName = ""; + return; + } + + if (_this->selectedStreamName.empty()) { + _this->streamId = 0; + _this->selectedStreamName = _this->streamNames[0]; + return; + } + + // Reselect stream in UI to make sure the ID is correct + int id = 0; + for (auto& str : _this->streamNames) { + if (str == _this->selectedStreamName) { + _this->streamId = id; + break; + } + id++; + } + } + + static void onStreamUnregister(std::string name, void* ctx){ + RecorderModule* _this = (RecorderModule*)ctx; + if (name != _this->selectedStreamName) { return; } + if (_this->recording) { _this->stopRecording(); } + if (_this->audioInput != NULL) { + sigpath::sinkManager.unbindStream(_this->selectedStreamName, _this->audioInput); + _this->audioInput = NULL; + } + } + + static void onStreamUnregistered(std::string name, void* ctx){ + RecorderModule* _this = (RecorderModule*)ctx; + _this->refreshStreams(); + + if (_this->streamNames.empty()) { + _this->selectedStreamName = ""; + return; + } + + // If current stream was deleted, reselect steam completely + if (name == _this->selectedStreamName) { + _this->streamId = std::clamp(_this->streamId, 0, _this->streamNames.size() - 1); + _this->selectStream(_this->streamNames[_this->streamId]); + return; + } + + // Reselect stream in UI to make sure the ID is correct + int id = 0; + for (auto& str : _this->streamNames) { + if (str == _this->selectedStreamName) { + _this->streamId = id; + break; + } + id++; + } + } + std::string name; bool enabled = true; @@ -415,6 +494,10 @@ private: uint64_t samplesWritten; int16_t* wavSampleBuf; + EventHandler streamRegisteredHandler; + EventHandler streamUnregisterHandler; + EventHandler streamUnregisteredHandler; + }; struct RecorderContext_t { diff --git a/rigctl_server/src/main.cpp b/rigctl_server/src/main.cpp index 6993bdf7..b7bb2ab8 100644 --- a/rigctl_server/src/main.cpp +++ b/rigctl_server/src/main.cpp @@ -53,16 +53,41 @@ public: selectedRecorder = config.conf[name]["recorder"]; config.release(true); - initHandler.handler = _initHandler; - initHandler.ctx = this; - gui::mainWindow.onInitComplete.bindHandler(&initHandler); - gui::menu.registerEntry(name, menuHandler, this, NULL); } ~SigctlServerModule() { gui::menu.removeEntry(name); - // TODO: Use several handler instead of one for the recorders and remove event bindings + sigpath::vfoManager.onVfoCreated.unbindHandler(&vfoCreatedHandler); + sigpath::vfoManager.onVfoDeleted.unbindHandler(&vfoDeletedHandler); + core::moduleManager.onInstanceCreated.unbindHandler(&modChangedHandler); + core::moduleManager.onInstanceDeleted.unbindHandler(&modChangedHandler); + if (client) { client->close(); } + if (listener) { listener->close(); } + } + + void postInit() { + // Refresh modules + refreshModules(); + + // Select VFO and recorder from config + selectVfoByName(selectedVfo); + selectRecorderByName(selectedRecorder); + + // Bind handlers + vfoCreatedHandler.handler = _vfoCreatedHandler; + vfoCreatedHandler.ctx = this; + vfoDeletedHandler.handler = _vfoDeletedHandler; + vfoDeletedHandler.ctx = this; + modChangedHandler.handler = _modChangeHandler; + modChangedHandler.ctx = this; + sigpath::vfoManager.onVfoCreated.bindHandler(&vfoCreatedHandler); + sigpath::vfoManager.onVfoDeleted.bindHandler(&vfoDeletedHandler); + core::moduleManager.onInstanceCreated.bindHandler(&modChangedHandler); + core::moduleManager.onInstanceDeleted.bindHandler(&modChangedHandler); + + // If autostart is enabled, start the server + if (autoStart) { startServer(); } } void enable() { @@ -262,32 +287,6 @@ private: } } - static void _initHandler(bool dummy, void* ctx) { - SigctlServerModule* _this = (SigctlServerModule*)ctx; - - // Refresh modules - _this->refreshModules(); - - // Select VFO and recorder from config - _this->selectVfoByName(_this->selectedVfo); - _this->selectRecorderByName(_this->selectedRecorder); - - // Bind handlers - _this->vfoCreatedHandler.handler = _vfoCreatedHandler; - _this->vfoCreatedHandler.ctx = _this; - _this->vfoDeletedHandler.handler = _vfoDeletedHandler; - _this->vfoDeletedHandler.ctx = _this; - _this->modChangedHandler.handler = _modChangeHandler; - _this->modChangedHandler.ctx = _this; - sigpath::vfoManager.onVfoCreated.bindHandler(&_this->vfoCreatedHandler); - sigpath::vfoManager.onVfoDeleted.bindHandler(&_this->vfoDeletedHandler); - core::moduleManager.onInstanceCreated.bindHandler(&_this->modChangedHandler); - core::moduleManager.onInstanceDeleted.bindHandler(&_this->modChangedHandler); - - // If autostart is enabled, start the server - if (_this->autoStart) { _this->startServer(); } - } - static void _vfoCreatedHandler(VFOManager::VFO* vfo, void* ctx) { SigctlServerModule* _this = (SigctlServerModule*)ctx; _this->refreshModules(); @@ -465,7 +464,6 @@ private: std::string command = ""; - EventHandler initHandler; EventHandler modChangedHandler; EventHandler vfoCreatedHandler; EventHandler vfoDeletedHandler; diff --git a/rtl_sdr_source/src/main.cpp b/rtl_sdr_source/src/main.cpp index 576a5044..4365837c 100644 --- a/rtl_sdr_source/src/main.cpp +++ b/rtl_sdr_source/src/main.cpp @@ -88,15 +88,16 @@ public: config.release(true); selectByName(selectedDevName); - core::setInputSampleRate(sampleRate); - sigpath::sourceManager.registerSource("RTL-SDR", &handler); } ~RTLSDRSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("RTL-SDR"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/rtl_tcp_source/src/main.cpp b/rtl_tcp_source/src/main.cpp index 6c1df071..880f537e 100644 --- a/rtl_tcp_source/src/main.cpp +++ b/rtl_tcp_source/src/main.cpp @@ -87,9 +87,12 @@ public: } ~RTLTCPSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("RTL-TCP"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/sddc_source/src/main.cpp b/sddc_source/src/main.cpp index 6209ca8d..545220f8 100644 --- a/sddc_source/src/main.cpp +++ b/sddc_source/src/main.cpp @@ -44,15 +44,16 @@ public: selectFirst(); - core::setInputSampleRate(sampleRate); - sigpath::sourceManager.registerSource("SDDC", &handler); } ~AirspyHFSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("SDDC"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/sdrplay_source/src/main.cpp b/sdrplay_source/src/main.cpp index a542d4ef..82c6813b 100644 --- a/sdrplay_source/src/main.cpp +++ b/sdrplay_source/src/main.cpp @@ -155,9 +155,13 @@ public: } ~SDRPlaySourceModule() { + stop(this); sdrplay_api_Close(); + sigpath::sourceManager.unregisterSource("SDRplay"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/soapy_source/src/main.cpp b/soapy_source/src/main.cpp index ee77f881..afd47834 100644 --- a/soapy_source/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -50,14 +50,15 @@ public: handler.tuneHandler = tune; handler.stream = &stream; sigpath::sourceManager.registerSource("SoapySDR", &handler); - - spdlog::info("SoapyModule '{0}': Instance created!", name); } ~SoapyModule() { - spdlog::info("SoapyModule '{0}': Instance deleted!", name); + stop(this); + sigpath::sourceManager.unregisterSource("SoapySDR"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/spyserver_source/src/main.cpp b/spyserver_source/src/main.cpp index a04db419..cca24674 100644 --- a/spyserver_source/src/main.cpp +++ b/spyserver_source/src/main.cpp @@ -71,9 +71,12 @@ public: } ~AirspyHFSourceModule() { - + stop(this); + sigpath::sourceManager.unregisterSource("SpyServer"); } + void postInit() {} + void enable() { enabled = true; } diff --git a/weather_sat_decoder/src/main.cpp b/weather_sat_decoder/src/main.cpp index 8925f92c..e25ebe75 100644 --- a/weather_sat_decoder/src/main.cpp +++ b/weather_sat_decoder/src/main.cpp @@ -67,6 +67,8 @@ public: decoder->stop(); } + void postInit() {} + void enable() { vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 1000000, 1000000, 1000000, 1000000, true); for (auto const& [name, dec] : decoders) { dec->setVFO(vfo); }