diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c024e8..a55fbea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,13 @@ cmake_minimum_required(VERSION 3.13) project(sdrpp) -# Cross platform modules +# Core of SDR++ add_subdirectory("core") + +# Cross platform modules add_subdirectory("radio") add_subdirectory("recorder") -add_subdirectory("soapy") +add_subdirectory("soapy_source") #add_subdirectory("file_source") add_subdirectory("rtl_tcp_source") add_subdirectory("audio_sink") diff --git a/audio_sink/src/main.cpp b/audio_sink/src/main.cpp index dc7a8ee..b8bb1d8 100644 --- a/audio_sink/src/main.cpp +++ b/audio_sink/src/main.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -9,6 +9,14 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) +SDRPP_MOD_INFO { + /* Name: */ "audio_sink", + /* Description: */ "Audio sink module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 +}; + class AudioSink : SinkManager::Sink { public: struct AudioDevice_t { @@ -224,7 +232,7 @@ private: }; -class AudioSinkModule { +class AudioSinkModule : public ModuleManager::Instance { public: AudioSinkModule(std::string name) { this->name = name; @@ -237,12 +245,25 @@ public: } + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + private: static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) { return (SinkManager::Sink*)(new AudioSink(stream, streamName)); } std::string name; + bool enabled = true; SinkManager::SinkProvider provider; }; @@ -261,6 +282,6 @@ MOD_EXPORT void _DELETE_INSTANCE_() { } -MOD_EXPORT void _STOP_() { +MOD_EXPORT void _END_() { } \ No newline at end of file diff --git a/core/src/config.cpp b/core/src/config.cpp index 6c7c2b3..913d5fa 100644 --- a/core/src/config.cpp +++ b/core/src/config.cpp @@ -7,6 +7,10 @@ ConfigManager::ConfigManager() { } +ConfigManager::~ConfigManager() { + disableAutoSave(); +} + void ConfigManager::setPath(std::string file) { path = file; } @@ -42,13 +46,17 @@ void ConfigManager::save(bool lock) { } void ConfigManager::enableAutoSave() { - autoSaveEnabled = true; - autoSaveThread = std::thread(autoSaveWorker, this); + if (!autoSaveEnabled) { + autoSaveEnabled = true; + autoSaveThread = std::thread(autoSaveWorker, this); + } } void ConfigManager::disableAutoSave() { - autoSaveEnabled = false; - autoSaveThread.join(); + if (autoSaveEnabled) { + autoSaveEnabled = false; + autoSaveThread.join(); + } } void ConfigManager::aquire() { diff --git a/core/src/config.h b/core/src/config.h index 337ea48..5db967f 100644 --- a/core/src/config.h +++ b/core/src/config.h @@ -26,6 +26,7 @@ using nlohmann::json; class ConfigManager { public: ConfigManager(); + ~ConfigManager(); void setPath(std::string file); void load(json def, bool lock = true); void save(bool lock = true); diff --git a/core/src/core.cpp b/core/src/core.cpp index bb601b5..775128d 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -30,6 +29,7 @@ namespace core { ConfigManager configManager; ScriptManager scriptManager; + ModuleManager moduleManager; void setInputSampleRate(double samplerate) { // NOTE: Zoom controls won't work diff --git a/core/src/core.h b/core/src/core.h index 8fc7fba..d775193 100644 --- a/core/src/core.h +++ b/core/src/core.h @@ -1,11 +1,13 @@ #pragma once -#include #include +#include #include +#include namespace core { SDRPP_EXPORT ConfigManager configManager; SDRPP_EXPORT ScriptManager scriptManager; + SDRPP_EXPORT ModuleManager moduleManager; void setInputSampleRate(double samplerate); }; diff --git a/core/src/dsp/block.h b/core/src/dsp/block.h index 090f1ae..1de249c 100644 --- a/core/src/dsp/block.h +++ b/core/src/dsp/block.h @@ -15,6 +15,10 @@ namespace dsp { public: virtual void init() {} + virtual ~generic_block() { + stop(); + } + virtual void start() { std::lock_guard lck(ctrlMtx); if (running) { diff --git a/core/src/gui/gui.h b/core/src/gui/gui.h index e6a2ee5..7ba33d1 100644 --- a/core/src/gui/gui.h +++ b/core/src/gui/gui.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace gui { SDRPP_EXPORT ImGui::WaterFall waterfall; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index a6f407d..0a37085 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include @@ -81,6 +81,7 @@ bool grabbingMenu = false; int newWidth = 300; int fftHeight = 300; bool showMenu = true; +bool centerTuning = false; dsp::stream dummyStream; void windowInit() { @@ -113,7 +114,43 @@ void windowInit() { sigpath::signalPath.start(); spdlog::info("Loading modules"); - mod::loadFromList(ROOT_DIR "/module_list.json"); + + // Load modules from /module directory + if (std::filesystem::is_directory(ROOT_DIR "/modules")) { + for (const auto & file : std::filesystem::directory_iterator(ROOT_DIR "/modules")) { + std::string path = file.path().generic_string(); + if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) { + continue; + } + if (!file.is_regular_file()) { continue; } + spdlog::info("Loading {0}", path); + LoadingScreen::show("Loading " + path); + core::moduleManager.loadModule(path); + } + } + else { + spdlog::warn("Module directory {0} does not exist, not loading modules from directory"); + } + + // Read module config + core::configManager.aquire(); + std::vector modules = core::configManager.conf["modules"]; + std::map modList = core::configManager.conf["moduleInstances"]; + core::configManager.release(); + + // Load additional modules specified through config + for (auto const& path : modules) { + spdlog::info("Loading {0}", path); + LoadingScreen::show("Loading " + path); + core::moduleManager.loadModule(path); + } + + // Create module instances + for (auto const& [name, module] : modList) { + spdlog::info("Initializing {0} ({1})", name, module); + LoadingScreen::show("Initializing " + name + " (" + module + ")"); + core::moduleManager.createInstance(name, module); + } sourecmenu::init(); sinkmenu::init(); @@ -158,10 +195,21 @@ void windowInit() { fftHeight = core::configManager.conf["fftHeight"]; gui::waterfall.setFFTHeight(fftHeight); + centerTuning = core::configManager.conf["centerTuning"]; + core::configManager.release(); } void setVFO(double freq) { + double viewBW = gui::waterfall.getViewBandwidth(); + double BW = gui::waterfall.getBandwidth(); + if (gui::waterfall.selectedVFO == "") { + gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0)); + gui::waterfall.setCenterFrequency(freq); + sigpath::sourceManager.tune(freq); + return; + } + ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO]; double currentOff = vfo->centerOffset; @@ -174,15 +222,21 @@ void setVFO(double freq) { double vfoTop = newVFO + (vfoBW / 2.0); double view = gui::waterfall.getViewOffset(); - double viewBW = gui::waterfall.getViewBandwidth(); double viewBottom = view - (viewBW / 2.0); double viewTop = view + (viewBW / 2.0); double wholeFreq = gui::waterfall.getCenterFrequency(); - double BW = gui::waterfall.getBandwidth(); double bottom = -(BW / 2.0); double top = (BW / 2.0); + if (centerTuning) { + gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0)); + gui::waterfall.setCenterFrequency(freq); + sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, 0); + sigpath::sourceManager.tune(freq); + return; + } + // VFO still fints in the view if (vfoBottom > viewBottom && vfoTop < viewTop) { sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO); @@ -249,19 +303,24 @@ void setVFO(double freq) { void drawWindow() { ImGui::Begin("Main", NULL, WINDOW_FLAGS); - ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO]; - - if (vfo->centerOffsetChanged) { - gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); - gui::freqSelect.frequencyChanged = false; - core::configManager.aquire(); - core::configManager.conf["frequency"] = gui::freqSelect.frequency; - core::configManager.release(true); + ImGui::WaterfallVFO* vfo = NULL; + if (gui::waterfall.selectedVFO != "") { + vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO]; } + if (vfo != NULL) { + if (vfo->centerOffsetChanged) { + gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); + gui::freqSelect.frequencyChanged = false; + core::configManager.aquire(); + core::configManager.conf["frequency"] = gui::freqSelect.frequency; + core::configManager.release(true); + } + } + sigpath::vfoManager.updateFromWaterfall(&gui::waterfall); - if (gui::waterfall.selectedVFOChanged) { + if (gui::waterfall.selectedVFOChanged && vfo != NULL) { gui::waterfall.selectedVFOChanged = false; gui::freqSelect.setFrequency(vfo->generalOffset + gui::waterfall.getCenterFrequency()); gui::freqSelect.frequencyChanged = false; @@ -273,9 +332,11 @@ void drawWindow() { if (gui::freqSelect.frequencyChanged) { gui::freqSelect.frequencyChanged = false; setVFO(gui::freqSelect.frequency); - vfo->centerOffsetChanged = false; - vfo->lowerOffsetChanged = false; - vfo->upperOffsetChanged = false; + if (vfo != NULL) { + vfo->centerOffsetChanged = false; + vfo->lowerOffsetChanged = false; + vfo->upperOffsetChanged = false; + } core::configManager.aquire(); core::configManager.conf["frequency"] = gui::freqSelect.frequency; core::configManager.release(true); @@ -284,7 +345,12 @@ void drawWindow() { if (gui::waterfall.centerFreqMoved) { gui::waterfall.centerFreqMoved = false; sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency()); - gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); + if (vfo != NULL) { + gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); + } + else { + gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency()); + } core::configManager.aquire(); core::configManager.conf["frequency"] = gui::freqSelect.frequency; core::configManager.release(true); @@ -437,7 +503,9 @@ void drawWindow() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "")) { gui::waterfall.setViewBandwidth(bw); - gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen + if (vfo != NULL) { + gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen + } } ImGui::NewLine(); diff --git a/core/src/gui/widgets/menu.cpp b/core/src/gui/widgets/menu.cpp index 3bd06ed..bd54ac4 100644 --- a/core/src/gui/widgets/menu.cpp +++ b/core/src/gui/widgets/menu.cpp @@ -1,14 +1,16 @@ #include #include +#include Menu::Menu() { } -void Menu::registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx) { +void Menu::registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx, ModuleManager::Instance* inst) { MenuItem_t item; item.drawHandler = drawHandler; item.ctx = ctx; + item.inst = inst; items[name] = item; if (!isInOrderList(name)) { order.push_back(name); @@ -21,15 +23,47 @@ void Menu::removeEntry(std::string name) { void Menu::draw() { MenuItem_t item; + float menuWidth = ImGui::GetContentRegionAvailWidth(); + ImGuiWindow* window = ImGui::GetCurrentWindow(); for (std::string name : order) { if (items.find(name) == items.end()) { continue; } item = items[name]; + + ImRect orginalRect = window->WorkRect; + if (item.inst != NULL) { + + window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - 6, orginalRect.Max.y)); + } + if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (item.inst != NULL) { + window->WorkRect = orginalRect; + ImVec2 pos = ImGui::GetCursorPos(); + ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); + ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); + bool enabled = item.inst->isEnabled(); + if (ImGui::Checkbox(("##_menu_checkbox_" + name).c_str(), &enabled)) { + enabled ? item.inst->enable() : item.inst->disable(); + } + ImGui::SetCursorPos(pos); + } + item.drawHandler(item.ctx); ImGui::Spacing(); } + else if (item.inst != NULL) { + window->WorkRect = orginalRect; + ImVec2 pos = ImGui::GetCursorPos(); + ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); + ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); + bool enabled = item.inst->isEnabled(); + if (ImGui::Checkbox(("##_menu_checkbox_" + name).c_str(), &enabled)) { + enabled ? item.inst->enable() : item.inst->disable(); + } + ImGui::SetCursorPos(pos); + } } } diff --git a/core/src/gui/widgets/menu.h b/core/src/gui/widgets/menu.h index 6e7965e..825f308 100644 --- a/core/src/gui/widgets/menu.h +++ b/core/src/gui/widgets/menu.h @@ -2,6 +2,7 @@ #include #include #include +#include class Menu { public: @@ -10,9 +11,10 @@ public: struct MenuItem_t { void (*drawHandler)(void* ctx); void* ctx; + ModuleManager::Instance* inst; }; - void registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx = NULL); + void registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx = NULL, ModuleManager::Instance* inst = NULL); void removeEntry(std::string name); void draw(); diff --git a/core/src/gui/widgets/waterfall.cpp b/core/src/gui/widgets/waterfall.cpp index f211e93..961fae2 100644 --- a/core/src/gui/widgets/waterfall.cpp +++ b/core/src/gui/widgets/waterfall.cpp @@ -195,14 +195,22 @@ namespace ImGui { } void WaterFall::selectFirstVFO() { + bool available = false; for (auto const& [name, vfo] : vfos) { + available = true; selectedVFO = name; return; } + if (!available) { + selectedVFO = ""; + } } void WaterFall::processInputs() { - WaterfallVFO* vfo = vfos[selectedVFO]; + WaterfallVFO* vfo = NULL; + if (selectedVFO != "") { + vfo = vfos[selectedVFO]; + } ImVec2 mousePos = ImGui::GetMousePos(); ImVec2 drag = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left); ImVec2 dragOrigin(mousePos.x - drag.x, mousePos.y - drag.y); @@ -229,19 +237,21 @@ namespace ImGui { return; } } - int refCenter = mousePos.x - (widgetPos.x + 50); - if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y)) { - double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset; - off += centerFreq; - off = (round(off / vfo->snapInterval) * vfo->snapInterval) - centerFreq; - vfo->setOffset(off); + if (vfo != NULL) { + int refCenter = mousePos.x - (widgetPos.x + 50); + if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y)) { + double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset; + off += centerFreq; + off = (round(off / vfo->snapInterval) * vfo->snapInterval) - centerFreq; + vfo->setOffset(off); + } } } // Draging VFO if (draging && mouseInFFT) { int refCenter = mousePos.x - (widgetPos.x + 50); - if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y)) { + if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y) && vfo != NULL) { double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset; off += centerFreq; off = (round(off / vfo->snapInterval) * vfo->snapInterval) - centerFreq; @@ -434,7 +444,9 @@ namespace ImGui { widgetEndPos.y += window->Pos.y; widgetSize = ImVec2(widgetEndPos.x - widgetPos.x, widgetEndPos.y - widgetPos.y); - + if (selectedVFO == "" && vfos.size() > 0) { + selectFirstVFO(); + } if (widgetPos.x != lastWidgetPos.x || widgetPos.y != lastWidgetPos.y) { lastWidgetPos = widgetPos; diff --git a/core/src/gui/widgets/waterfall.h b/core/src/gui/widgets/waterfall.h index f31a2a9..0b9bd00 100644 --- a/core/src/gui/widgets/waterfall.h +++ b/core/src/gui/widgets/waterfall.h @@ -104,7 +104,7 @@ namespace ImGui { bandplan::BandPlan_t* bandplan = NULL; std::map vfos; - std::string selectedVFO; + std::string selectedVFO = ""; bool selectedVFOChanged = false; enum { diff --git a/core/src/module.cpp b/core/src/module.cpp deleted file mode 100644 index 2afb9e1..0000000 --- a/core/src/module.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using nlohmann::json; - -namespace mod { - std::map modules; - std::vector moduleNames; - - void loadModule(std::string path, std::string name) { - if (!std::filesystem::exists(path)) { - spdlog::error("{0} does not exist", path); - return; - } - if (!std::filesystem::is_regular_file(path)) { - spdlog::error("{0} isn't a loadable module", path); - return; - } - Module_t mod; -#ifdef _WIN32 - mod.inst = LoadLibraryA(path.c_str()); - if (mod.inst == NULL) { - spdlog::error("Couldn't load {0}.", name); - return; - } - - mod._INIT_ = (void(*)())GetProcAddress(mod.inst, "_INIT_"); - mod._CREATE_INSTANCE_ = (void*(*)(std::string))GetProcAddress(mod.inst, "_CREATE_INSTANCE_"); - mod._DELETE_INSTANCE_ = (void(*)(void*))GetProcAddress(mod.inst, "_DELETE_INSTANCE_"); - mod._STOP_ = (void(*)())GetProcAddress(mod.inst, "_STOP_"); -#else - mod.inst = dlopen(path.c_str(), RTLD_LAZY); - if (mod.inst == NULL) { - spdlog::error("Couldn't load {0}.", name); - return; - } - mod._INIT_ = (void(*)())dlsym(mod.inst, "_INIT_"); - mod._CREATE_INSTANCE_ = (void*(*)(std::string))dlsym(mod.inst, "_CREATE_INSTANCE_"); - mod._DELETE_INSTANCE_ = (void(*)(void*))dlsym(mod.inst, "_DELETE_INSTANCE_"); - mod._STOP_ = (void(*)())dlsym(mod.inst, "_STOP_"); -#endif - if (mod._INIT_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _INIT_.", name); - return; - } - if (mod._CREATE_INSTANCE_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _CREATE_INSTANCE_.", name); - return; - } - if (mod._DELETE_INSTANCE_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _DELETE_INSTANCE_.", name); - return; - } - if (mod._STOP_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _STOP_.", name); - return; - } - - if (!isLoaded(mod.inst)) { - mod._INIT_(); - } - - mod.ctx = mod._CREATE_INSTANCE_(name); - if (mod.ctx == NULL) { - spdlog::error("{0} Failed to initialize.", name); - } - - modules[name] = mod; - moduleNames.push_back(name); - } - - void loadFromList(std::string path) { - if (!std::filesystem::exists(path)) { - spdlog::error("Module list file does not exist"); - return; - } - if (!std::filesystem::is_regular_file(path)) { - spdlog::error("Module list file isn't a file..."); - return; - } - std::ifstream file(path.c_str()); - json data; - file >> data; - file.close(); - - std::map list = data.get>(); - for (auto const& [name, file] : list) { - std::string msg = "Loading "; - msg += name; - msg += " ("; - msg += file; - msg += ")"; - LoadingScreen::show(msg); - spdlog::info("Loading {0} ({1})", name, file); - loadModule(file, name); - } - } - - bool isLoaded(void* handle) { - for (auto const& [name, module] : modules) { - if (module.inst == handle) { - return true; - } - } - return false; - } -}; - diff --git a/core/src/module.h b/core/src/module.h deleted file mode 100644 index 807f8b4..0000000 --- a/core/src/module.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include -#include -#include - -#ifdef _WIN32 -#ifdef SDRPP_IS_CORE -#define SDRPP_EXPORT extern "C" __declspec(dllexport) -#else -#define SDRPP_EXPORT extern "C" __declspec(dllimport) -#endif -#else -#define SDRPP_EXPORT extern -#endif - -#ifdef _WIN32 -#include -#define MOD_EXPORT extern "C" __declspec(dllexport) -#else -#include -#define MOD_EXPORT extern "C" -#endif - -namespace mod { - - struct Module_t { -#ifdef _WIN32 - HINSTANCE inst; -#else - void* inst; -#endif - void (*_INIT_)(); - void* (*_CREATE_INSTANCE_)(std::string name); - void (*_DELETE_INSTANCE_)(void* instance); - void (*_STOP_)(); - void* ctx; - }; - - struct ModuleInfo_t { - const char* name; - const char* description; - const char* author; - const char* version; - }; - - void loadModule(std::string path, std::string name); - void loadFromList(std::string path); - bool isLoaded(void* handle); - - extern std::map modules; - extern std::vector moduleNames; -}; - -#define MOD_INFO MOD_EXPORT const mod::ModuleInfo_t _INFO \ No newline at end of file diff --git a/core/src/new_module.cpp b/core/src/new_module.cpp new file mode 100644 index 0000000..4f41d8b --- /dev/null +++ b/core/src/new_module.cpp @@ -0,0 +1,145 @@ +#include +#include +#include + +ModuleManager::Module_t ModuleManager::loadModule(std::string path) { + Module_t mod; + if (!std::filesystem::exists(path)) { + spdlog::error("{0} does not exist", path); + mod.handle = NULL; + return mod; + } + if (!std::filesystem::is_regular_file(path)) { + spdlog::error("{0} isn't a loadable module", path); + mod.handle = NULL; + return mod; + } +#ifdef _WIN32 + mod.handle = LoadLibraryA(path.c_str()); + if (mod.handle == NULL) { + spdlog::error("Couldn't load {0}.", path); + mod.handle = NULL; + return mod; + } + mod.info = (ModuleInfo_t*)GetProcAddress(mod.handle, "_INFO_"); + mod.init = (void(*)())GetProcAddress(mod.handle, "_INIT_"); + mod.createInstance = (Instance*(*)(std::string))GetProcAddress(mod.handle, "_CREATE_INSTANCE_"); + mod.deleteInstance = (void(*)(Instance*))GetProcAddress(mod.handle, "_DELETE_INSTANCE_"); + mod.end = (void(*)())GetProcAddress(mod.handle, "_END_"); +#else + mod.handle = dlopen(path.c_str(), RTLD_LAZY); + if (mod.handle == NULL) { + spdlog::error("Couldn't load {0}.", path); + mod.handle = NULL; + return mod; + } + mod.info = (ModuleInfo_t*)dlsym(mod.handle, "_INFO_"); + mod.init = (void(*)())dlsym(mod.handle, "_INIT_"); + mod.createInstance = (Instance*(*)(std::string))dlsym(mod.handle, "_CREATE_INSTANCE_"); + mod.deleteInstance = (void(*)(Instance*))dlsym(mod.handle, "_DELETE_INSTANCE_"); + mod.end = (void(*)())dlsym(mod.handle, "_END_"); +#endif + if (mod.info == NULL) { + spdlog::error("{0} is missing _INFO_ symbol", path); + mod.handle = NULL; + return mod; + } + if (mod.init == NULL) { + spdlog::error("{0} is missing _INIT_ symbol", path); + mod.handle = NULL; + return mod; + } + if (mod.createInstance == NULL) { + spdlog::error("{0} is missing _CREATE_INSTANCE_ symbol", path); + mod.handle = NULL; + return mod; + } + if (mod.deleteInstance == NULL) { + spdlog::error("{0} is missing _DELETE_INSTANCE_ symbol", path); + mod.handle = NULL; + return mod; + } + if (mod.end == NULL) { + spdlog::error("{0} is missing _END_ symbol", path); + mod.handle = NULL; + return mod; + } + if (modules.find(mod.info->name) != modules.end()) { + spdlog::error("{0} has the same name as an already loaded module", path); + mod.handle = NULL; + return mod; + } + for (auto const& [name, _mod] : modules) { + if (mod.handle == _mod.handle) { + return _mod; + } + } + mod.init(); + modules[mod.info->name] = mod; + return mod; +} + +void ModuleManager::createInstance(std::string name, std::string module) { + if (modules.find(module) == modules.end()) { + spdlog::error("Module '{0}' doesn't exist", module); + return; + } + if (instances.find(name) != instances.end()) { + spdlog::error("A module instance with the name '{0}' already exists", name); + return; + } + int maxCount = modules[module].info->maxInstances; + if (countModuleInstances(module) >= maxCount && maxCount > 0) { + spdlog::error("Maximum number of instances reached for '{0}'", module); + return; + } + Instance_t inst; + inst.module = modules[module]; + inst.instance = inst.module.createInstance(name); + instances[name] = inst; +} + +void ModuleManager::deleteInstance(std::string name) { + spdlog::error("DELETE INSTANCE NOT IMPLEMENTED"); +} + +void ModuleManager::deleteInstance(ModuleManager::Instance* instance) { + spdlog::error("DELETE INSTANCE NOT IMPLEMENTED"); +} + +void ModuleManager::enableInstance(std::string name) { + if (instances.find(name) == instances.end()) { + spdlog::error("Cannot enable '{0}', instance doesn't exist", name); + return; + } + instances[name].instance->enable(); +} + +void ModuleManager::disableInstance(std::string name) { + if (instances.find(name) == instances.end()) { + spdlog::error("Cannot disable '{0}', instance doesn't exist", name); + return; + } + instances[name].instance->disable(); +} + +bool ModuleManager::instanceEnabled(std::string name) { + if (instances.find(name) == instances.end()) { + spdlog::error("Cannot check if '{0}' is enabled, instance doesn't exist", name); + return false; + } + return instances[name].instance->isEnabled(); +} + +int ModuleManager::countModuleInstances(std::string module) { + if (modules.find(module) == modules.end()) { + spdlog::error("Cannot count instances of '{0}', Module doesn't exist", module); + return -1; + } + ModuleManager::Module_t mod = modules[module]; + int count = 0; + for (auto const& [name, instance] : instances) { + if (instance.module == mod) { count++; } + } + return count; +} \ No newline at end of file diff --git a/core/src/new_module.h b/core/src/new_module.h new file mode 100644 index 0000000..044a489 --- /dev/null +++ b/core/src/new_module.h @@ -0,0 +1,91 @@ +#pragma once +#include +#include +#include + +#ifdef _WIN32 +#ifdef SDRPP_IS_CORE +#define SDRPP_EXPORT extern "C" __declspec(dllexport) +#else +#define SDRPP_EXPORT extern "C" __declspec(dllimport) +#endif +#else +#define SDRPP_EXPORT extern +#endif + +#ifdef _WIN32 +#include +#define MOD_EXPORT extern "C" __declspec(dllexport) +#define SDRPP_MOD_EXTENTSION ".dll" +#else +#include +#define MOD_EXPORT extern "C" +#define SDRPP_MOD_EXTENTSION ".so" +#endif + +class ModuleManager { +public: + struct ModuleInfo_t { + const char* name; + const char* description; + const char* author; + const int versionMajor; + const int versionMinor; + const int versionBuild; + const int maxInstances; + }; + + class Instance { + public: + virtual void enable() = 0; + virtual void disable() = 0; + virtual bool isEnabled() = 0; + }; + + struct Module_t { +#ifdef _WIN32 + HMODULE handle; +#else + void* handle; +#endif + ModuleManager::ModuleInfo_t* info; + void (*init)(); + ModuleManager::Instance* (*createInstance)(std::string name); + void (*deleteInstance)(ModuleManager::Instance* instance); + void (*end)(); + + friend bool operator==(const Module_t& a, const Module_t& b) { + if (a.handle != b.handle) { return false; } + if (a.info != b.info) { return false; } + if (a.init != b.init) { return false; } + if (a.createInstance != b.createInstance) { return false; } + if (a.deleteInstance != b.deleteInstance) { return false; } + if (a.end != b.end) { return false; } + return true; + } + }; + + struct Instance_t { + ModuleManager::Module_t module; + ModuleManager::Instance* instance; + }; + + ModuleManager::Module_t loadModule(std::string path); + + void createInstance(std::string name, std::string module); + void deleteInstance(std::string name); + void deleteInstance(ModuleManager::Instance* instance); + + void enableInstance(std::string name); + void disableInstance(std::string name); + bool instanceEnabled(std::string name); + + int countModuleInstances(std::string module); + +private: + std::map modules; + std::map instances; + +}; + +#define SDRPP_MOD_INFO MOD_EXPORT const ModuleManager::ModuleInfo_t _INFO_ \ No newline at end of file diff --git a/core/src/signal_path/dsp.cpp b/core/src/signal_path/dsp.cpp index 528e658..5ef07a2 100644 --- a/core/src/signal_path/dsp.cpp +++ b/core/src/signal_path/dsp.cpp @@ -50,6 +50,12 @@ void SignalPath::start() { fftHandlerSink.start(); } +void SignalPath::stop() { + split.stop(); + reshape.stop(); + fftHandlerSink.stop(); +} + dsp::VFO* SignalPath::addVFO(std::string name, double outSampleRate, double bandwidth, double offset) { if (vfos.find(name) != vfos.end()) { return NULL; diff --git a/core/src/signal_path/dsp.h b/core/src/signal_path/dsp.h index 900e3cf..128a7ea 100644 --- a/core/src/signal_path/dsp.h +++ b/core/src/signal_path/dsp.h @@ -3,13 +3,13 @@ #include #include #include -#include class SignalPath { public: SignalPath(); void init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::complex_t*,int,void*)); void start(); + void stop(); void setSampleRate(double sampleRate); void setFFTRate(double rate); double getSampleRate(); diff --git a/core/src/signal_path/signal_path.h b/core/src/signal_path/signal_path.h index 4969327..7bdf610 100644 --- a/core/src/signal_path/signal_path.h +++ b/core/src/signal_path/signal_path.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace sigpath { SDRPP_EXPORT SignalPath signalPath; diff --git a/core/src/signal_path/sink.cpp b/core/src/signal_path/sink.cpp index 27222d3..a1a2d3a 100644 --- a/core/src/signal_path/sink.cpp +++ b/core/src/signal_path/sink.cpp @@ -174,18 +174,18 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w float ypos = ImGui::GetCursorPosY(); - if (streams.find(name) == streams.end()) { + if (streams.find(name) == streams.end() || name == "") { float dummy = 0.0f; style::beginDisabled(); - ImGui::SetNextItemWidth(width - height); - ImGui::PushID(ImGui::GetID(("sdrpp_dummy_mute_btn_" + name).c_str())); + ImGui::PushID(ImGui::GetID(("sdrpp_unmute_btn_" + name).c_str())); ImGui::ImageButton(icons::MUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), btwBorder); ImGui::PopID(); ImGui::SameLine(); - ImGui::SetCursorPosY(ypos - ((height - sliderHeight) / 2.0f)); + ImGui::SetNextItemWidth(width - height - 8); + ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); ImGui::SliderFloat((prefix + name).c_str(), &dummy, 0.0f, 1.0f, ""); - ImGui::SetCursorPosY(ypos); style::endDisabled(); + return; } SinkManager::Stream* stream = streams[name]; diff --git a/core/src/signal_path/vfo_manager.cpp b/core/src/signal_path/vfo_manager.cpp index 019a309..d7be903 100644 --- a/core/src/signal_path/vfo_manager.cpp +++ b/core/src/signal_path/vfo_manager.cpp @@ -13,7 +13,11 @@ VFOManager::VFO::VFO(std::string name, int reference, double offset, double band } VFOManager::VFO::~VFO() { + dspVFO->stop(); gui::waterfall.vfos.erase(name); + if (gui::waterfall.selectedVFO == name) { + gui::waterfall.selectFirstVFO(); + } sigpath::signalPath.removeVFO(name); delete wtfVFO; } @@ -76,6 +80,7 @@ void VFOManager::deleteVFO(VFOManager::VFO* vfo) { if (name == "") { return; } + delete vfo; vfos.erase(name); } diff --git a/plutosdr_source/src/main.cpp b/plutosdr_source/src/main.cpp index 6222f7c..f085130 100644 --- a/plutosdr_source/src/main.cpp +++ b/plutosdr_source/src/main.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -10,11 +10,12 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) -MOD_INFO { - /* Name: */ "plutosdr_source", - /* Description: */ "PlutoSDR input module for SDR++", - /* Author: */ "Ryzerth", - /* Version: */ "0.1.0" +SDRPP_MOD_INFO { + /* Name: */ "plutosdr_source", + /* Description: */ "PlutoSDR source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 }; const char* gainModes[] = { @@ -25,7 +26,7 @@ const char* gainModesTxt = "Manual\0Fast Attack\0Slow Attack\0Hybrid\0"; ConfigManager config; -class PlutoSDRSourceModule { +class PlutoSDRSourceModule : public ModuleManager::Instance { public: PlutoSDRSourceModule(std::string name) { this->name = name; @@ -55,6 +56,18 @@ public: spdlog::info("PlutoSDRSourceModule '{0}': Instance deleted!", name); } + void enable() { + enabled = true; + } + + void disable() { + enabled = true; + } + + bool isEnabled() { + return enabled; + } + private: static void menuSelected(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; @@ -227,6 +240,7 @@ private: } std::string name; + bool enabled = true; dsp::stream stream; float sampleRate; SourceManager::SourceHandler handler; @@ -253,15 +267,15 @@ MOD_EXPORT void _INIT_() { config.enableAutoSave(); } -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { return new PlutoSDRSourceModule(name); } -MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { delete (PlutoSDRSourceModule*)instance; } -MOD_EXPORT void _STOP_() { +MOD_EXPORT void _END_() { config.disableAutoSave(); config.save(); } \ No newline at end of file diff --git a/radio/src/main.cpp b/radio/src/main.cpp index d27fc4d..2e42111 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -1,10 +1,11 @@ #include -#include #include #include #include +#include #include #include +#include #include #include #include @@ -16,20 +17,23 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) -MOD_INFO { - /* Name: */ "radio", - /* Description: */ "Radio module for SDR++", - /* Author: */ "Ryzerth", - /* Version: */ "0.3.0" +SDRPP_MOD_INFO { + /* Name: */ "radio", + /* Description: */ "Radio module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 3, 0, + /* Max instances */ -1 }; -class RadioModule { +class RadioModule : public ModuleManager::Instance { public: RadioModule(std::string name) { this->name = name; vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 1); + ns.init(vfo->output); + wfmDemod.init(name, vfo, audioSampRate, 200000); fmDemod.init(name, vfo, audioSampRate, 12500); amDemod.init(name, vfo, audioSampRate, 12500); @@ -50,16 +54,40 @@ public: stream.start(); - gui::menu.registerEntry(name, menuHandler, this); + gui::menu.registerEntry(name, menuHandler, this, this); } ~RadioModule() { } + void enable() { + vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 1); + //ns.stop(); + currentDemod->setVFO(vfo); + currentDemod->select(); + currentDemod->start(); + enabled = true; + } + + void disable() { + currentDemod->stop(); + sigpath::vfoManager.deleteVFO(vfo); + //ns.setInput(vfo->output); + //ns.start(); + enabled = false; + } + + bool isEnabled() { + return enabled; + } + private: static void menuHandler(void* ctx) { RadioModule* _this = (RadioModule*)ctx; + + if (!_this->enabled) { style::beginDisabled(); } + float menuWidth = ImGui::GetContentRegionAvailWidth(); ImGui::BeginGroup(); @@ -106,6 +134,8 @@ private: ImGui::EndGroup(); _this->currentDemod->showMenu(); + + if (!_this->enabled) { style::endDisabled(); } } static void sampleRateChangeHandler(float sampleRate, void* ctx) { @@ -128,6 +158,7 @@ private: } std::string name; + bool enabled = true; int demodId = 0; float audioSampRate = 48000; Demodulator* currentDemod = NULL; @@ -143,6 +174,8 @@ private: RAWDemodulator rawDemod; CWDemodulator cwDemod; + dsp::NullSink ns; + Event::EventHandler srChangeHandler; SinkManager::Stream stream; @@ -152,7 +185,7 @@ MOD_EXPORT void _INIT_() { // Do your one time init here } -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { return new RadioModule(name); } @@ -160,6 +193,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { delete (RadioModule*)instance; } -MOD_EXPORT void _STOP_() { +MOD_EXPORT void _END_() { // Do your one shutdown here } \ No newline at end of file diff --git a/recorder/src/main.cpp b/recorder/src/main.cpp index aa16aea..815bd66 100644 --- a/recorder/src/main.cpp +++ b/recorder/src/main.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -15,6 +15,14 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) +SDRPP_MOD_INFO { + /* Name: */ "recorder", + /* Description: */ "Recorder module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 2, + /* Max instances */ -1 +}; + // TODO: Fix this and finish module std::string genFileName(std::string prefix) { @@ -38,7 +46,7 @@ std::string expandString(std::string input) { return std::regex_replace(input, std::regex("//"), "/"); } -class RecorderModule { +class RecorderModule : public ModuleManager::Instance { public: RecorderModule(std::string name) { this->name = name; @@ -54,6 +62,18 @@ public: } + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + private: static void menuHandler(void* ctx) { RecorderModule* _this = (RecorderModule*)ctx; @@ -239,6 +259,7 @@ private: } std::string name; + bool enabled = true; char path[4096]; bool pathValid = true; dsp::stream* audioStream; @@ -266,15 +287,14 @@ MOD_EXPORT void _INIT_() { // Nothing here } -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { - RecorderModule* instance = new RecorderModule(name); - return instance; +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new RecorderModule(name); } -MOD_EXPORT void _DELETE_INSTANCE_() { - +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* inst) { + delete (RecorderModule*)inst; } -MOD_EXPORT void _STOP_(RecorderContext_t* ctx) { +MOD_EXPORT void _END_(RecorderContext_t* ctx) { } \ No newline at end of file diff --git a/root_dev/config.json b/root_dev/config.json index f33c289..4eb63e0 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -1,13 +1,15 @@ { "bandPlan": "General", "bandPlanEnabled": true, - "fftHeight": 296, - "frequency": 99000000, + "centerTuning": true, + "fftHeight": 300, + "frequency": 100100000, "max": 0.0, - "maximized": true, + "maximized": false, "menuOrder": [ "Source", - "Radio", + "Radio 1", + "Radio 2", "Recorder", "Sinks", "Audio", @@ -16,16 +18,41 @@ "Display" ], "menuWidth": 300, - "min": -70.5882339477539, + "min": -70.0, + "moduleInstances": { + "Audio Sink": "audio_sink", + "PlutoSDR Source": "plutosdr_source", + "RTL-TCP Source": "rtl_tcp_source", + "Radio 1": "radio", + "Recorder": "recorder", + "SoapySDR Source": "soapy_source" + }, + "modules": [ + "./radio/Release/radio.dll", + "./recorder/Release/recorder.dll", + "./soapy_source/Release/soapy_source.dll", + "./rtl_tcp_source/Release/rtl_tcp_source.dll", + "./audio_sink/Release/audio_sink.dll", + "./plutosdr_source/Release/plutosdr_source.dll" + ], "offset": 0.0, "showWaterfall": true, - "source": "SoapySDR", - "sourceSettings": {}, + "source": "PlutoSDR", "streams": { "Radio": { "muted": false, "sink": "Audio", - "volume": 0.4285714328289032 + "volume": 0.29591837525367737 + }, + "Radio 1": { + "muted": false, + "sink": "Audio", + "volume": 0.4292035400867462 + }, + "Radio 2": { + "muted": false, + "sink": "Audio", + "volume": 0.4292035400867462 } }, "windowSize": { diff --git a/root_dev/module_list.json b/root_dev/module_list.json deleted file mode 100644 index 5187e16..0000000 --- a/root_dev/module_list.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Radio": "./radio/Release/radio.dll", - "Recorder": "./recorder/Release/recorder.dll", - "Soapy": "./soapy/Release/soapy.dll", - "RTLTCPSource": "./rtl_tcp_source/Release/rtl_tcp_source.dll", - "PlutoSDRSource": "./plutosdr_source/Release/plutosdr_source.dll", - "AudioSink": "./audio_sink/Release/audio_sink.dll" -} diff --git a/root_dev/soapy_source_config.json b/root_dev/soapy_source_config.json index caffcfe..9e18a59 100644 --- a/root_dev/soapy_source_config.json +++ b/root_dev/soapy_source_config.json @@ -8,9 +8,10 @@ "sampleRate": 8000000.0 }, "AirSpy HF+ [c852435de0224af7]": { + "agc": false, "gains": { - "LNA": 5.989999771118164, - "RF": 6.0 + "LNA": 6.0, + "RF": 0.0 }, "sampleRate": 768000.0 }, diff --git a/rtl_tcp_source/src/main.cpp b/rtl_tcp_source/src/main.cpp index 2bc0323..0e0b252 100644 --- a/rtl_tcp_source/src/main.cpp +++ b/rtl_tcp_source/src/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -9,14 +9,15 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) -MOD_INFO { - /* Name: */ "rtl_tcp_source", - /* Description: */ "RTL-TCP input module for SDR++", - /* Author: */ "Ryzerth", - /* Version: */ "0.1.0" +SDRPP_MOD_INFO { + /* Name: */ "rtl_tcp_source", + /* Description: */ "RTL-TCP source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 }; -class RTLTCPSourceModule { +class RTLTCPSourceModule : public ModuleManager::Instance { public: RTLTCPSourceModule(std::string name) { this->name = name; @@ -32,12 +33,22 @@ public: handler.tuneHandler = tune; handler.stream = &stream; sigpath::sourceManager.registerSource("RTL-TCP", &handler); - - spdlog::info("RTLTCPSourceModule '{0}': Instance created!", name); } ~RTLTCPSourceModule() { - spdlog::info("RTLTCPSourceModule '{0}': Instance deleted!", name); + + } + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; } private: @@ -157,6 +168,7 @@ private: } std::string name; + bool enabled = true; dsp::stream stream; double sampleRate; SourceManager::SourceHandler handler; @@ -176,14 +188,14 @@ MOD_EXPORT void _INIT_() { // Do your one time init here } -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { return new RTLTCPSourceModule(name); } -MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { delete (RTLTCPSourceModule*)instance; } -MOD_EXPORT void _STOP_() { +MOD_EXPORT void _END_() { // Do your one shutdown here } \ No newline at end of file diff --git a/soapy/CMakeLists.txt b/soapy_source/CMakeLists.txt similarity index 56% rename from soapy/CMakeLists.txt rename to soapy_source/CMakeLists.txt index 4522c8c..33281da 100644 --- a/soapy/CMakeLists.txt +++ b/soapy_source/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.13) -project(soapy) +project(soapy_source) if (MSVC) set(CMAKE_CXX_FLAGS "-O2 /std:c++17") @@ -9,9 +9,9 @@ endif (MSVC) file(GLOB SRC "src/*.cpp") -add_library(soapy SHARED ${SRC}) -target_link_libraries(soapy PRIVATE sdrpp_core) -set_target_properties(soapy PROPERTIES PREFIX "") +add_library(soapy_source SHARED ${SRC}) +target_link_libraries(soapy_source PRIVATE sdrpp_core) +set_target_properties(soapy_source PROPERTIES PREFIX "") if (MSVC) # Lib path @@ -24,7 +24,7 @@ else (MSVC) pkg_check_modules(SOAPY REQUIRED SoapySDR) - target_include_directories(soapy PUBLIC ${SOAPY_INCLUDE_DIRS}) - target_link_directories(soapy PUBLIC ${SOAPY_LIBRARY_DIRS}) - target_link_libraries(soapy PUBLIC ${SOAPY_LIBRARIES}) + target_include_directories(soapy_source PUBLIC ${SOAPY_INCLUDE_DIRS}) + target_link_directories(soapy_source PUBLIC ${SOAPY_LIBRARY_DIRS}) + target_link_libraries(soapy_source PUBLIC ${SOAPY_LIBRARIES}) endif (MSVC) \ No newline at end of file diff --git a/soapy/src/main.cpp b/soapy_source/src/main.cpp similarity index 94% rename from soapy/src/main.cpp rename to soapy_source/src/main.cpp index 3a36fa0..4ab5d84 100644 --- a/soapy/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -11,16 +11,17 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) -MOD_INFO { - /* Name: */ "soapy", - /* Description: */ "SoapySDR input module for SDR++", - /* Author: */ "Ryzerth", - /* Version: */ "0.1.0" +SDRPP_MOD_INFO { + /* Name: */ "soapy_source", + /* Description: */ "SoapySDR source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 5, + /* Max instances */ 1 }; ConfigManager config; -class SoapyModule { +class SoapyModule : public ModuleManager::Instance { public: SoapyModule(std::string name) { this->name = name; @@ -50,6 +51,23 @@ public: spdlog::info("SoapyModule '{0}': Instance created!", name); } + ~SoapyModule() { + spdlog::info("SoapyModule '{0}': Instance deleted!", name); + } + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: void refresh() { devList = SoapySDR::Device::enumerate(); txtDevList = ""; @@ -61,11 +79,6 @@ public: } } - ~SoapyModule() { - spdlog::info("SoapyModule '{0}': Instance deleted!", name); - } - -private: void selectSampleRate(double samplerate) { spdlog::info("Setting sample rate to {0}", samplerate); if (sampleRates.size() == 0) { @@ -342,6 +355,7 @@ private: } std::string name; + bool enabled = true; dsp::stream stream; SoapySDR::Stream* devStream; SourceManager::SourceHandler handler; @@ -375,15 +389,15 @@ MOD_EXPORT void _INIT_() { config.enableAutoSave(); } -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { return new SoapyModule(name); } -MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { delete (SoapyModule*)instance; } -MOD_EXPORT void _STOP_() { +MOD_EXPORT void _END_() { config.disableAutoSave(); config.save(); } \ No newline at end of file