LCOV - code coverage report
Current view: top level - src/audio/engine - audio_engine_chain.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 114 114
Test Date: 2026-06-03 09:13:19 Functions: 100.0 % 10 10

            Line data    Source code
       1              : #include "audio/engine/audio_engine.h"
       2              : #include <algorithm>
       3              : 
       4              : namespace Amplitron {
       5              : 
       6         1092 : void AudioEngine::sync_graph_with_dummy_effects(bool reset_graph) {
       7          364 :     {
       8         1092 :         std::lock_guard<std::mutex> lock(effect_mutex_);
       9         1092 :         if (reset_graph) {
      10          114 :             main_graph_ = AudioGraph();
      11           38 :         }
      12              :         
      13         1092 :         if (main_graph_.get_nodes().empty()) {
      14              :             // 1. INITIAL SETUP: Reset the main graph model completely and auto-wire
      15          351 :             main_graph_ = AudioGraph();
      16              :             
      17          351 :             float cursor_x = 40.0f;
      18          351 :             float cursor_y = 60.0f;
      19              :             
      20          585 :             int input_node_id = main_graph_.add_node("Input", NodeRoutingType::StandardEffect, nullptr);
      21          351 :             main_graph_.set_node_as_input(input_node_id, true);
      22          351 :             main_graph_.set_node_position(input_node_id, cursor_x, cursor_y);
      23          351 :             cursor_x += 160.0f; // Input node is narrower
      24              :             
      25          351 :             int prev_output_pin = main_graph_.get_nodes().back().output_pin_ids.empty() ? -1 : main_graph_.get_nodes().back().output_pin_ids[0];
      26              :             
      27              :             // Loop through the linear pedals and wire them back-to-back in the DAG
      28          852 :             for (auto& fx : dummy_effects_) {
      29          501 :                 fx->set_sample_rate(sample_rate_);
      30          501 :                 fx->reset();
      31          835 :                 int node_id = main_graph_.add_node(fx->name(), NodeRoutingType::StandardEffect, fx);
      32          501 :                 main_graph_.set_node_position(node_id, cursor_x, cursor_y);
      33          501 :                 cursor_x += 230.0f; // Standard pedals width + comfortable gap
      34              :                 
      35              :                 // Output routing will be handled dynamically at the end
      36              :                 
      37          501 :                 const auto& nodes = main_graph_.get_nodes();
      38          501 :                 if (nodes.empty()) continue;
      39          501 :                 const auto& current_node = nodes.back();
      40              :                 // Connect the previous pedal's output pin to this pedal's input pin
      41          501 :                 if (prev_output_pin != -1 && !current_node.input_pin_ids.empty()) {
      42          501 :                     main_graph_.add_link(prev_output_pin, current_node.input_pin_ids[0]);
      43          167 :                 }
      44              :                 
      45          501 :                 if (!current_node.output_pin_ids.empty()) {
      46          501 :                     prev_output_pin = current_node.output_pin_ids[0];
      47          167 :                 }
      48              :             }
      49              :             
      50              :             // Mark the last node in the linear chain as the Output so sound reaches the speakers
      51          351 :             if (!main_graph_.get_nodes().empty()) {
      52          351 :                 main_graph_.set_node_as_output(main_graph_.get_nodes().back().id, true);
      53          117 :             }
      54          117 :         } else {
      55              :             // 2. MODULAR MODE: Remove standard nodes that are no longer in dummy_effects_
      56          741 :             std::vector<int> nodes_to_remove;
      57         2859 :             for (const auto& node : main_graph_.get_nodes()) {
      58         2118 :                 if (node.routing_type == NodeRoutingType::StandardEffect && node.pedal != nullptr) {
      59         1371 :                     auto it = std::find(dummy_effects_.begin(), dummy_effects_.end(), node.pedal);
      60         1371 :                     if (it == dummy_effects_.end()) {
      61          315 :                         nodes_to_remove.push_back(node.id);
      62          105 :                     }
      63          457 :                 }
      64              :             }
      65         1056 :             for (int nid : nodes_to_remove) {
      66          315 :                 main_graph_.remove_node(nid);
      67              :             }
      68              :             
      69              :             // Add standard nodes for effects in dummy_effects_ that are not yet in the graph
      70         2196 :             for (int i = 0; i < static_cast<int>(dummy_effects_.size()); ++i) {
      71         1455 :                 auto& fx = dummy_effects_[i];
      72              :                 // Find if a node already exists for this effect
      73         1455 :                 bool exists = false;
      74         5130 :                 for (const auto& node : main_graph_.get_nodes()) {
      75         4731 :                     if (node.pedal == fx) {
      76          704 :                         exists = true;
      77          704 :                         break;
      78              :                     }
      79              :                 }
      80              :                 
      81         1455 :                 if (!exists) {
      82          399 :                     fx->set_sample_rate(sample_rate_);
      83          399 :                     fx->reset();
      84          665 :                     int node_id = main_graph_.add_node(fx->name(), NodeRoutingType::StandardEffect, fx);
      85          532 :                     if (std::string(fx->name()) == "Amp Sim") {
      86            6 :                         main_graph_.set_node_as_output(node_id, true);
      87            2 :                     }
      88          133 :                 }
      89          485 :             }
      90          741 :         }
      91         1092 :     }
      92              :     
      93              :     // 3. Compile the topology plan and push it to the hot audio thread safely
      94         1092 :     commit_graph_changes();
      95         1092 : }
      96              : 
      97          561 : void AudioEngine::add_effect(std::shared_ptr<Effect> fx) {
      98          561 :     dummy_effects_.push_back(fx);
      99          561 :     sync_graph_with_dummy_effects();
     100          561 : }
     101              : 
     102            3 : void AudioEngine::insert_effect(int index, std::shared_ptr<Effect> fx) {
     103            3 :     if (index >= 0 && index <= static_cast<int>(dummy_effects_.size())) {
     104            3 :         dummy_effects_.insert(dummy_effects_.begin() + index, fx);
     105            3 :         sync_graph_with_dummy_effects();
     106            1 :     }
     107            3 : }
     108              : 
     109          252 : void AudioEngine::remove_effect(int index) {
     110          252 :     if (index >= 0 && index < static_cast<int>(dummy_effects_.size())) {
     111          246 :         dummy_effects_.erase(dummy_effects_.begin() + index);
     112          246 :         sync_graph_with_dummy_effects();
     113           82 :     }
     114          252 : }
     115              : 
     116           72 : void AudioEngine::clear_effects() {
     117           72 :     dummy_effects_.clear();
     118           72 :     sync_graph_with_dummy_effects(true);
     119           72 : }
     120              : 
     121           24 : void AudioEngine::move_effect(int from, int to) {
     122           24 :     int size = static_cast<int>(dummy_effects_.size());
     123           24 :     if (from < 0 || from >= size || to < 0 || to >= size) {
     124            8 :         return;
     125              :     }
     126              :     
     127           18 :     if (from == to) return;
     128              : 
     129           15 :     auto fx = dummy_effects_[from];
     130           15 :     dummy_effects_.erase(dummy_effects_.begin() + from);
     131           15 :     dummy_effects_.insert(dummy_effects_.begin() + to, fx);
     132              :     
     133           15 :     sync_graph_with_dummy_effects();
     134           18 : }
     135              : 
     136          153 : void AudioEngine::restore_effects_state(std::vector<std::shared_ptr<Effect>> state) {
     137          153 :     dummy_effects_ = state;
     138          153 :     sync_graph_with_dummy_effects();
     139          153 : }
     140              : 
     141            3 : void AudioEngine::set_tuner_tap(std::shared_ptr<Effect> tap) {
     142            3 :     std::lock_guard<std::mutex> lock(effect_mutex_);
     143            3 :     tuner_tap_ = std::move(tap);
     144            3 :     if (tuner_tap_) {
     145            3 :         tuner_tap_->set_sample_rate(sample_rate_);
     146            3 :         tuner_tap_->reset();
     147            1 :     }
     148            3 :     topology_dirty_.store(true, std::memory_order_release);
     149            3 : }
     150              : 
     151            3 : void AudioEngine::clear_tuner_tap() {
     152            3 :     std::lock_guard<std::mutex> lock(effect_mutex_);
     153            3 :     tuner_tap_.reset();
     154            3 :     topology_dirty_.store(true, std::memory_order_release);
     155            3 : }
     156              : 
     157            9 : bool AudioEngine::has_tuner_tap() const {
     158            9 :     return tuner_tap_ != nullptr;
     159              : }
     160              : 
     161              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1