mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 10:49:11 +01:00 
			
		
		
		
	Fixed a tone of stuff + new features
This commit is contained in:
		@@ -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")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <signal_path/sink.h>
 | 
			
		||||
@@ -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_() {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
#include <version.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <gui/widgets/bandplan.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <stb_image.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
@@ -30,6 +29,7 @@
 | 
			
		||||
namespace core {
 | 
			
		||||
    ConfigManager configManager;
 | 
			
		||||
    ScriptManager scriptManager;
 | 
			
		||||
    ModuleManager moduleManager;
 | 
			
		||||
 | 
			
		||||
    void setInputSampleRate(double samplerate) {
 | 
			
		||||
        // NOTE: Zoom controls won't work
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <scripting.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
 | 
			
		||||
namespace core {
 | 
			
		||||
    SDRPP_EXPORT ConfigManager configManager;
 | 
			
		||||
    SDRPP_EXPORT ScriptManager scriptManager;
 | 
			
		||||
    SDRPP_EXPORT ModuleManager moduleManager;
 | 
			
		||||
 | 
			
		||||
    void setInputSampleRate(double samplerate);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,10 @@ namespace dsp {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual void init() {}
 | 
			
		||||
 | 
			
		||||
        virtual ~generic_block() {
 | 
			
		||||
            stop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtual void start() {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(ctrlMtx);
 | 
			
		||||
            if (running) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
#include <gui/widgets/frequency_select.h>
 | 
			
		||||
#include <gui/widgets/menu.h>
 | 
			
		||||
#include <gui/dialogs/loading_screen.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
 | 
			
		||||
namespace gui {
 | 
			
		||||
    SDRPP_EXPORT ImGui::WaterFall waterfall;
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
#include <gui/icons.h>
 | 
			
		||||
#include <gui/widgets/bandplan.h>
 | 
			
		||||
#include <watcher.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <signal_path/vfo_manager.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
@@ -28,6 +27,7 @@
 | 
			
		||||
#include <gui/menus/sink.h>
 | 
			
		||||
#include <gui/menus/scripting.h>
 | 
			
		||||
#include <gui/dialogs/credits.h>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <signal_path/source.h>
 | 
			
		||||
#include <gui/dialogs/loading_screen.h>
 | 
			
		||||
 | 
			
		||||
@@ -81,6 +81,7 @@ bool grabbingMenu = false;
 | 
			
		||||
int newWidth = 300;
 | 
			
		||||
int fftHeight = 300;
 | 
			
		||||
bool showMenu = true;
 | 
			
		||||
bool centerTuning = false;
 | 
			
		||||
dsp::stream<dsp::complex_t> 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<std::string> modules = core::configManager.conf["modules"];
 | 
			
		||||
    std::map<std::string, std::string> 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();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
#include <gui/widgets/menu.h>
 | 
			
		||||
#include <imgui/imgui.h>
 | 
			
		||||
#include <imgui/imgui_internal.h>
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -104,7 +104,7 @@ namespace ImGui {
 | 
			
		||||
        bandplan::BandPlan_t* bandplan = NULL;
 | 
			
		||||
 | 
			
		||||
        std::map<std::string, WaterfallVFO*> vfos;
 | 
			
		||||
        std::string selectedVFO;
 | 
			
		||||
        std::string selectedVFO = "";
 | 
			
		||||
        bool selectedVFOChanged = false;
 | 
			
		||||
 | 
			
		||||
        enum {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,112 +0,0 @@
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <json.hpp>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <gui/dialogs/loading_screen.h>
 | 
			
		||||
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
 | 
			
		||||
namespace mod {
 | 
			
		||||
    std::map<std::string, Module_t> modules;
 | 
			
		||||
    std::vector<std::string> 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<std::string, std::string> list = data.get<std::map<std::string, std::string>>();
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <json.hpp>
 | 
			
		||||
 | 
			
		||||
#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 <Windows.h>
 | 
			
		||||
#define MOD_EXPORT extern "C" __declspec(dllexport)
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#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<std::string, Module_t> modules;
 | 
			
		||||
    extern std::vector<std::string> moduleNames;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MOD_INFO    MOD_EXPORT const mod::ModuleInfo_t _INFO
 | 
			
		||||
							
								
								
									
										145
									
								
								core/src/new_module.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								core/src/new_module.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										91
									
								
								core/src/new_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								core/src/new_module.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <json.hpp>
 | 
			
		||||
 | 
			
		||||
#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 <Windows.h>
 | 
			
		||||
#define MOD_EXPORT extern "C" __declspec(dllexport)
 | 
			
		||||
#define SDRPP_MOD_EXTENTSION    ".dll"
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#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<std::string, ModuleManager::Module_t> modules;
 | 
			
		||||
    std::map<std::string, ModuleManager::Instance_t> instances;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SDRPP_MOD_INFO    MOD_EXPORT const ModuleManager::ModuleInfo_t _INFO_
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
#include <dsp/vfo.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <dsp/sink.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
 | 
			
		||||
class SignalPath {
 | 
			
		||||
public:
 | 
			
		||||
    SignalPath();
 | 
			
		||||
    void init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* 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();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
#include <signal_path/vfo_manager.h>
 | 
			
		||||
#include <signal_path/source.h>
 | 
			
		||||
#include <signal_path/sink.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
 | 
			
		||||
namespace sigpath {
 | 
			
		||||
    SDRPP_EXPORT SignalPath signalPath;
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
@@ -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<dsp::complex_t> 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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <watcher.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <radio_demod.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <wfm_demod.h>
 | 
			
		||||
#include <fm_demod.h>
 | 
			
		||||
#include <am_demod.h>
 | 
			
		||||
@@ -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<dsp::complex_t> ns;
 | 
			
		||||
 | 
			
		||||
    Event<float>::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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <watcher.h>
 | 
			
		||||
#include <wav.h>
 | 
			
		||||
#include <dsp/types.h>
 | 
			
		||||
@@ -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<dsp::stereo_t>* 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) {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -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": {
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#include <rtltcp_client.h>
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
@@ -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<dsp::complex_t> 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
 | 
			
		||||
}
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <new_module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <SoapySDR/Device.hpp>
 | 
			
		||||
@@ -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<dsp::complex_t> 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();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user