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 % 117 117
Test Date: 2026-06-07 15:51:50 Functions: 100.0 % 10 10

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

Generated by: LCOV version 2.0-1