Line data Source code
1 : #pragma once
2 :
3 : #include <atomic>
4 : #include <cstring>
5 : #include <memory>
6 : #include <nlohmann/json.hpp>
7 : #include <string>
8 : #include <vector>
9 :
10 : #include "audio/effects/core/effect_param.h"
11 : #include "audio/effects/core/i_metadata.h"
12 : #include "audio/effects/core/i_parameterizable.h"
13 : #include "audio/effects/core/i_processor.h"
14 : #include "audio/effects/core/i_serializable.h"
15 : #include "common.h"
16 :
17 : namespace Amplitron {
18 :
19 : // Common interface for all mono/stereo audio effects in the pedal chain.
20 1172 : class Effect : public IProcessor, public IParameterizable, public ISerializable, public IMetadata {
21 : public:
22 1251 : virtual ~Effect() = default;
23 :
24 : // Process a mono buffer in place.
25 : virtual void process(float* buffer, int num_samples) override = 0;
26 :
27 : // Stereo processing. Default fans mono left channel to both outputs.
28 : // Stereo-capable effects override this to produce true stereo.
29 3 : virtual void process_stereo(float* left, float* right, int num_samples) override {
30 3 : process(left, num_samples);
31 3 : std::memcpy(right, left, static_cast<size_t>(num_samples) * sizeof(float));
32 3 : }
33 :
34 : // Update the processing sample rate before audio starts or after device changes.
35 2103 : virtual void set_sample_rate(int sample_rate) override { sample_rate_ = sample_rate; }
36 :
37 : // Clear delay lines, envelopes, filters, and other effect state.
38 : virtual void reset() override = 0;
39 :
40 : // Tempo broadcast receiver.
41 42 : virtual void set_transport_state(float /*bpm*/) {}
42 :
43 : // Display name used by the pedal board and preset serialization.
44 : virtual const char* name() const override = 0;
45 :
46 0 : virtual const char* type_id() const override { return name(); }
47 :
48 : // Mutable parameter list used by controls and automation.
49 : virtual std::vector<EffectParam>& params() override = 0;
50 : virtual const std::vector<EffectParam>& params() const override = 0;
51 :
52 636 : void set_enabled(bool enabled) { enabled_ = enabled; }
53 1459 : bool is_enabled() const { return enabled_; }
54 :
55 525 : void set_mix(float mix) { mix_.store(clamp(mix, 0.0f, 1.0f), std::memory_order_relaxed); }
56 1018 : float get_mix() const { return mix_.load(std::memory_order_relaxed); }
57 :
58 : virtual std::shared_ptr<Effect> clone() const;
59 :
60 3 : std::vector<std::string> get_param_names() override {
61 3 : std::vector<std::string> names;
62 15 : for (const auto& p : params()) {
63 12 : names.push_back(p.name);
64 : }
65 3 : return names;
66 1 : }
67 :
68 15 : float get_param_value(const std::string& name) override {
69 24 : for (const auto& p : params()) {
70 21 : if (p.name == name) {
71 12 : return p.value;
72 : }
73 : }
74 2 : return 0.0f;
75 5 : }
76 :
77 9 : void set_param_by_name(const std::string& name, float value) override {
78 9 : for (auto& p : params()) {
79 9 : if (p.name == name) {
80 9 : p.value = clamp(value, p.min_val, p.max_val);
81 9 : return;
82 : }
83 : }
84 3 : }
85 :
86 3 : virtual const char* get_display_name() const override { return name(); }
87 :
88 : // --- AUTOMATED SERIALIZATION LOGIC ---
89 : // These methods automatically handle saving/loading for any effect
90 : // that uses the EffectParam vector.
91 :
92 9 : virtual nlohmann::json get_params() const override {
93 9 : nlohmann::json j;
94 9 : const auto& p_list = params();
95 36 : for (const auto& p : p_list) {
96 36 : j[p.name] = p.value;
97 : }
98 9 : j["enabled"] = enabled_.load();
99 9 : j["mix"] = mix_.load(std::memory_order_relaxed);
100 9 : return j;
101 3 : }
102 :
103 12 : virtual void set_params(const nlohmann::json& j) override {
104 12 : if (j.contains("enabled")) enabled_.store(j["enabled"].get<bool>());
105 12 : if (j.contains("mix")) mix_.store(j["mix"].get<float>(), std::memory_order_relaxed);
106 :
107 12 : auto& p_list = params();
108 48 : for (auto& p : p_list) {
109 36 : if (j.contains(p.name)) {
110 27 : p.value = j[p.name];
111 9 : }
112 : }
113 12 : }
114 :
115 : protected:
116 586 : int sample_rate_ = DEFAULT_SAMPLE_RATE;
117 586 : std::atomic<bool> enabled_{true};
118 586 : std::atomic<float> mix_{1.0f};
119 :
120 : // Wet/dry mix helper
121 6 : void apply_mix(const float* dry, float* wet, int num_samples) {
122 6 : float current_mix = mix_.load(std::memory_order_relaxed);
123 6 : if (current_mix >= 1.0f) return;
124 15 : for (int i = 0; i < num_samples; ++i) {
125 12 : wet[i] = dry[i] * (1.0f - current_mix) + wet[i] * current_mix;
126 4 : }
127 2 : }
128 : };
129 :
130 : } // namespace Amplitron
|