From 31a95031e4fb749fd1cf57cfca56e244f38193f3 Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Wed, 12 Aug 2020 16:43:44 +0200 Subject: [PATCH] Full module system --- CMakeLists.txt | 2 +- module_list.json | 3 +++ modules/radio/src/main.cpp | 39 ++++++++++++++++++++++++++++----- modules/radio/src/path.cpp | 5 +++++ modules/radio/src/path.h | 5 ++++- readme.md | 3 ++- src/audio.cpp | 1 + src/audio.h | 6 ++++++ src/io/audio.h | 44 +++++++++++++++++++++++++++++++++++--- src/main.cpp | 8 ------- src/main_window.cpp | 27 +++++++++++++++++++---- src/main_window.h | 4 +++- src/module.cpp | 43 ++++++++++++++++++++++++++++++++++--- src/module.h | 10 ++++++++- src/waterfall.cpp | 11 ++-------- 15 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 module_list.json create mode 100644 src/audio.cpp create mode 100644 src/audio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6feea033..ff30aecc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ else() link_libraries(portaudio) link_libraries(X11) link_libraries(Xxf86vm) - link_libraries(DL) + link_libraries(dl) endif (MSVC) link_libraries(volk) diff --git a/module_list.json b/module_list.json new file mode 100644 index 00000000..96a6dd79 --- /dev/null +++ b/module_list.json @@ -0,0 +1,3 @@ +{ + "Radio": "../modules/radio/build/Release/radio.dll" +} \ No newline at end of file diff --git a/modules/radio/src/main.cpp b/modules/radio/src/main.cpp index 34a97367..847938cf 100644 --- a/modules/radio/src/main.cpp +++ b/modules/radio/src/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -10,6 +11,8 @@ struct RadioContext_t { std::string name; int demod = 1; SigPath sigPath; + watcher volume; + watcher audioDevice; }; MOD_EXPORT void* _INIT_(mod::API_t* _API, ImGuiContext* imctx, std::string _name) { @@ -18,10 +21,26 @@ MOD_EXPORT void* _INIT_(mod::API_t* _API, ImGuiContext* imctx, std::string _name ctx->name = _name; ctx->sigPath.init(_name, 200000, 1000, API->registerVFO(_name, mod::API_t::REF_CENTER, 0, 200000, 200000, 1000)); ctx->sigPath.start(); + ctx->volume.val = 1.0f; + ctx->volume.markAsChanged(); + API->bindVolumeVariable(&ctx->volume.val); + ctx->audioDevice.val = ctx->sigPath.audio.getDeviceId(); + ctx->audioDevice.changed(); // clear change ImGui::SetCurrentContext(imctx); return ctx; } +MOD_EXPORT void _NEW_FRAME_(RadioContext_t* ctx) { + if (ctx->volume.changed()) { + ctx->sigPath.setVolume(ctx->volume.val); + } + if (ctx->audioDevice.changed()) { + ctx->sigPath.audio.stop(); + ctx->sigPath.audio.setDevice(ctx->audioDevice.val); + ctx->sigPath.audio.start(); + } +} + MOD_EXPORT void _DRAW_MENU_(RadioContext_t* ctx) { ImGui::BeginGroup(); @@ -30,20 +49,20 @@ MOD_EXPORT void _DRAW_MENU_(RadioContext_t* ctx) { ctx->sigPath.setDemodulator(SigPath::DEMOD_NFM); ctx->demod = 0; API->setVFOBandwidth(ctx->name, 12500); - // vfo->setReference(ImGui::WaterFall::REF_CENTER); + API->setVFOReference(ctx->name, mod::API_t::REF_CENTER); } if (ImGui::RadioButton(CONCAT("WFM##_", ctx->name), ctx->demod == 1) && ctx->demod != 1) { ctx->sigPath.setDemodulator(SigPath::DEMOD_FM); ctx->demod = 1; API->setVFOBandwidth(ctx->name, 200000); - // vfo->setReference(ImGui::WaterFall::REF_CENTER); + API->setVFOReference(ctx->name, mod::API_t::REF_CENTER); } ImGui::NextColumn(); if (ImGui::RadioButton(CONCAT("AM##_", ctx->name), ctx->demod == 2) && ctx->demod != 2) { ctx->sigPath.setDemodulator(SigPath::DEMOD_AM); ctx->demod = 2; API->setVFOBandwidth(ctx->name, 12500); - // vfo->setReference(ImGui::WaterFall::REF_CENTER); + API->setVFOReference(ctx->name, mod::API_t::REF_CENTER); } if (ImGui::RadioButton(CONCAT("DSB##_", ctx->name), ctx->demod == 3) && ctx->demod != 3) { ctx->demod = 3; }; ImGui::NextColumn(); @@ -51,7 +70,7 @@ MOD_EXPORT void _DRAW_MENU_(RadioContext_t* ctx) { ctx->sigPath.setDemodulator(SigPath::DEMOD_USB); ctx->demod = 4; API->setVFOBandwidth(ctx->name, 3000); - // vfo->setReference(ImGui::WaterFall::REF_LOWER); + API->setVFOReference(ctx->name, mod::API_t::REF_LOWER); } if (ImGui::RadioButton(CONCAT("CW##_", ctx->name), ctx->demod == 5) && ctx->demod != 5) { ctx->demod = 5; }; ImGui::NextColumn(); @@ -59,18 +78,28 @@ MOD_EXPORT void _DRAW_MENU_(RadioContext_t* ctx) { ctx->sigPath.setDemodulator(SigPath::DEMOD_LSB); ctx->demod = 6; API->setVFOBandwidth(ctx->name, 3000); - // vfo->setReference(ImGui::WaterFall::REF_UPPER); + API->setVFOReference(ctx->name, mod::API_t::REF_UPPER); } if (ImGui::RadioButton(CONCAT("RAW##_", ctx->name), ctx->demod == 7) && ctx->demod != 7) { ctx->demod = 7; }; ImGui::Columns(1, CONCAT("EndRadioModeColumns##_", ctx->name), false); ImGui::EndGroup(); + + ImGui::PushItemWidth(ImGui::GetWindowSize().x); + ImGui::Combo(CONCAT("##_audio_dev_", ctx->name), &ctx->audioDevice.val, ctx->sigPath.audio.devTxtList.c_str()); + ImGui::PopItemWidth(); } MOD_EXPORT void _HANDLE_EVENT_(RadioContext_t* ctx, int eventId) { + // INSTEAD OF EVENTS, REGISTER HANDLER WHEN CREATING VFO if (eventId == mod::EVENT_STREAM_PARAM_CHANGED) { ctx->sigPath.updateBlockSize(); } + else if (eventId == mod::EVENT_SELECTED_VFO_CHANGED) { + if (API->getSelectedVFOName() == ctx->name) { + API->bindVolumeVariable(&ctx->volume.val); + } + } } MOD_EXPORT void _STOP_(RadioContext_t* ctx) { diff --git a/modules/radio/src/path.cpp b/modules/radio/src/path.cpp index afa3dee7..5fffa4ee 100644 --- a/modules/radio/src/path.cpp +++ b/modules/radio/src/path.cpp @@ -111,4 +111,9 @@ void SigPath::start() { demod.start(); audioResamp.start(); audio.start(); +} + +void SigPath::DEBUG_TEST() { + audio.stop(); + audio.start(); } \ No newline at end of file diff --git a/modules/radio/src/path.h b/modules/radio/src/path.h index e85088cd..0d83762a 100644 --- a/modules/radio/src/path.h +++ b/modules/radio/src/path.h @@ -25,6 +25,10 @@ public: void setDemodulator(int demod); + void DEBUG_TEST(); + + io::AudioSink audio; + enum { DEMOD_FM, DEMOD_NFM, @@ -44,7 +48,6 @@ private: // Audio output dsp::FloatFIRResampler audioResamp; - io::AudioSink audio; std::string vfoName; diff --git a/readme.md b/readme.md index 065124ec..d4f5cef2 100644 --- a/readme.md +++ b/readme.md @@ -80,4 +80,5 @@ I will soon publish a contributing.md listing the code style to use. * [aosync](https://github.com/aosync) * [Benjamin Kyd](https://github.com/benkyd) * [Tobias Mädel](https://github.com/Manawyrm) -* [Raov](https://twitter.com/raov_birbtog) \ No newline at end of file +* [Raov](https://twitter.com/raov_birbtog) +* [SignalsEverywhere](https://signalseverywhere.com/) \ No newline at end of file diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 00000000..f7432479 --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 00000000..7f063699 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,6 @@ +#pragma once +#include + +namespace audio { + void registerStream(dsp::stream* stream, std::string name, std::string vfoName); +}; \ No newline at end of file diff --git a/src/io/audio.h b/src/io/audio.h index 784d73e7..01aa1892 100644 --- a/src/io/audio.h +++ b/src/io/audio.h @@ -27,6 +27,16 @@ namespace io { buffer = new float[_bufferSize * 2]; _volume = 1.0f; Pa_Initialize(); + + devTxtList = ""; + int devCount = Pa_GetDeviceCount(); + const PaDeviceInfo *deviceInfo; + for(int i = 0; i < devCount; i++) { + deviceInfo = Pa_GetDeviceInfo(i); + devTxtList += deviceInfo->name; + devTxtList += '\0'; + } + devIndex = Pa_GetDefaultOutputDevice(); } void setVolume(float volume) { @@ -34,11 +44,14 @@ namespace io { } void start() { + if (running) { + return; + } PaStreamParameters outputParams; outputParams.channelCount = 2; outputParams.sampleFormat = paFloat32; outputParams.hostApiSpecificStreamInfo = NULL; - outputParams.device = Pa_GetDefaultOutputDevice(); + outputParams.device = devIndex; outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency; PaError err = Pa_OpenStream(&stream, NULL, &outputParams, 48000.0f, _bufferSize, paClipOff, _callback, this); if (err != 0) { @@ -51,18 +64,41 @@ namespace io { return; } spdlog::info("Audio device open."); + running = true; } void stop() { + if (!running) { + return; + } + Pa_StopStream(stream); Pa_CloseStream(stream); + running = false; } void setBlockSize(int blockSize) { - stop(); + if (running) { + return; + } _bufferSize = blockSize; - start(); } + void setDevice(int id) { + if (devIndex == id) { + return; + } + if (running) { + return; + } + devIndex = id; + } + + int getDeviceId() { + return devIndex; + } + + std::string devTxtList; + private: static int _callback(const void *input, void *output, @@ -81,10 +117,12 @@ namespace io { return 0; } + int devIndex; int _bufferSize; dsp::stream* _input; float* buffer; float _volume; PaStream *stream; + bool running = false; }; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index c2dc095f..507bab9a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -73,14 +73,6 @@ int main() { spdlog::info("Loading band plans color table"); bandplan::loadColorTable("band_colors.json"); - spdlog::info("Loading test module"); - mod::initAPI(); - mod::loadModule("../modules/radio/build/Release/radio.dll", "Radio 1"); - mod::loadModule("../modules/radio/build/Release/radio.dll", "Radio 2"); - //mod::loadModule("../modules/demo/build/Release/demo.dll", "Demo Module 2"); - //mod::loadModule("../modules/demo/build/Release/demo.dll", "Demo Module 3"); - - spdlog::info("Ready."); // Main loop diff --git a/src/main_window.cpp b/src/main_window.cpp index 57653b1c..a20eaca2 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -56,6 +56,10 @@ void windowInit() { vfoman::init(&wtf, &sigPath); uiGains = new float[1]; + + spdlog::info("Loading modules"); + mod::initAPI(&wtf); + mod::loadFromList("module_list.json"); } watcher devId(0, true); @@ -64,7 +68,8 @@ watcher bandplanId(0, true); watcher freq(90500000L); int demod = 1; watcher vfoFreq(92000000.0f); -watcher volume(1.0f); +float dummyVolume = 1.0f; +float* volume = &dummyVolume; float fftMin = -70.0f; float fftMax = 0.0f; watcher offset(0.0f, true); @@ -175,6 +180,7 @@ void drawWindow() { if (wtf.selectedVFOChanged) { wtf.selectedVFOChanged = false; fSel.setFrequency(vfo->generalOffset + wtf.getCenterFrequency()); + mod::broadcastEvent(mod::EVENT_SELECTED_VFO_CHANGED); } if (fSel.frequencyChanged) { @@ -233,6 +239,13 @@ 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 (playing) { if (ImGui::ImageButton(icons::STOP_RAW, ImVec2(30, 30))) { @@ -252,7 +265,7 @@ void drawWindow() { ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8); ImGui::SetNextItemWidth(200); - ImGui::SliderFloat("##_2_", &volume.val, 0.0f, 1.0f, ""); + ImGui::SliderFloat("##_2_", volume, 0.0f, 1.0f, ""); ImGui::SameLine(); @@ -296,8 +309,6 @@ void drawWindow() { } } - int modCount = mod::moduleNames.size(); - mod::Module_t mod; for (int i = 0; i < modCount; i++) { if (ImGui::CollapsingHeader(mod::moduleNames[i].c_str())) { mod = mod::modules[mod::moduleNames[i]]; @@ -360,4 +371,12 @@ void drawWindow() { wtf.setFFTMax(fftMax); wtf.setWaterfallMin(fftMin); wtf.setWaterfallMax(fftMax); +} + +void bindVolumeVariable(float* vol) { + volume = vol; +} + +void unbindVolumeVariable() { + volume = &dummyVolume; } \ No newline at end of file diff --git a/src/main_window.h b/src/main_window.h index e7617d55..2cf7d24e 100644 --- a/src/main_window.h +++ b/src/main_window.h @@ -27,4 +27,6 @@ #define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground void windowInit(); -void drawWindow(); \ No newline at end of file +void drawWindow(); +void bindVolumeVariable(float* vol); +void unbindVolumeVariable(); \ No newline at end of file diff --git a/src/module.cpp b/src/module.cpp index 515ae0fb..d19c150b 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -1,12 +1,19 @@ #include #include +#include namespace mod { API_t API; std::map modules; std::vector moduleNames; + ImGui::WaterFall* _wtf; - void initAPI() { + std::string api_getSelectedVFOName() { + return _wtf->selectedVFO; + } + + void initAPI(ImGui::WaterFall* wtf) { + _wtf = wtf; API.registerVFO = vfoman::create; API.setVFOOffset = vfoman::setOffset; API.setVFOCenterOffset = vfoman::setCenterOffset; @@ -15,6 +22,9 @@ namespace mod { API.getVFOOutputBlockSize = vfoman::getOutputBlockSize; API.setVFOReference = vfoman::setReference; API.removeVFO = vfoman::remove; + API.getSelectedVFOName = api_getSelectedVFOName; + API.bindVolumeVariable = bindVolumeVariable; + API.unbindVolumeVariable = unbindVolumeVariable; } void loadModule(std::string path, std::string name) { @@ -33,7 +43,8 @@ namespace mod { spdlog::error("Couldn't load {0}.", name); return; } - mod._INIT_ = (void*(*)(mod::API_t*,ImGuiContext*,std::string))GetProcAddress(mod.inst, "_INIT_"); + 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._STOP_ = (void(*)(void*))GetProcAddress(mod.inst, "_STOP_"); @@ -43,7 +54,8 @@ namespace mod { spdlog::error("Couldn't load {0}.", name); return; } - mod._INIT_ = (void*(*)(mod::API_t*,ImGuiContext*,std::string))dlsym(mod.inst, "_INIT_"); + 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_"); @@ -52,6 +64,10 @@ namespace mod { 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); + return; + } if (mod._DRAW_MENU_ == NULL) { spdlog::error("Couldn't load {0} because it's missing _DRAW_MENU_.", name); return; @@ -80,5 +96,26 @@ namespace mod { mod._HANDLE_EVENT_(mod.ctx, eventId); } } + + 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; + data << file; + file.close(); + + std::map list = data.get>(); + for (auto const& [name, file] : list) { + spdlog::info("Loading {0} ({1})", name, file); + loadModule(file, name); + } + } }; diff --git a/src/module.h b/src/module.h index 2c9231a1..ff57504a 100644 --- a/src/module.h +++ b/src/module.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #ifdef _WIN32 #include @@ -27,6 +29,9 @@ namespace mod { int (*getVFOOutputBlockSize)(std::string name); void (*setVFOReference)(std::string name, int ref); void (*removeVFO)(std::string name); + std::string (*getSelectedVFOName)(void); + void (*bindVolumeVariable)(float* vol); + void (*unbindVolumeVariable)(void); enum { REF_LOWER, @@ -38,6 +43,7 @@ namespace mod { enum { EVENT_STREAM_PARAM_CHANGED, + EVENT_SELECTED_VFO_CHANGED, _EVENT_COUNT }; @@ -49,14 +55,16 @@ namespace mod { #endif void* (*_INIT_)(API_t*, ImGuiContext*, std::string); void (*_DRAW_MENU_)(void*); + void (*_NEW_FRAME_)(void*); void (*_HANDLE_EVENT_)(void*, int); void (*_STOP_)(void*); void* ctx; }; - void initAPI(); + void initAPI(ImGui::WaterFall* wtf); void loadModule(std::string path, std::string name); void broadcastEvent(int eventId); + void loadFromList(std::string path); extern std::map modules; extern std::vector moduleNames; diff --git a/src/waterfall.cpp b/src/waterfall.cpp index 10b4f63e..4f91e2cc 100644 --- a/src/waterfall.cpp +++ b/src/waterfall.cpp @@ -649,15 +649,8 @@ namespace ImGui { return; } reference = ref; - if (reference == REF_CENTER) { - setOffset(centerOffset); - } - else if (reference == REF_LOWER) { - setOffset(lowerOffset); - } - else if (reference == REF_UPPER) { - setOffset(upperOffset); - } + setOffset(generalOffset); + } void WaterfallVFO::updateDrawingVars(float viewBandwidth, float dataWidth, float viewOffset, ImVec2 widgetPos, int fftHeight) {