mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-06-26 04:17:50 +02:00
Fixed a tone of stuff + new features
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user