Line data Source code
1 : #pragma once
2 :
3 : #include <cstring>
4 : #include <memory>
5 : #include <unordered_map>
6 : #include <vector>
7 :
8 : #include "audio/effects/core/effect.h"
9 : #include "audio/engine/audio_graph.h"
10 :
11 : namespace Amplitron {
12 :
13 1468 : class INodeProcessor {
14 : public:
15 2936 : virtual ~INodeProcessor() = default;
16 : virtual void process(const float* input, float* output, int num_samples) = 0;
17 : };
18 :
19 : class StandardEffectProcessor : public INodeProcessor {
20 : public:
21 932 : explicit StandardEffectProcessor(std::shared_ptr<Effect> pedal) : pedal_(std::move(pedal)) {}
22 39 : void process(const float* input, float* output, int num_samples) override {
23 39 : std::memcpy(output, input, static_cast<size_t>(num_samples) * sizeof(float));
24 39 : if (pedal_) {
25 39 : pedal_->process(output, num_samples);
26 13 : }
27 39 : }
28 :
29 : private:
30 : std::shared_ptr<Effect> pedal_;
31 : };
32 :
33 1235 : class PassthroughProcessor : public INodeProcessor {
34 : public:
35 3177 : void process(const float* input, float* output, int num_samples) override {
36 3177 : std::memcpy(output, input, static_cast<size_t>(num_samples) * sizeof(float));
37 3177 : }
38 : };
39 :
40 : class AudioGraphExecutor {
41 : public:
42 : friend class AudioEngine;
43 :
44 : AudioGraphExecutor();
45 3237 : ~AudioGraphExecutor() = default;
46 :
47 : // Allocate the memory pool ahead of time (Call this during engine initialization)
48 : void prepare(int sample_rate, int max_block_size, int max_nodes = 32);
49 :
50 : // Translates the AudioGraph into a flat, allocation-free execution array
51 : // Call this from the UI thread whenever connections change!
52 : void compile(const AudioGraph& graph);
53 :
54 : // Broadcast tempo/BPM down to all active nodes in the execution plan
55 : void update_transport_state(float bpm);
56 :
57 : // Hot-path processing (Strictly allocation-free and lock-free)
58 : // Adjust the pedal->process signature if your pedals process strictly in-place
59 : void process(const float* input, float* output, int num_samples);
60 : void update_mixer_gain(int node_id, int pin_index, float gain);
61 :
62 21 : std::shared_ptr<Effect> get_effect_by_node_id(int node_id) const {
63 39 : for (const auto& step : execution_plan_) {
64 18 : if (step.node_id == node_id) {
65 0 : return step.pedal;
66 : }
67 : }
68 21 : return nullptr;
69 7 : }
70 :
71 : private:
72 : int sample_rate_ = 48000;
73 : int max_block_size_ = 512;
74 : int max_nodes_ = 32;
75 :
76 : struct InputSource {
77 : int buffer_index; // The pool index to read from
78 : float gain = 1.0f;
79 : int pin_index = 0;
80 : };
81 :
82 2936 : struct NodeExecutionStep {
83 : int node_id;
84 : int buffer_index; // The pool index this node writes to
85 : NodeRoutingType type;
86 : std::shared_ptr<Effect> pedal;
87 : std::vector<InputSource> input_sources; // Which buffers to sum together for the input
88 : std::unique_ptr<INodeProcessor> processor; // Polymorphic node executor
89 1468 : bool is_graph_input = false;
90 1468 : bool is_graph_output = false;
91 1468 : bool is_sink = false;
92 : };
93 :
94 : std::vector<NodeExecutionStep> execution_plan_;
95 :
96 : bool any_explicit_input_ = false;
97 : int fallback_input_node_id_ = -1;
98 :
99 : // Pre-allocated memory for routing parallel signal streams: [node_capacity][max_block_size]
100 : std::vector<std::vector<float>> buffer_pool_;
101 :
102 : // A dedicated temporary buffer for summing multiple incoming signals
103 : std::vector<float> sum_buffer_;
104 : };
105 :
106 : } // namespace Amplitron
|