From 1507e6ec31cde52d770f15d8dc3cfb459caeff95 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 1 Oct 2020 13:46:12 +0200 Subject: [PATCH] New module system --- CMakeLists.txt | 5 +- core/src/gui/main_window.cpp | 17 -- core/src/module.cpp | 82 ++++----- core/src/module.h | 15 +- core/src/signal_path/vfo_manager.cpp | 2 +- file_source/CMakeLists.txt | 14 ++ file_source/src/main.cpp | 90 ++++++++++ radio/src/main.cpp | 1 + recorder/src/main.cpp | 241 ++++++++++++++------------- root_dev/config.json | 102 ++++++------ root_dev/module_list.json | 8 +- soapy/CMakeLists.txt | 14 ++ soapy/src/main.cpp | 92 ++++++++++ source_demo/CMakeLists.txt | 14 ++ source_demo/src/main.cpp | 87 ++++++++++ 15 files changed, 534 insertions(+), 250 deletions(-) create mode 100644 file_source/CMakeLists.txt create mode 100644 file_source/src/main.cpp create mode 100644 soapy/CMakeLists.txt create mode 100644 soapy/src/main.cpp create mode 100644 source_demo/CMakeLists.txt create mode 100644 source_demo/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index acc0882f..c4ac7f96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,9 @@ project(sdrpp_core) add_subdirectory("core") add_subdirectory("radio") -# Add back recorder when module new system is ready -#add_subdirectory("recorder") +add_subdirectory("recorder") +add_subdirectory("soapy") +add_subdirectory("file_source") add_subdirectory("demo") add_executable(sdrpp "src/main.cpp" "win32/resources.rc") diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index cb9b0b1b..16aebbb4 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -72,7 +72,6 @@ void windowInit() { sigpath::signalPath.start(); spdlog::info("Loading modules"); - mod::initAPI(&gui::waterfall); mod::loadFromList(ROOT_DIR "/module_list.json"); sourecmenu::init(); @@ -231,7 +230,6 @@ void drawWindow() { gui::waterfall.selectedVFOChanged = false; gui::freqSelect.setFrequency(vfo->generalOffset + gui::waterfall.getCenterFrequency()); gui::freqSelect.frequencyChanged = false; - mod::broadcastEvent(mod::EVENT_SELECTED_VFO_CHANGED); audioStreamName = audio::getNameFromVFO(gui::waterfall.selectedVFO); if (audioStreamName != "") { volume = &audio::streams[audioStreamName]->volume; @@ -279,13 +277,6 @@ void drawWindow() { int width = vMax.x - vMin.x; int height = vMax.y - vMin.y; - int modCount = mod::moduleNames.size(); - mod::Module_t mod; - for (int i = 0; i < modCount; i++) { - mod = mod::modules[mod::moduleNames[i]]; - mod._NEW_FRAME_(mod.ctx); - } - // To Bar if (ImGui::ImageButton(icons::MENU, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) { showMenu = !showMenu; @@ -384,14 +375,6 @@ void drawWindow() { gui::menu.draw(); - for (int i = 0; i < modCount; i++) { - if (ImGui::CollapsingHeader(mod::moduleNames[i].c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - mod = mod::modules[mod::moduleNames[i]]; - mod._DRAW_MENU_(mod.ctx); - ImGui::Spacing(); - } - } - if(ImGui::CollapsingHeader("Debug")) { ImGui::Text("Frame time: %.3f ms/frame", 1000.0f / ImGui::GetIO().Framerate); ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate); diff --git a/core/src/module.cpp b/core/src/module.cpp index 69f6c48a..3251ad2a 100644 --- a/core/src/module.cpp +++ b/core/src/module.cpp @@ -4,37 +4,10 @@ #include namespace mod { - API_t API; std::map modules; std::vector moduleNames; ImGui::WaterFall* _wtf; - std::string api_getSelectedVFOName() { - return _wtf->selectedVFO; - } - - void initAPI(ImGui::WaterFall* wtf) { - _wtf = wtf; - - // GUI - API.getSelectedVFOName = api_getSelectedVFOName; - API.bindVolumeVariable = bindVolumeVariable; - API.unbindVolumeVariable = unbindVolumeVariable; - - // Audio - API.registerMonoStream = audio::registerMonoStream; - API.registerStereoStream = audio::registerStereoStream; - API.startStream = audio::startStream; - API.stopStream = audio::stopStream; - API.removeStream = audio::removeStream; - API.bindToStreamMono = audio::bindToStreamMono; - API.bindToStreamStereo = audio::bindToStreamStereo; - API.setBlockSize = audio::setBlockSize; - API.unbindFromStreamMono = audio::unbindFromStreamMono; - API.unbindFromStreamStereo = audio::unbindFromStreamStereo; - API.getStreamNameList = audio::getStreamNameList; - } - void loadModule(std::string path, std::string name) { if (!std::filesystem::exists(path)) { spdlog::error("{0} does not exist", path); @@ -51,10 +24,10 @@ namespace mod { spdlog::error("Couldn't load {0}.", name); return; } - mod._INIT_ = (void*(*)(mod::API_t*,ImGuiContext*,std::string))GetProcAddress(mod.inst, "_INIT_"); - mod._NEW_FRAME_ = (void(*)(void*))GetProcAddress(mod.inst, "_NEW_FRAME_"); - mod._DRAW_MENU_ = (void(*)(void*))GetProcAddress(mod.inst, "_DRAW_MENU_"); - mod._HANDLE_EVENT_ = (void(*)(void*, int))GetProcAddress(mod.inst, "_HANDLE_EVENT_"); + + 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(*)(void*))GetProcAddress(mod.inst, "_STOP_"); #else mod.inst = dlopen(path.c_str(), RTLD_LAZY); @@ -62,49 +35,41 @@ namespace mod { spdlog::error("Couldn't load {0}.", name); return; } - mod._INIT_ = (void*(*)(mod::API_t*,ImGuiContext*,std::string))dlsym(mod.inst, "_INIT_"); - mod._NEW_FRAME_ = (void(*)(void*))dlsym(mod.inst, "_NEW_FRAME_"); - mod._DRAW_MENU_ = (void(*)(void*))dlsym(mod.inst, "_DRAW_MENU_"); - mod._HANDLE_EVENT_ = (void(*)(void*, int))dlsym(mod.inst, "_HANDLE_EVENT_"); - mod._STOP_ = (void(*)(void*))dlsym(mod.inst, "_STOP_"); + 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._NEW_FRAME_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _NEW_FRAME_.", name); + if (mod._CREATE_INSTANCE_ == NULL) { + spdlog::error("Couldn't load {0} because it's missing _CREATE_INSTANCE_.", name); return; } - if (mod._DRAW_MENU_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _DRAW_MENU_.", name); - return; - } - if (mod._HANDLE_EVENT_ == NULL) { - spdlog::error("Couldn't load {0} because it's missing _HANDLE_EVENT_.", name); + 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; } - mod.ctx = mod._INIT_(&API, ImGui::GetCurrentContext(), name); + + 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 broadcastEvent(int eventId) { - if (eventId < 0 || eventId >= _EVENT_COUNT) { - return; - } - for (auto const& [name, mod] : modules) { - mod._HANDLE_EVENT_(mod.ctx, eventId); - } - } - void loadFromList(std::string path) { if (!std::filesystem::exists(path)) { spdlog::error("Module list file does not exist"); @@ -125,5 +90,14 @@ namespace mod { loadModule(file, name); } } + + bool isLoaded(void* handle) { + for (auto const& [name, module] : modules) { + if (module.inst == handle) { + return true; + } + } + return false; + } }; diff --git a/core/src/module.h b/core/src/module.h index 159de6a7..d366ad49 100644 --- a/core/src/module.h +++ b/core/src/module.h @@ -37,23 +37,22 @@ namespace mod { void* inst; #endif void (*_INIT_)(); - void* (*_CREATE_INSTANCE)(std::string); - void (*_DELETE_INSTANCE)(); + void* (*_CREATE_INSTANCE_)(std::string name); + void (*_DELETE_INSTANCE_)(void* instance); void (*_STOP_)(); void* ctx; }; struct ModuleInfo_t { - char* name; - char* description; - char* author; - char* version; + const char* name; + const char* description; + const char* author; + const char* version; }; - void initAPI(ImGui::WaterFall* wtf); void loadModule(std::string path, std::string name); - void broadcastEvent(int eventId); void loadFromList(std::string path); + bool isLoaded(void* handle); extern std::map modules; extern std::vector moduleNames; diff --git a/core/src/signal_path/vfo_manager.cpp b/core/src/signal_path/vfo_manager.cpp index f43248f1..0396114e 100644 --- a/core/src/signal_path/vfo_manager.cpp +++ b/core/src/signal_path/vfo_manager.cpp @@ -48,7 +48,7 @@ int VFOManager::VFO::getOutputBlockSize() { VFOManager::VFOManager() { - + } VFOManager::VFO* VFOManager::createVFO(std::string name, int reference, float offset, float bandwidth, float sampleRate, int blockSize) { diff --git a/file_source/CMakeLists.txt b/file_source/CMakeLists.txt new file mode 100644 index 00000000..adc08b05 --- /dev/null +++ b/file_source/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.13) +project(file_source) + +if (MSVC) + set(CMAKE_CXX_FLAGS "-O2 /std:c++17") +else() + set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") +endif (MSVC) + +file(GLOB SRC "src/*.cpp") + +add_library(file_source SHARED ${SRC}) +target_link_libraries(file_source PRIVATE sdrpp_core) +set_target_properties(file_source PROPERTIES PREFIX "") \ No newline at end of file diff --git a/file_source/src/main.cpp b/file_source/src/main.cpp new file mode 100644 index 00000000..66e10369 --- /dev/null +++ b/file_source/src/main.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +MOD_INFO { + /* Name: */ "fike_source", + /* Description: */ "File input module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ "0.1.0" +}; + +class FileSourceModule { +public: + FileSourceModule(std::string name) { + this->name = name; + + stream.init(100); + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + handler.stream = &stream; + sigpath::sourceManager.registerSource("File", &handler); + spdlog::info("FileSourceModule '{0}': Instance created!", name); + } + + ~FileSourceModule() { + spdlog::info("FileSourceModule '{0}': Instance deleted!", name); + } + +private: + static void menuSelected(void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + spdlog::info("FileSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + spdlog::info("FileSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + spdlog::info("FileSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + spdlog::info("FileSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + spdlog::info("FileSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + + static void menuHandler(void* ctx) { + FileSourceModule* _this = (FileSourceModule*)ctx; + ImGui::Text("Hi from %s!", _this->name.c_str()); + } + + std::string name; + dsp::stream stream; + SourceManager::SourceHandler handler; +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here +} + +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + return new FileSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (FileSourceModule*)instance; +} + +MOD_EXPORT void _STOP_() { + // Do your one shutdown here +} \ No newline at end of file diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 54e0a451..ebaba395 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -25,6 +25,7 @@ public: sigPath.init(name, 200000, 1000); sigPath.start(); sigPath.setDemodulator(SigPath::DEMOD_FM, bandWidth); + gui::menu.registerEntry(name, menuHandler, this); } ~RadioModule() { diff --git a/recorder/src/main.cpp b/recorder/src/main.cpp index 4dda16cf..60c72efc 100644 --- a/recorder/src/main.cpp +++ b/recorder/src/main.cpp @@ -7,40 +7,11 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) -struct RecorderContext_t { - std::string name; - dsp::stream* stream; - WavWriter* writer; - std::thread workerThread; - bool recording; - time_t startTime; - std::string lastNameList; - std::string selectedStreamName; - int selectedStreamId; - uint64_t samplesWritten; - float sampleRate; -}; - -void _writeWorker(RecorderContext_t* ctx) { - dsp::StereoFloat_t* floatBuf = new dsp::StereoFloat_t[1024]; - int16_t* sampleBuf = new int16_t[2048]; - while (true) { - if (ctx->stream->read(floatBuf, 1024) < 0) { - break; - } - for (int i = 0; i < 1024; i++) { - sampleBuf[(i * 2) + 0] = floatBuf[i].l * 0x7FFF; - sampleBuf[(i * 2) + 1] = floatBuf[i].r * 0x7FFF; - } - ctx->samplesWritten += 1024; - ctx->writer->writeSamples(sampleBuf, 2048 * sizeof(int16_t)); - } - delete[] floatBuf; - delete[] sampleBuf; -} +// TODO: Fix this and finish module std::string genFileName(std::string prefix) { time_t now = time(0); @@ -58,102 +29,144 @@ void sampleRateChanged(void* ctx, float sampleRate, int blockSize) { } -MOD_EXPORT void* _INIT_(mod::API_t* _API, ImGuiContext* imctx, std::string _name) { - RecorderContext_t* ctx = new RecorderContext_t; - ctx->recording = false; - ctx->selectedStreamName = ""; - ctx->selectedStreamId = 0; - ctx->lastNameList = ""; - ImGui::SetCurrentContext(imctx); - return ctx; -} - -MOD_EXPORT void _NEW_FRAME_(RecorderContext_t* ctx) { - -} - -MOD_EXPORT void _DRAW_MENU_(RecorderContext_t* ctx) { - float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); - - std::vector streamNames = audio::getStreamNameList(); - std::string nameList; - for (std::string name : streamNames) { - nameList += name; - nameList += '\0'; +class RecorderModule { +public: + RecorderModule(std::string name) { + this->name = name; + recording = false; + selectedStreamName = ""; + selectedStreamId = 0; + lastNameList = ""; + gui::menu.registerEntry(name, menuHandler, this); } - if (nameList == "") { - ImGui::Text("No audio stream available"); - return; + ~RecorderModule() { + } - if (ctx->lastNameList != nameList) { - ctx->lastNameList = nameList; - auto _nameIt = std::find(streamNames.begin(), streamNames.end(), ctx->selectedStreamName); - if (_nameIt == streamNames.end()) { - // TODO: verify if there even is a stream - ctx->selectedStreamId = 0; - ctx->selectedStreamName = streamNames[ctx->selectedStreamId]; +private: + static void menuHandler(void* ctx) { + RecorderModule* _this = (RecorderModule*)ctx; + float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); + + std::vector streamNames = audio::getStreamNameList(); + std::string nameList; + for (std::string name : streamNames) { + nameList += name; + nameList += '\0'; + } + + if (nameList == "") { + ImGui::Text("No audio stream available"); + return; + } + + if (_this->lastNameList != nameList) { + _this->lastNameList = nameList; + auto _nameIt = std::find(streamNames.begin(), streamNames.end(), _this->selectedStreamName); + if (_nameIt == streamNames.end()) { + // TODO: verify if there even is a stream + _this->selectedStreamId = 0; + _this->selectedStreamName = streamNames[_this->selectedStreamId]; + } + else { + _this->selectedStreamId = std::distance(streamNames.begin(), _nameIt); + _this->selectedStreamName = streamNames[_this->selectedStreamId]; + } + } + + ImGui::PushItemWidth(menuColumnWidth); + if (!_this->recording) { + if (ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str())) { + _this->selectedStreamName = streamNames[_this->selectedStreamId]; + } } else { - ctx->selectedStreamId = std::distance(streamNames.begin(), _nameIt); - ctx->selectedStreamName = streamNames[ctx->selectedStreamId]; + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f)); + ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str()); + ImGui::PopItemFlag(); + ImGui::PopStyleColor(3); + } + + if (!_this->recording) { + if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { + _this->samplesWritten = 0; + _this->sampleRate = 48000; + _this->writer = new WavWriter("recordings/" + genFileName("audio_"), 16, 2, 48000); + _this->stream = audio::bindToStreamStereo(_this->selectedStreamName, streamRemovedHandler, sampleRateChanged, _this); + _this->workerThread = std::thread(_writeWorker, _this); + _this->recording = true; + _this->startTime = time(0); + } + ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); + } + else { + if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { + _this->stream->stopReader(); + _this->workerThread.join(); + _this->stream->clearReadStop(); + audio::unbindFromStreamStereo(_this->selectedStreamName, _this->stream); + _this->writer->close(); + delete _this->writer; + _this->recording = false; + } + uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; + time_t diff = seconds; + tm *dtm = gmtime(&diff); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); } } - ImGui::PushItemWidth(menuColumnWidth); - if (!ctx->recording) { - if (ImGui::Combo(CONCAT("##_strea_select_", ctx->name), &ctx->selectedStreamId, nameList.c_str())) { - ctx->selectedStreamName = streamNames[ctx->selectedStreamId]; + static void _writeWorker(RecorderModule* _this) { + dsp::StereoFloat_t* floatBuf = new dsp::StereoFloat_t[1024]; + int16_t* sampleBuf = new int16_t[2048]; + while (true) { + if (_this->stream->read(floatBuf, 1024) < 0) { + break; + } + for (int i = 0; i < 1024; i++) { + sampleBuf[(i * 2) + 0] = floatBuf[i].l * 0x7FFF; + sampleBuf[(i * 2) + 1] = floatBuf[i].r * 0x7FFF; + } + _this->samplesWritten += 1024; + _this->writer->writeSamples(sampleBuf, 2048 * sizeof(int16_t)); } + delete[] floatBuf; + delete[] sampleBuf; } - else { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f)); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f)); - ImGui::Combo(CONCAT("##_strea_select_", ctx->name), &ctx->selectedStreamId, nameList.c_str()); - ImGui::PopItemFlag(); - ImGui::PopStyleColor(3); - } - - if (!ctx->recording) { - if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { - ctx->samplesWritten = 0; - ctx->sampleRate = 48000; - ctx->writer = new WavWriter("recordings/" + genFileName("audio_"), 16, 2, 48000); - ctx->stream = audio::bindToStreamStereo(ctx->selectedStreamName, streamRemovedHandler, sampleRateChanged, ctx); - ctx->workerThread = std::thread(_writeWorker, ctx); - ctx->recording = true; - ctx->startTime = time(0); - } - ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); - } - else { - if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { - ctx->stream->stopReader(); - ctx->workerThread.join(); - ctx->stream->clearReadStop(); - audio::unbindFromStreamStereo(ctx->selectedStreamName, ctx->stream); - ctx->writer->close(); - delete ctx->writer; - ctx->recording = false; - } - uint64_t seconds = ctx->samplesWritten / (uint64_t)ctx->sampleRate; - time_t diff = seconds; - tm *dtm = gmtime(&diff); - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); - } + + std::string name; + dsp::stream* stream; + WavWriter* writer; + std::thread workerThread; + bool recording; + time_t startTime; + std::string lastNameList; + std::string selectedStreamName; + int selectedStreamId; + uint64_t samplesWritten; + float sampleRate; + +}; + +struct RecorderContext_t { + std::string name; +}; + +MOD_EXPORT void _INIT_() { + // Nothing here } -MOD_EXPORT void _HANDLE_EVENT_(RecorderContext_t* ctx, int eventId) { - // INSTEAD OF EVENTS, REGISTER HANDLER WHEN CREATING VFO - if (eventId == mod::EVENT_STREAM_PARAM_CHANGED) { - - } - else if (eventId == mod::EVENT_SELECTED_VFO_CHANGED) { - - } +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + RecorderModule* instance = new RecorderModule(name); + return instance; +} + +MOD_EXPORT void _DELETE_INSTANCE_() { + } MOD_EXPORT void _STOP_(RecorderContext_t* ctx) { diff --git a/root_dev/config.json b/root_dev/config.json index 3d8e90f8..2f180be9 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -1,52 +1,52 @@ -{ - "audio": { - "Radio": { - "device": "Speakers (Realtek High Definiti", - "sampleRate": 48000.0, - "volume": 0.602150559425354 - }, - "Radio 1": { - "device": "Speakers (Realtek High Definiti", - "sampleRate": 48000.0, - "volume": 0.4185185134410858 - }, - "Radio 2": { - "device": "Speakers (Realtek High Definiti", - "sampleRate": 48000.0, - "volume": 0.0 - } - }, - "bandPlan": "General", - "bandPlanEnabled": true, - "fftHeight": 300, - "frequency": 99000000, - "max": 0.0, - "maximized": false, - "menuWidth": 300, - "min": -51.47058868408203, - "showWaterfall": true, - "source": "Generic RTL2832U OEM :: 00000001", - "sourceSettings": { - "Generic RTL2832U OEM :: 00000001": { - "gains": { - "TUNER": 11.625 - }, - "sampleRate": 2560000 - }, - "HackRF One #0 901868dc282c8f8b": { - "gains": { - "AMP": 0.0, - "LNA": 24.503000259399414, - "VGA": 16.229999542236328 - }, - "sampleRate": 8000000 - }, - "PulseAudio": { - "sampleRate": 96000 - } - }, - "windowSize": { - "h": 784, - "w": 1460 - } +{ + "audio": { + "Radio": { + "device": "Speakers (Realtek High Definiti", + "sampleRate": 48000.0, + "volume": 0.602150559425354 + }, + "Radio 1": { + "device": "Speakers (Realtek High Definiti", + "sampleRate": 48000.0, + "volume": 0.4185185134410858 + }, + "Radio 2": { + "device": "Speakers (Realtek High Definiti", + "sampleRate": 48000.0, + "volume": 0.0 + } + }, + "bandPlan": "General", + "bandPlanEnabled": true, + "fftHeight": 300, + "frequency": 96570096, + "max": 0.0, + "maximized": false, + "menuWidth": 300, + "min": -51.47058868408203, + "showWaterfall": true, + "source": "Generic RTL2832U OEM :: 00000001", + "sourceSettings": { + "Generic RTL2832U OEM :: 00000001": { + "gains": { + "TUNER": 11.625 + }, + "sampleRate": 2560000 + }, + "HackRF One #0 901868dc282c8f8b": { + "gains": { + "AMP": 0.0, + "LNA": 24.503000259399414, + "VGA": 16.229999542236328 + }, + "sampleRate": 8000000 + }, + "PulseAudio": { + "sampleRate": 96000 + } + }, + "windowSize": { + "h": 1053, + "w": 959 + } } \ No newline at end of file diff --git a/root_dev/module_list.json b/root_dev/module_list.json index ac435873..fff6c734 100644 --- a/root_dev/module_list.json +++ b/root_dev/module_list.json @@ -1,4 +1,6 @@ { - "Radio 1": "./radio/Release/radio.dll", - "Recorder 1": "./recorder/Release/recorder.dll" -} + "Radio": "./radio/radio.so", + "Recorder": "./recorder/recorder.so", + "Soapy": "./soapy/soapy.so", + "FileSource": "./file_source/file_source.so" +} \ No newline at end of file diff --git a/soapy/CMakeLists.txt b/soapy/CMakeLists.txt new file mode 100644 index 00000000..7e4fcbfa --- /dev/null +++ b/soapy/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.13) +project(soapy) + +if (MSVC) + set(CMAKE_CXX_FLAGS "-O2 /std:c++17") +else() + set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") +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 "") \ No newline at end of file diff --git a/soapy/src/main.cpp b/soapy/src/main.cpp new file mode 100644 index 00000000..6a19bab9 --- /dev/null +++ b/soapy/src/main.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +#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" +}; + +class SoapyModule { +public: + SoapyModule(std::string name) { + this->name = name; + + //TODO: Make module tune on source select change (in sdrpp_core) + + stream.init(100); + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + handler.stream = &stream; + sigpath::sourceManager.registerSource("SoapySDR", &handler); + spdlog::info("SoapyModule '{0}': Instance created!", name); + } + + ~SoapyModule() { + spdlog::info("SoapyModule '{0}': Instance deleted!", name); + } + +private: + static void menuSelected(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Tune: {1}!", _this->name, freq); + } + + + static void menuHandler(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + ImGui::Text("Hi from %s!", _this->name.c_str()); + } + + std::string name; + dsp::stream stream; + SourceManager::SourceHandler handler; +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here +} + +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + return new SoapyModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (SoapyModule*)instance; +} + +MOD_EXPORT void _STOP_() { + // Do your one shutdown here +} \ No newline at end of file diff --git a/source_demo/CMakeLists.txt b/source_demo/CMakeLists.txt new file mode 100644 index 00000000..7e4fcbfa --- /dev/null +++ b/source_demo/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.13) +project(soapy) + +if (MSVC) + set(CMAKE_CXX_FLAGS "-O2 /std:c++17") +else() + set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") +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 "") \ No newline at end of file diff --git a/source_demo/src/main.cpp b/source_demo/src/main.cpp new file mode 100644 index 00000000..8617ced5 --- /dev/null +++ b/source_demo/src/main.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#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" +}; + +class SoapyModule { +public: + SoapyModule(std::string name) { + this->name = name; + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + sigpath::sourceManager.registerSource("SoapySDR", &handler); + spdlog::info("SoapyModule '{0}': Instance created!", name); + } + + ~SoapyModule() { + spdlog::info("SoapyModule '{0}': Instance deleted!", name); + } + +private: + static void menuSelected(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + spdlog::info("SoapyModule '{0}': Tune: {1}!", _this->name, freq); + } + + + static void menuHandler(void* ctx) { + SoapyModule* _this = (SoapyModule*)ctx; + ImGui::Text("Hi from %s!", _this->name.c_str()); + } + + std::string name; + dsp::stream stream; + SourceManager::SourceHandler handler; +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here +} + +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + return new SoapyModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (SoapyModule*)instance; +} + +MOD_EXPORT void _STOP_() { + // Do your one shutdown here +} \ No newline at end of file