LCOV - code coverage report
Current view: top level - src - main.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 5.8 % 208 12
Test Date: 2026-06-03 09:13:19 Functions: 57.1 % 7 4

            Line data    Source code
       1              : #include "common.h"
       2              : #include "audio/engine/audio_engine.h"
       3              : #ifndef AMPLITRON_HEADLESS
       4              : #include "gui/gui_manager.h"
       5              : #include "gui/state/gui_graph_state.h"
       6              : // New include for Recovery
       7              : #include "gui/crash_recovery_ui.h"
       8              : #endif
       9              : #include "preset_manager.h"
      10              : #include "cli.h"
      11              : #include "gui/commands/command_graph.h"
      12              : #include "gui/commands/command_history.h"
      13              : 
      14              : #include "audio/effects/noise_gate.h"
      15              : #include "audio/effects/compressor.h"
      16              : #include "audio/effects/overdrive.h"
      17              : #include "audio/effects/distortion.h"
      18              : #include "audio/effects/equalizer.h"
      19              : #include "audio/effects/chorus.h"
      20              : #include "audio/effects/delay.h"
      21              : #include "audio/effects/reverb.h"
      22              : #include "audio/effects/cabinet_sim.h"
      23              : #include "audio/effects/amp_simulator.h"
      24              : 
      25              : #include <iostream>
      26              : #include <csignal>
      27              : #include <atomic>
      28              : #include <filesystem>
      29              : #include <thread>
      30              : #include <chrono>
      31              : #include <mutex>
      32              : #include <vector>
      33              : 
      34              : // New include for Autosave
      35              : #include "session_manager.h"
      36              : 
      37              : #ifdef __EMSCRIPTEN__
      38              : #include <emscripten.h>
      39              : #endif
      40              : 
      41              : #ifdef __ANDROID__
      42              : #include <SDL_main.h>
      43              : #elif defined(__APPLE__)
      44              : #include <TargetConditionals.h>
      45              : #if TARGET_OS_IOS
      46              : #include <SDL_main.h>
      47              : #endif
      48              : #endif
      49              : 
      50              : static std::atomic<bool> g_running{true};
      51              : 
      52              : #ifdef __EMSCRIPTEN__
      53              : static Amplitron::GuiManager* g_gui = nullptr;
      54              : 
      55              : static void em_main_loop() {
      56              :     if (!g_gui || !g_gui->run_frame()) {
      57              :         g_running = false;
      58              :         emscripten_cancel_main_loop();
      59              :     }
      60              : }
      61              : 
      62              : extern "C" EMSCRIPTEN_KEEPALIVE void on_midi_cc(int channel, int cc, int value) {
      63              :     // Validate MIDI ranges before processing
      64              :     if (!g_gui) {
      65              :         emscripten_log(EM_LOG_WARN, "[MIDI] GUI not initialized, dropping event");
      66              :         return;
      67              :     }
      68              :     
      69              :     // Range validation (standard MIDI)
      70              :     if (channel < 0 || channel > 15) {
      71              :         emscripten_log(EM_LOG_DEBUG, "[MIDI] Invalid channel: %d", channel);
      72              :         return;
      73              :     }
      74              :     if (cc < 0 || cc > 127) {
      75              :         emscripten_log(EM_LOG_DEBUG, "[MIDI] Invalid CC number: %d", cc);
      76              :         return;
      77              :     }
      78              :     if (value < 0 || value > 127) {
      79              :         emscripten_log(EM_LOG_DEBUG, "[MIDI] Invalid CC value: %d", value);
      80              :         return;
      81              :     }
      82              :     
      83              :     // Create MIDI event
      84              :     Amplitron::MidiEvent event;
      85              :     event.status = static_cast<uint8_t>(0xB0 | (channel & 0x0F));  // CC message
      86              :     event.data1 = static_cast<uint8_t>(cc);
      87              :     event.data2 = static_cast<uint8_t>(value);
      88              :     event.pad = 0;
      89              :     
      90              :     // Inject into MIDI queue
      91              :     g_gui->midi_manager().inject_event(event);
      92              :     
      93              :     emscripten_log(EM_LOG_DEBUG, "[MIDI] CC %d = %d on channel %d", cc, value, channel);
      94              : }
      95              : 
      96              : extern "C" EMSCRIPTEN_KEEPALIVE void on_midi_device_connected(const char* device_name) {
      97              :     if (!g_gui || !device_name) return;
      98              :     
      99              :     emscripten_log(EM_LOG_INFO, "[MIDI] Device connected: %s", device_name);
     100              : }
     101              : 
     102              : extern "C" EMSCRIPTEN_KEEPALIVE void on_canvas_touch_gesture(float dx, float dy, float dscale, float local_x, float local_y) {
     103              :     auto& ui = Amplitron::GuiGraphState::get_instance();
     104              :     if (dscale != 0.0f) {
     105              :         float factor = 1.0f + dscale;
     106              :         float old_zoom = ui.target_zoom;
     107              :         float new_zoom = old_zoom * factor;
     108              :         if (new_zoom < 0.2f) new_zoom = 0.2f;
     109              :         if (new_zoom > 5.0f) new_zoom = 5.0f;
     110              :         float actual_factor = new_zoom / old_zoom;
     111              :         ui.target_scrolling.x = local_x - (local_x - ui.target_scrolling.x) * actual_factor;
     112              :         ui.target_scrolling.y = local_y - (local_y - ui.target_scrolling.y) * actual_factor;
     113              :         ui.target_zoom = new_zoom;
     114              :     }
     115              :     ui.target_scrolling.x += dx;
     116              :     ui.target_scrolling.y += dy;
     117              : }
     118              : 
     119              : extern "C" EMSCRIPTEN_KEEPALIVE void on_canvas_touch_down(float x, float y) {
     120              :     auto& io = ImGui::GetIO();
     121              :     io.AddMousePosEvent(x, y);
     122              :     io.AddMouseButtonEvent(0, true);
     123              : }
     124              : 
     125              : extern "C" EMSCRIPTEN_KEEPALIVE void on_canvas_touch_move(float x, float y) {
     126              :     ImGui::GetIO().AddMousePosEvent(x, y);
     127              : }
     128              : 
     129              : extern "C" EMSCRIPTEN_KEEPALIVE void on_canvas_touch_up(float x, float y) {
     130              :     auto& io = ImGui::GetIO();
     131              :     io.AddMousePosEvent(x, y);
     132              :     io.AddMouseButtonEvent(0, false);
     133              : }
     134              : 
     135              : extern "C" EMSCRIPTEN_KEEPALIVE void on_canvas_touch_cancel() {
     136              :     auto& io = ImGui::GetIO();
     137              :     io.AddMouseButtonEvent(0, false);
     138              : }
     139              : 
     140              : extern "C" EMSCRIPTEN_KEEPALIVE bool is_canvas_hovered() {
     141              :     return Amplitron::GuiGraphState::get_instance().canvas_hovered;
     142              : }
     143              : 
     144              : extern "C" EMSCRIPTEN_KEEPALIVE float get_canvas_zoom() {
     145              :     return Amplitron::GuiGraphState::get_instance().target_zoom;
     146              : }
     147              : 
     148              : extern "C" EMSCRIPTEN_KEEPALIVE float get_canvas_scroll_x() {
     149              :     return Amplitron::GuiGraphState::get_instance().target_scrolling.x;
     150              : }
     151              : 
     152              : extern "C" EMSCRIPTEN_KEEPALIVE float get_canvas_scroll_y() {
     153              :     return Amplitron::GuiGraphState::get_instance().target_scrolling.y;
     154              : }
     155              : 
     156              : extern "C" EMSCRIPTEN_KEEPALIVE int get_node_count() {
     157              :     if (!g_gui) return 0;
     158              :     return static_cast<int>(g_gui->audio_engine().graph().get_nodes().size());
     159              : }
     160              : 
     161              : extern "C" EMSCRIPTEN_KEEPALIVE int get_link_count() {
     162              :     if (!g_gui) return 0;
     163              :     return static_cast<int>(g_gui->audio_engine().graph().get_links().size());
     164              : }
     165              : 
     166              : extern "C" EMSCRIPTEN_KEEPALIVE bool has_node_of_type(int routing_type) {
     167              :     if (!g_gui) return false;
     168              :     for (const auto& n : g_gui->audio_engine().graph().get_nodes()) {
     169              :         if (static_cast<int>(n.routing_type) == routing_type) return true;
     170              :     }
     171              :     return false;
     172              : }
     173              : 
     174              : extern "C" EMSCRIPTEN_KEEPALIVE int trigger_add_splitter_node() {
     175              :     if (!g_gui) return -1;
     176              :     auto cmd = std::make_unique<Amplitron::AddGraphNodeCommand>(
     177              :         g_gui->audio_engine(), "Splitter", Amplitron::NodeRoutingType::Splitter, nullptr, ImVec2(0, 0));
     178              :     auto* raw = cmd.get();
     179              :     g_gui->command_history().execute(std::move(cmd));
     180              :     return (raw->node_id != -1) ? raw->node_id : -1;
     181              : }
     182              : 
     183              : extern "C" EMSCRIPTEN_KEEPALIVE int trigger_add_link(int src_pin, int dst_pin) {
     184              :     if (!g_gui) return -1;
     185              :     auto cmd = std::make_unique<Amplitron::AddGraphLinkCommand>(g_gui->audio_engine(), src_pin, dst_pin);
     186              :     auto* raw = cmd.get();
     187              :     g_gui->command_history().execute(std::move(cmd));
     188              :     return raw->was_successful ? raw->link.id : -1;
     189              : }
     190              : 
     191              : extern "C" EMSCRIPTEN_KEEPALIVE int get_node_output_pin_by_index(int node_index, int pin_index) {
     192              :     if (!g_gui) return -1;
     193              :     const auto& nodes = g_gui->audio_engine().graph().get_nodes();
     194              :     if (node_index < 0 || node_index >= static_cast<int>(nodes.size())) return -1;
     195              :     const auto& node = nodes[node_index];
     196              :     if (pin_index < 0 || pin_index >= static_cast<int>(node.output_pin_ids.size())) return -1;
     197              :     return node.output_pin_ids[pin_index];
     198              : }
     199              : 
     200              : extern "C" EMSCRIPTEN_KEEPALIVE int get_node_input_pin_by_index(int node_index, int pin_index) {
     201              :     if (!g_gui) return -1;
     202              :     const auto& nodes = g_gui->audio_engine().graph().get_nodes();
     203              :     if (node_index < 0 || node_index >= static_cast<int>(nodes.size())) return -1;
     204              :     const auto& node = nodes[node_index];
     205              :     if (pin_index < 0 || pin_index >= static_cast<int>(node.input_pin_ids.size())) return -1;
     206              :     return node.input_pin_ids[pin_index];
     207              : }
     208              : 
     209              : extern "C" EMSCRIPTEN_KEEPALIVE bool trigger_delete_last_node() {
     210              :     if (!g_gui) return false;
     211              :     auto& graph = g_gui->audio_engine().graph();
     212              :     const auto& nodes = graph.get_nodes();
     213              :     // Walk backwards to find the last deletable node (mirrors GUI rules: Input and Amp Sim are protected)
     214              :     for (int i = static_cast<int>(nodes.size()) - 1; i >= 0; --i) {
     215              :         const auto& node = nodes[i];
     216              :         if (node.name == "Input" || node.name == "Amp Sim") continue;
     217              :         
     218              :         auto& ui_positions = Amplitron::GuiGraphState::get_instance().node_positions;
     219              :         ImVec2 pos(0, 0);
     220              :         auto pos_it = ui_positions.find(node.id);
     221              :         if (pos_it != ui_positions.end()) pos = pos_it->second.position;
     222              : 
     223              :         g_gui->command_history().execute(
     224              :             std::make_unique<Amplitron::RemoveGraphNodeCommand>(
     225              :                 g_gui->audio_engine(), node.id, node.routing_type, pos
     226              :             )
     227              :         );
     228              :         // Note: node_positions.erase is handled inside RemoveGraphNodeCommand::execute()
     229              :         return true;
     230              :     }
     231              :     return false; // No deletable node found
     232              : }
     233              : 
     234              : #endif
     235              : 
     236            6 : void signal_handler(int /*signal*/) {
     237            5 :     g_running = false;
     238            4 : }
     239              : 
     240            9 : int main(int argc, char* argv[]) {
     241              :     //breaks global iostream lock
     242            9 :     std::cin.tie(nullptr);
     243              :     //CLI argument parsing
     244            9 :     Amplitron::CliOptions cli_opts = Amplitron::handle_cli_args(argc, argv);
     245              : 
     246              :     #ifdef AMPLITRON_HEADLESS
     247            6 :     cli_opts.is_headless = true;
     248              :     
     249              :     // If the parser didn't already trigger an exit (like --help) validate the preset
     250            6 :     if (!cli_opts.exit_early && cli_opts.preset_path.empty()) {
     251            0 :         std::cerr << "Error: Strict headless build requires a --preset <path> argument." << std::endl;
     252            0 :         return 1; // Return non-zero failure code
     253              :     }
     254              :     #endif
     255              : 
     256            9 :     if(cli_opts.exit_early){
     257           12 :         std::cout << "[CLI]Application exited early :" << cli_opts.exit_reason << std::endl;
     258            9 :         return cli_opts.exit_code;
     259              :     }
     260              : 
     261            0 :     std::signal(SIGINT, signal_handler);
     262            0 :     std::signal(SIGTERM, signal_handler);
     263              : 
     264              :     // Initialize Session Manager
     265            0 :     Amplitron::SessionManager sessionManager("SudipMondal", "Amplitron");
     266              : 
     267            0 :     std::cout << "=== Amplitron v1.0 - Guitar Amp Simulator ===" << std::endl;
     268            0 :     std::cout << "Starting up..." << std::endl;
     269              : 
     270              :     // Initialize audio engine
     271            0 :     Amplitron::AudioEngine engine;
     272            0 :     if (!engine.initialize()) {
     273            0 :         std::cerr << "Failed to initialize audio engine!" << std::endl;
     274            0 :         return 1;
     275              :     }
     276              : 
     277              :     #ifndef AMPLITRON_HEADLESS
     278            0 :     std::unique_ptr<Amplitron::GuiManager> gui = nullptr;
     279              :     #endif
     280              : 
     281            0 :     if(cli_opts.is_headless){
     282            0 :         std::cout << "=== HEADLESS MODE ===" << std::endl;
     283            0 :         std::cout << "Loading preset: " << cli_opts.preset_path << std::endl;
     284              :         
     285              :         //Safe preset injection
     286            0 :         if (!Amplitron::PresetManager::load_preset(cli_opts.preset_path, engine, nullptr)){
     287            0 :             std::cerr << "Fatal Error: Could not load preset for headless mode." << std::endl;
     288            0 :             engine.shutdown();
     289            0 :             return 1;
     290              :         }
     291              :         //Hardware routing(i/p)
     292            0 :         if(!cli_opts.input_device.empty()){
     293            0 :             auto devices = engine.get_input_devices();
     294            0 :             std::vector<int> match_indices;
     295              : 
     296              :             //Searching and storing all matching devices
     297            0 :             for(size_t i = 0; i < devices.size(); ++i){
     298            0 :                 if (devices[i].name.find(cli_opts.input_device) != std::string::npos){
     299            0 :                     match_indices.push_back(i);
     300            0 :                 }
     301            0 :             }
     302              :             
     303            0 :             if(match_indices.empty()){
     304            0 :                 std::cerr << "Warning: Could not find requested input device: '" << cli_opts.input_device << "'" << std::endl;
     305            0 :             } else if (match_indices.size() == 1){
     306            0 :                 engine.set_input_device(devices[match_indices[0]].index);
     307            0 :                 std::cout << "Input routed to: " << devices[match_indices[0]].name << std::endl;
     308            0 :             } else {
     309            0 :                 std::cerr << "Warning: Ambiguous input name '" << cli_opts.input_device << "'. Multiple matches found:" << std::endl;
     310            0 :                 for(int idx : match_indices){
     311            0 :                     std::cerr << " -- " << devices[idx].name << std::endl;
     312              :                 }
     313            0 :                 std::cerr << "Auto-selecting the first match: " << devices[match_indices[0]].name << std::endl;
     314            0 :                 engine.set_input_device(devices[match_indices[0]].index);
     315              :             }
     316            0 :         }
     317              :         //hardware routing(o/p)
     318            0 :         if(!cli_opts.output_device.empty()) {
     319            0 :             auto devices = engine.get_output_devices();
     320            0 :             std::vector<int> match_indices;
     321              : 
     322            0 :             for(size_t i = 0;i <devices.size(); ++i){
     323            0 :                 if(devices[i].name.find(cli_opts.output_device) != std::string::npos) {
     324            0 :                     match_indices.push_back(i);
     325            0 :                 }
     326            0 :             }
     327              : 
     328            0 :             if(match_indices.empty()){
     329            0 :                 std::cerr << "Warning: Could not find requested output device: '" << cli_opts.output_device << "'" << std::endl;
     330            0 :             } else if(match_indices.size() == 1){
     331            0 :                 engine.set_output_device(devices[match_indices[0]].index);
     332            0 :                 std::cout<< "Output routed to: " << devices[match_indices[0]].name << std::endl;
     333            0 :             } else{
     334            0 :                 std::cerr << "Warning: Ambiguous output name '" << cli_opts.output_device << "'. Multiple matches found:" <<std::endl;
     335            0 :                 for(int idx : match_indices){
     336            0 :                     std::cerr << " -- " << devices[idx].name << std::endl;
     337              :                 }
     338            0 :                 std::cerr << "Auto-selecting the first match: " << devices[match_indices[0]].name << std::endl;
     339            0 :                 engine.set_output_device(devices[match_indices[0]].index);
     340              :             }
     341            0 :         }
     342              : 
     343            0 :     } 
     344              :     #ifndef AMPLITRON_HEADLESS
     345              :     else {
     346              :     // GUI bootup
     347            0 :         gui = std::make_unique<Amplitron::GuiManager>(engine);
     348              :     // Create a small, automatically wired, and highly playable circuit
     349            0 :         auto cabinet = std::make_shared<Amplitron::CabinetSim>();
     350            0 :         cabinet->set_enabled(true);
     351              : 
     352            0 :         auto amp = std::make_shared<Amplitron::AmpSimulator>();
     353            0 :         amp->set_enabled(true);
     354              : 
     355            0 :         engine.add_initial_effects({cabinet, amp});
     356              : 
     357            0 :         engine.set_input_gain(0.7f);
     358              : 
     359            0 :         if (sessionManager.hasUnsavedSession()) {
     360            0 :             if (promptRestoreSession()) {
     361            0 :                 try {
     362            0 :                     nlohmann::json savedState = sessionManager.loadSession();
     363            0 :                     engine.deserialize(savedState);
     364            0 :                 } catch (const nlohmann::json::parse_error& e) {
     365            0 :                     std::cerr << "Autosave file corrupted. Discarding." << std::endl;
     366            0 :                     sessionManager.clearSession();
     367            0 :                 }
     368            0 :             } else {
     369            0 :                 sessionManager.clearSession();
     370              :             }
     371            0 :         }
     372              : 
     373            0 :         if (std::filesystem::exists("presets")) {
     374            0 :             Amplitron::PresetManager::set_presets_dir("presets");
     375            0 :         }
     376              : 
     377            0 :         if (!gui->initialize(1280, 720)) {
     378            0 :             std::cerr << "Failed to initialize GUI!" << std::endl;
     379            0 :             engine.shutdown();
     380            0 :             return 1;
     381              :         }
     382            0 :     }
     383              :     #endif
     384              :     
     385              :     
     386            0 :     if (!engine.start()) {
     387            0 :         std::cerr << "Warning: Could not start audio stream." << std::endl;
     388            0 :     }
     389              : 
     390            0 :     std::cout << "Amplitron is ready. Let's play!" << std::endl;
     391              : #ifdef __EMSCRIPTEN__
     392              :     g_gui = gui.get();
     393              :     emscripten_set_main_loop(em_main_loop, 0, 1);
     394              : #else
     395            0 :     std::atomic<bool> show_telemetry{true};
     396              : 
     397            0 :     if (cli_opts.is_headless){
     398            0 :         std::cout << "Audio Engine is running in the background." << std::endl;
     399            0 :         std::cout << "Commands: chain, gain <val>, bypass <idx>, enable <idx>, telemetry <on/off>" << std::endl;
     400            0 :         std::cout << "Press Ctrl+C to shut down." << std::endl;
     401              : 
     402            0 :         std::mutex cli_mutex;
     403            0 :         std::vector<std::string> cli_commands;
     404              : 
     405              :         //stdin thread to listen commands
     406            0 :         std::thread stdin_listener([&cli_mutex, &cli_commands](){
     407            0 :             std::string line;
     408            0 :             while(std::getline(std::cin, line)){
     409            0 :                 if(line.empty()) continue;
     410            0 :                 std::lock_guard<std::mutex> lock(cli_mutex);
     411            0 :                 cli_commands.push_back(line);
     412            0 :             }
     413            0 :         });
     414            0 :         stdin_listener.detach();
     415            0 :         int loop_counter=0;
     416              : 
     417              :         //headless loop
     418            0 :         while(g_running){
     419            0 :             std::this_thread::sleep_for(std::chrono::milliseconds(100));
     420            0 :             std::vector<std::string> pending_commands;
     421            0 :             {
     422            0 :                 std::lock_guard<std::mutex> lock(cli_mutex);
     423            0 :                 std::swap(pending_commands,cli_commands);
     424            0 :             }
     425              : 
     426            0 :             for(const std::string& line : pending_commands){     
     427            0 :             if(line.find("gain ") == 0){
     428            0 :                     try{
     429            0 :                         float val = std::stof(line.substr(5));
     430            0 :                         engine.set_output_gain(val);
     431            0 :                         std::cout << ">> Output gain set to " << val << std::endl;
     432            0 :                     } catch (...){
     433            0 :                         std::cout << ">> Invalid gain" << std::endl;
     434            0 :                     }
     435            0 :                 } else if (line.find("bypass ") == 0){
     436            0 :                     try {
     437            0 :                         int idx = std::stoi(line.substr(7));
     438              :                         //push_effect_enabled expects a float(>0.5 is enabled, <0.5 is bypassed)
     439            0 :                         engine.push_effect_enabled(idx, 0.0f);
     440            0 :                         std::cout << ">> Effect " << idx << " bypassed." << std::endl;
     441            0 :                     } catch(...){
     442            0 :                         std::cout << ">> Invalid index." << std::endl;
     443            0 :                     }
     444            0 :                 } else if (line.find("enable ") == 0){
     445            0 :                     try{
     446            0 :                         int idx = std::stoi(line.substr(7));
     447            0 :                         engine.push_effect_enabled(idx, 1.0f);
     448            0 :                         std::cout << ">> Effect " << idx << " enabled." << std::endl;
     449            0 :                     } catch(...){
     450            0 :                         std::cout << ">> Invalid index. Try: enable 0" << std::endl;
     451            0 :                     }
     452            0 :                 } else if (line == "telemetry off"){
     453            0 :                     show_telemetry.store(false, std::memory_order_relaxed);
     454            0 :                     std::cout << ">> Telemetry muted. Type 'telemetry on' to resume." << std::endl;
     455            0 :                 }
     456            0 :                   else if (line == "telemetry on"){
     457            0 :                     show_telemetry.store(true, std::memory_order_relaxed);
     458            0 :                     std::cout << ">> Telemetry resumed." << std::endl;
     459            0 :                 } else if (line == "chain"){
     460            0 :                     std::string chain_str = "\n=== ACTIVE SIGNAL CHAIN ===\n";
     461              :                     
     462            0 :                     const auto& nodes = engine.graph().get_nodes();
     463            0 :                     int print_index = 0;
     464              :                     
     465            0 :                     for (const auto& node : nodes) {
     466            0 :                         if (node.pedal) { // Only print actual effects
     467            0 :                             chain_str += "[" + std::to_string(print_index) + "] " + 
     468            0 :                                          node.pedal->get_display_name() + 
     469            0 :                                          (node.pedal->is_enabled() ? " (ON)\n" : " (BYPASSED)\n");
     470            0 :                             print_index++;
     471            0 :                         }
     472              :                     }
     473              :                     
     474              :                     // If no actual pedals were found in the graph
     475            0 :                     if (print_index == 0) {
     476            0 :                         chain_str += "(Chain is empty)\n";
     477            0 :                     }
     478              : 
     479            0 :                     chain_str += "===========================";
     480            0 :                     std::cout << chain_str << std::endl;
     481            0 :                 }
     482              :                  else {
     483            0 :                     std::cout << ">> Unknown command. Available: gain <val>, bypass <idx>, enable <idx>, telemetry <on/off>" << std::endl;
     484              :                 }
     485              :             }
     486              :             //telemetry logic(activates every 10s)
     487            0 :             if(++loop_counter >= 100){
     488            0 :                 if(show_telemetry.load(std::memory_order_relaxed)) { 
     489            0 :                     float dsp_load = engine.get_cpu_load() * 100.0f;
     490            0 :                     float in_peak = engine.get_input_level();
     491            0 :                     float out_peak = engine.get_output_level();
     492            0 :                     std::string dashboard = "\n========================================\n";
     493            0 :                     dashboard += "Active Buffer Size: " + std::to_string(engine.get_buffer_size()) + 
     494            0 :                                  " samples @ " + std::to_string(engine.get_sample_rate()) + "Hz\n";
     495            0 :                     dashboard += "DSP Load: " + std::to_string(dsp_load) + "%\n";
     496            0 :                     dashboard += "Peak I/O : IN " + std::to_string(in_peak) + " | OUT " + std::to_string(out_peak) + "\n";
     497            0 :                     dashboard += "========================================\n";
     498              :                     
     499            0 :                     std::cout << dashboard << std::flush;
     500            0 :                 }
     501            0 :                 loop_counter = 0;//reset timer
     502            0 :             }
     503            0 :         }
     504            0 :     } 
     505              :     #ifndef AMPLITRON_HEADLESS
     506              :     else{
     507              :         //GUI loop
     508            0 :         while(g_running && gui->run_frame()){
     509            0 :             if (sessionManager.shouldSave()){
     510            0 :                 sessionManager.saveSession(engine.serialize());
     511            0 :             }
     512              :         }
     513              :     }
     514              :     #endif
     515              : #endif
     516              : 
     517              :     // Cleanup
     518            0 :     std::cout << "Shutting down..." << std::endl;
     519              :     
     520              : #ifdef __EMSCRIPTEN__
     521              :     g_gui = nullptr;
     522              : #endif
     523              : 
     524              : #ifndef AMPLITRON_HEADLESS
     525            0 :     if(!cli_opts.is_headless){ 
     526            0 :         gui->shutdown();
     527            0 :     }
     528              : #endif
     529              : 
     530            0 :     if(!cli_opts.is_headless) {
     531            0 :         sessionManager.clearSession(); 
     532            0 :     }
     533            0 :     engine.shutdown();
     534            0 :     std::cout << "Goodbye!" << std::endl;
     535              : 
     536            0 :     return 0;
     537            9 : }
        

Generated by: LCOV version 2.0-1