#include #include #include "imgui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void MainWindow::init() { LoadingScreen::show("Initializing UI"); gui::waterfall.init(); gui::waterfall.setRawFFTSize(fftSize); credits::init(); core::configManager.acquire(); json menuElements = core::configManager.conf["menuElements"]; std::string modulesDir = core::configManager.conf["modulesDirectory"]; std::string resourcesDir = core::configManager.conf["resourcesDirectory"]; core::configManager.release(); // Assert that directories are absolute modulesDir = std::filesystem::absolute(modulesDir).string(); resourcesDir = std::filesystem::absolute(resourcesDir).string(); // Load menu elements gui::menu.order.clear(); for (auto& elem : menuElements) { if (!elem.contains("name")) { spdlog::error("Menu element is missing name key"); continue; } if (!elem["name"].is_string()) { spdlog::error("Menu element name isn't a string"); continue; } if (!elem.contains("open")) { spdlog::error("Menu element is missing open key"); continue; } if (!elem["open"].is_boolean()) { spdlog::error("Menu element name isn't a string"); continue; } Menu::MenuOption_t opt; opt.name = elem["name"]; opt.open = elem["open"]; gui::menu.order.push_back(opt); } gui::menu.registerEntry("Source", sourcemenu::draw, NULL); gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL); gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL); gui::menu.registerEntry("Display", displaymenu::draw, NULL); gui::menu.registerEntry("Theme", thememenu::draw, NULL); gui::menu.registerEntry("VFO Color", vfo_color_menu::draw, NULL); gui::menu.registerEntry("Module Manager", module_manager_menu::draw, NULL); gui::freqSelect.init(); // Set default values for waterfall in case no source init's it gui::waterfall.setBandwidth(8000000); gui::waterfall.setViewBandwidth(8000000); fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); sigpath::signalPath.init(8000000, 20, fftSize, &dummyStream, (dsp::complex_t*)fft_in, fftHandler, this); sigpath::signalPath.start(); vfoCreatedHandler.handler = vfoAddedHandler; vfoCreatedHandler.ctx = this; sigpath::vfoManager.onVfoCreated.bindHandler(&vfoCreatedHandler); spdlog::info("Loading modules"); // Load modules from /module directory if (std::filesystem::is_directory(modulesDir)) { for (const auto& file : std::filesystem::directory_iterator(modulesDir)) { 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 " + file.path().filename().string()); core::moduleManager.loadModule(path); } } else { spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir); } // Read module config core::configManager.acquire(); std::vector modules = core::configManager.conf["modules"]; auto modList = core::configManager.conf["moduleInstances"].items(); core::configManager.release(); // Load additional modules specified through config for (auto const& path : modules) { #ifndef __ANDROID__ std::string apath = std::filesystem::absolute(path).string(); spdlog::info("Loading {0}", apath); LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string()); core::moduleManager.loadModule(apath); #else core::moduleManager.loadModule(path); #endif } // Create module instances for (auto const& [name, _module] : modList) { std::string mod = _module["module"]; bool enabled = _module["enabled"]; spdlog::info("Initializing {0} ({1})", name, mod); LoadingScreen::show("Initializing " + name + " (" + mod + ")"); core::moduleManager.createInstance(name, mod); if (!enabled) { core::moduleManager.disableInstance(name); } } // Load color maps LoadingScreen::show("Loading color maps"); spdlog::info("Loading color maps"); if (std::filesystem::is_directory(resourcesDir + "/colormaps")) { for (const auto& file : std::filesystem::directory_iterator(resourcesDir + "/colormaps")) { std::string path = file.path().generic_string(); LoadingScreen::show("Loading " + file.path().filename().string()); spdlog::info("Loading {0}", path); if (file.path().extension().generic_string() != ".json") { continue; } if (!file.is_regular_file()) { continue; } colormaps::loadMap(path); } } else { spdlog::warn("Color map directory {0} does not exist, not loading modules from directory", modulesDir); } gui::waterfall.updatePalletteFromArray(colormaps::maps["Turbo"].map, colormaps::maps["Turbo"].entryCount); sourcemenu::init(); sinkmenu::init(); bandplanmenu::init(); displaymenu::init(); vfo_color_menu::init(); module_manager_menu::init(); // TODO for 0.2.5 // Fix gain not updated on startup, soapysdr // Update UI settings LoadingScreen::show("Loading configuration"); core::configManager.acquire(); fftMin = core::configManager.conf["min"]; fftMax = core::configManager.conf["max"]; gui::waterfall.setFFTMin(fftMin); gui::waterfall.setWaterfallMin(fftMin); gui::waterfall.setFFTMax(fftMax); gui::waterfall.setWaterfallMax(fftMax); double frequency = core::configManager.conf["frequency"]; showMenu = core::configManager.conf["showMenu"]; startedWithMenuClosed = !showMenu; gui::freqSelect.setFrequency(frequency); gui::freqSelect.frequencyChanged = false; sigpath::sourceManager.tune(frequency); gui::waterfall.setCenterFrequency(frequency); bw = 1.0; gui::waterfall.vfoFreqChanged = false; gui::waterfall.centerFreqMoved = false; gui::waterfall.selectFirstVFO(); menuWidth = core::configManager.conf["menuWidth"]; newWidth = menuWidth; fftHeight = core::configManager.conf["fftHeight"]; gui::waterfall.setFFTHeight(fftHeight); tuningMode = core::configManager.conf["centerTuning"] ? tuner::TUNER_MODE_CENTER : tuner::TUNER_MODE_NORMAL; gui::waterfall.VFOMoveSingleClick = (tuningMode == tuner::TUNER_MODE_CENTER); core::configManager.release(); // Correct the offset of all VFOs so that they fit on the screen float finalBwHalf = gui::waterfall.getBandwidth() / 2.0; for (auto& [_name, _vfo] : gui::waterfall.vfos) { if (_vfo->lowerOffset < -finalBwHalf) { sigpath::vfoManager.setCenterOffset(_name, (_vfo->bandwidth / 2) - finalBwHalf); continue; } if (_vfo->upperOffset > finalBwHalf) { sigpath::vfoManager.setCenterOffset(_name, finalBwHalf - (_vfo->bandwidth / 2)); continue; } } initComplete = true; core::moduleManager.doPostInitAll(); } void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { MainWindow* _this = (MainWindow*)ctx; std::lock_guard lck(_this->fft_mtx); // Check if the count is valid if (count > _this->fftSize) { return; } // Apply window volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, sigpath::signalPath.fftTaps, count); // Zero out the rest of the samples if (count < _this->fftSize) { memset(&_this->fft_in[count], 0, (_this->fftSize - count) * sizeof(dsp::complex_t)); } // Execute FFT fftwf_execute(_this->fftwPlan); // Get the FFT buffer float* fftBuf = gui::waterfall.getFFTBuffer(); if (fftBuf == NULL) { gui::waterfall.pushFFT(); return; } // Take power of spectrum volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fft_out, _this->fftSize, _this->fftSize); // Push back data gui::waterfall.pushFFT(); } void MainWindow::vfoAddedHandler(VFOManager::VFO* vfo, void* ctx) { MainWindow* _this = (MainWindow*)ctx; std::string name = vfo->getName(); core::configManager.acquire(); if (!core::configManager.conf["vfoOffsets"].contains(name)) { core::configManager.release(); return; } double offset = core::configManager.conf["vfoOffsets"][name]; core::configManager.release(); double viewBW = gui::waterfall.getViewBandwidth(); double viewOffset = gui::waterfall.getViewOffset(); double viewLower = viewOffset - (viewBW / 2.0); double viewUpper = viewOffset + (viewBW / 2.0); double newOffset = std::clamp(offset, viewLower, viewUpper); sigpath::vfoManager.setCenterOffset(name, _this->initComplete ? newOffset : offset); } void MainWindow::draw() { ImGui::Begin("Main", NULL, WINDOW_FLAGS); ImGui::WaterfallVFO* vfo = NULL; if (gui::waterfall.selectedVFO != "") { vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO]; } // Handle VFO movement if (vfo != NULL) { if (vfo->centerOffsetChanged) { if (tuningMode == tuner::TUNER_MODE_CENTER) { tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::waterfall.getCenterFrequency() + vfo->generalOffset); } gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); gui::freqSelect.frequencyChanged = false; core::configManager.acquire(); core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset; core::configManager.release(true); } } sigpath::vfoManager.updateFromWaterfall(&gui::waterfall); // Handle selection of another VFO if (gui::waterfall.selectedVFOChanged) { gui::freqSelect.setFrequency((vfo != NULL) ? (vfo->generalOffset + gui::waterfall.getCenterFrequency()) : gui::waterfall.getCenterFrequency()); gui::waterfall.selectedVFOChanged = false; gui::freqSelect.frequencyChanged = false; } // Handle change in selected frequency if (gui::freqSelect.frequencyChanged) { gui::freqSelect.frequencyChanged = false; tuner::tune(tuningMode, gui::waterfall.selectedVFO, gui::freqSelect.frequency); if (vfo != NULL) { vfo->centerOffsetChanged = false; vfo->lowerOffsetChanged = false; vfo->upperOffsetChanged = false; } core::configManager.acquire(); core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency(); if (vfo != NULL) { core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset; } core::configManager.release(true); } // Handle dragging the frequency scale if (gui::waterfall.centerFreqMoved) { gui::waterfall.centerFreqMoved = false; sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency()); if (vfo != NULL) { gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset); } else { gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency()); } core::configManager.acquire(); core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency(); core::configManager.release(true); } int _fftHeight = gui::waterfall.getFFTHeight(); if (fftHeight != _fftHeight) { fftHeight = _fftHeight; core::configManager.acquire(); core::configManager.conf["fftHeight"] = fftHeight; core::configManager.release(true); } // To Bar ImVec2 btnSize(30 * style::uiScale, 30 * style::uiScale); ImGui::PushID(ImGui::GetID("sdrpp_menu_btn")); if (ImGui::ImageButton(icons::MENU, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_MENU, false)) { showMenu = !showMenu; core::configManager.acquire(); core::configManager.conf["showMenu"] = showMenu; core::configManager.release(true); } ImGui::PopID(); ImGui::SameLine(); bool tmpPlaySate = playing; if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); } if (playing) { ImGui::PushID(ImGui::GetID("sdrpp_stop_btn")); if (ImGui::ImageButton(icons::STOP, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) { setPlayState(false); } ImGui::PopID(); } else { // TODO: Might need to check if there even is a device ImGui::PushID(ImGui::GetID("sdrpp_play_btn")); if (ImGui::ImageButton(icons::PLAY, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) { setPlayState(true); } ImGui::PopID(); } if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); } ImGui::SameLine(); float origY = ImGui::GetCursorPosY(); sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248 * style::uiScale, btnSize.x, 5, true); ImGui::SameLine(); ImGui::SetCursorPosY(origY); gui::freqSelect.draw(); ImGui::SameLine(); ImGui::SetCursorPosY(origY); if (tuningMode == tuner::TUNER_MODE_CENTER) { ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn")); if (ImGui::ImageButton(icons::CENTER_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) { tuningMode = tuner::TUNER_MODE_NORMAL; gui::waterfall.VFOMoveSingleClick = false; core::configManager.acquire(); core::configManager.conf["centerTuning"] = false; core::configManager.release(true); } ImGui::PopID(); } else { // TODO: Might need to check if there even is a device ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn")); if (ImGui::ImageButton(icons::NORMAL_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) { tuningMode = tuner::TUNER_MODE_CENTER; gui::waterfall.VFOMoveSingleClick = true; tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency); core::configManager.acquire(); core::configManager.conf["centerTuning"] = true; core::configManager.release(true); } ImGui::PopID(); } ImGui::SameLine(); int snrOffset = 87.0f * style::uiScale; int snrWidth = std::min(300.0f * style::uiScale, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - snrOffset); ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth + snrOffset)); ImGui::SetCursorPosY(origY + (5.0f * style::uiScale)); ImGui::SetNextItemWidth(snrWidth); ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0); ImGui::SameLine(); // Logo button ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (48 * style::uiScale)); ImGui::SetCursorPosY(10 * style::uiScale); if (ImGui::ImageButton(icons::LOGO, ImVec2(32 * style::uiScale, 32 * style::uiScale), ImVec2(0, 0), ImVec2(1, 1), 0)) { showCredits = true; } if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { showCredits = false; } if (ImGui::IsKeyPressed(KB_KEY_ESC)) { showCredits = false; } // Handle menu resize ImVec2 winSize = ImGui::GetWindowSize(); ImVec2 mousePos = ImGui::GetMousePos(); if (!lockWaterfallControls) { float curY = ImGui::GetCursorPosY(); bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left); bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left); if (grabbingMenu) { newWidth = mousePos.x; newWidth = std::clamp(newWidth, 250, winSize.x - 250); ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive)); } if (mousePos.x >= newWidth - (2.0f * style::uiScale) && mousePos.x <= newWidth + (2.0f * style::uiScale) && mousePos.y > curY) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (click) { grabbingMenu = true; } } else { ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow); } if (!down && grabbingMenu) { grabbingMenu = false; menuWidth = newWidth; core::configManager.acquire(); core::configManager.conf["menuWidth"] = menuWidth; core::configManager.release(true); } } // Left Column lockWaterfallControls = false; if (showMenu) { ImGui::Columns(3, "WindowColumns", false); ImGui::SetColumnWidth(0, menuWidth); ImGui::SetColumnWidth(1, winSize.x - menuWidth - (60.0f * style::uiScale)); ImGui::SetColumnWidth(2, 60.0f * style::uiScale); ImGui::BeginChild("Left Column"); if (gui::menu.draw(firstMenuRender)) { core::configManager.acquire(); json arr = json::array(); for (int i = 0; i < gui::menu.order.size(); i++) { arr[i]["name"] = gui::menu.order[i].name; arr[i]["open"] = gui::menu.order[i].open; } core::configManager.conf["menuElements"] = arr; // Update enabled and disabled modules for (auto [_name, inst] : core::moduleManager.instances) { if (!core::configManager.conf["moduleInstances"].contains(_name)) { continue; } core::configManager.conf["moduleInstances"][_name]["enabled"] = inst.instance->isEnabled(); } core::configManager.release(true); } if (startedWithMenuClosed) { startedWithMenuClosed = false; } else { firstMenuRender = false; } if (ImGui::CollapsingHeader("Debug")) { ImGui::Text("Frame time: %.3f ms/frame", ImGui::GetIO().DeltaTime * 1000.0f); ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate); ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency()); ImGui::Text("Source name: %s", sourceName.c_str()); ImGui::Checkbox("Show demo window", &demoWindow); ImGui::Text("ImGui version: %s_feb_2022", ImGui::GetVersion()); ImGui::Checkbox("Bypass buffering", &sigpath::signalPath.inputBuffer.bypass); ImGui::Text("Buffering: %d", (sigpath::signalPath.inputBuffer.writeCur - sigpath::signalPath.inputBuffer.readCur + 32) % 32); if (ImGui::Button("Test Bug")) { spdlog::error("Will this make the software crash?"); } if (ImGui::Button("Testing something")) { gui::menu.order[0].open = true; firstMenuRender = true; } ImGui::Checkbox("WF Single Click", &gui::waterfall.VFOMoveSingleClick); ImGui::Spacing(); } ImGui::EndChild(); } else { // When hiding the menu bar ImGui::Columns(3, "WindowColumns", false); ImGui::SetColumnWidth(0, 8 * style::uiScale); ImGui::SetColumnWidth(1, winSize.x - ((8 + 60) * style::uiScale)); ImGui::SetColumnWidth(2, 60.0f * style::uiScale); } // Right Column ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGui::NextColumn(); ImGui::PopStyleVar(); ImGui::BeginChild("Waterfall"); gui::waterfall.draw(); ImGui::EndChild(); if (!lockWaterfallControls) { // Handle arrow keys if (vfo != NULL && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) { if (ImGui::IsKeyPressed(KB_KEY_LEFT) && !gui::freqSelect.digitHovered) { double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset - vfo->snapInterval; nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval; tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq); } if (ImGui::IsKeyPressed(KB_KEY_RIGHT) && !gui::freqSelect.digitHovered) { double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + vfo->snapInterval; nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval; tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq); } core::configManager.acquire(); core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency(); if (vfo != NULL) { core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset; } core::configManager.release(true); } // Handle scrollwheel int wheel = ImGui::GetIO().MouseWheel; if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) { double nfreq; if (vfo != NULL) { nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (vfo->snapInterval * wheel); nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval; } else { nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0); } tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq); gui::freqSelect.setFrequency(nfreq); core::configManager.acquire(); core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency(); if (vfo != NULL) { core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset; } core::configManager.release(true); } } ImGui::NextColumn(); ImGui::BeginChild("WaterfallControls"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0)); ImGui::TextUnformatted("Zoom"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); ImVec2 wfSliderSize(20.0 * style::uiScale, 150.0 * style::uiScale); if (ImGui::VSliderFloat("##_7_", wfSliderSize, &bw, 1.0, 0.0, "")) { double factor = (double)bw * (double)bw; // Map 0.0 -> 1.0 to 1000.0 -> bandwidth double wfBw = gui::waterfall.getBandwidth(); double delta = wfBw - 1000.0; double finalBw = std::min(1000.0 + (factor * delta), wfBw); gui::waterfall.setViewBandwidth(finalBw); if (vfo != NULL) { gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen } } ImGui::NewLine(); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0)); ImGui::TextUnformatted("Max"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); if (ImGui::VSliderFloat("##_8_", wfSliderSize, &fftMax, 0.0, -160.0f, "")) { fftMax = std::max(fftMax, fftMin + 10); core::configManager.acquire(); core::configManager.conf["max"] = fftMax; core::configManager.release(true); } ImGui::NewLine(); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0)); ImGui::TextUnformatted("Min"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); if (ImGui::VSliderFloat("##_9_", wfSliderSize, &fftMin, 0.0, -160.0f, "")) { fftMin = std::min(fftMax - 10, fftMin); core::configManager.acquire(); core::configManager.conf["min"] = fftMin; core::configManager.release(true); } ImGui::EndChild(); gui::waterfall.setFFTMin(fftMin); gui::waterfall.setFFTMax(fftMax); gui::waterfall.setWaterfallMin(fftMin); gui::waterfall.setWaterfallMax(fftMax); ImGui::End(); if (showCredits) { credits::show(); } if (demoWindow) { ImGui::ShowDemoWindow(); } } void MainWindow::setPlayState(bool _playing) { if (_playing == playing) { return; } if (_playing) { sigpath::signalPath.inputBuffer.flush(); sigpath::sourceManager.start(); sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency()); playing = true; onPlayStateChange.emit(true); } else { playing = false; onPlayStateChange.emit(false); sigpath::sourceManager.stop(); sigpath::signalPath.inputBuffer.flush(); } } void MainWindow::setViewBandwidthSlider(float bandwidth) { bw = bandwidth; } bool MainWindow::sdrIsRunning() { return playing; } void MainWindow::setFFTSize(int size) { std::lock_guard lck(fft_mtx); fftSize = size; gui::waterfall.setRawFFTSize(fftSize); sigpath::signalPath.setFFTSize(fftSize); fftwf_destroy_plan(fftwPlan); fftwf_free(fft_in); fftwf_free(fft_out); fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); } void MainWindow::setFFTWindow(int win) { std::lock_guard lck(fft_mtx); sigpath::signalPath.setFFTWindow(win); } bool MainWindow::isPlaying() { return playing; } void MainWindow::setFirstMenuRender() { firstMenuRender = true; }