Line data Source code
1 : #pragma once
2 :
3 : #include <utility>
4 : #include <vector>
5 :
6 : #include "audio/effects/core/effect.h"
7 : #include "audio/engine/i_audio_engine.h"
8 : #include "gui/commands/command_base.h"
9 :
10 : namespace Amplitron {
11 :
12 : /**
13 : * @brief Command that captures the full effect-chain state before and after a
14 : * preset load, enabling undo/redo of the entire preset switch.
15 : *
16 : * Uses AudioEngine::restore_effects_state() so the effect chain is replaced
17 : * atomically under the engine's mutex.
18 : */
19 : class LoadPresetCommand : public Command {
20 : public:
21 : /**
22 : * @brief Snapshot of a single effect's configuration at a point in time.
23 : */
24 231 : struct EffectSnapshot {
25 : std::shared_ptr<Effect> effect; ///< The effect instance.
26 : bool enabled; ///< Whether the effect was enabled.
27 : float mix; ///< Dry/wet mix level.
28 : std::vector<float> param_values; ///< Ordered parameter values.
29 : };
30 :
31 : /**
32 : * @brief Construct a LoadPresetCommand.
33 : * @param engine Reference to the audio engine.
34 : * @param before_state Effect chain snapshot before the load.
35 : * @param before_input_gain Input gain before the load.
36 : * @param before_output_gain Output gain before the load.
37 : * @param after_state Effect chain snapshot after the load.
38 : * @param after_input_gain Input gain after the load.
39 : * @param after_output_gain Output gain after the load.
40 : */
41 36 : LoadPresetCommand(IAudioEngine &engine, std::vector<EffectSnapshot> before_state,
42 : float before_input_gain, float before_output_gain,
43 : std::vector<EffectSnapshot> after_state, float after_input_gain,
44 : float after_output_gain)
45 45 : : engine_(engine),
46 27 : before_state_(std::move(before_state)),
47 27 : before_input_gain_(before_input_gain),
48 27 : before_output_gain_(before_output_gain),
49 27 : after_state_(std::move(after_state)),
50 27 : after_input_gain_(after_input_gain),
51 45 : after_output_gain_(after_output_gain) {}
52 :
53 : /** @brief Restore the after-load state (redo). */
54 3 : bool execute() override {
55 3 : apply_state(after_state_, after_input_gain_, after_output_gain_);
56 3 : return true;
57 : }
58 :
59 : /** @brief Restore the before-load state (undo). */
60 3 : void undo() override { apply_state(before_state_, before_input_gain_, before_output_gain_); }
61 :
62 : /** @brief Return "Load Preset". */
63 0 : const char *description() const override { return "Load Preset"; }
64 :
65 : private:
66 : /**
67 : * @brief Replace the engine's effect chain and gains with the given snapshot.
68 : * @param state Vector of EffectSnapshot to restore.
69 : * @param input_gain Input gain to set.
70 : * @param output_gain Output gain to set.
71 : */
72 6 : void apply_state(const std::vector<EffectSnapshot> &state, float input_gain,
73 : float output_gain) {
74 : // Prepare effect list with restored params before swapping
75 6 : std::vector<std::shared_ptr<Effect>> new_effects;
76 6 : new_effects.reserve(state.size());
77 15 : for (auto &snap : state) {
78 9 : snap.effect->set_enabled(snap.enabled);
79 9 : snap.effect->set_mix(snap.mix);
80 9 : auto ¶ms = snap.effect->params();
81 59 : for (int i = 0; i < static_cast<int>(params.size()) &&
82 30 : i < static_cast<int>(snap.param_values.size());
83 10 : ++i) {
84 30 : params[i].value = snap.param_values[i];
85 10 : }
86 9 : new_effects.push_back(snap.effect);
87 : }
88 :
89 : // Atomic swap under engine lock — audio thread never sees half-applied
90 : // state
91 6 : engine_.restore_effects_state(std::move(new_effects));
92 :
93 6 : engine_.set_input_gain(input_gain);
94 6 : engine_.set_output_gain(output_gain);
95 6 : }
96 :
97 : IAudioEngine &engine_;
98 : std::vector<EffectSnapshot> before_state_;
99 : float before_input_gain_;
100 : float before_output_gain_;
101 : std::vector<EffectSnapshot> after_state_;
102 : float after_input_gain_;
103 : float after_output_gain_;
104 : };
105 :
106 : /**
107 : * @brief Command that applies a stored in-session snapshot (A/B/C/D) to the
108 : * engine.
109 : *
110 : * Semantically identical to LoadPresetCommand but describes itself as
111 : * "Recall Snapshot" in the undo/redo menu. Constructed from a pair of
112 : * SnapshotManager::BoardSnapshot values (before/after) by GuiSnapshots.
113 : */
114 : class RecallSnapshotCommand : public Command {
115 : public:
116 : using EffectSnapshot = LoadPresetCommand::EffectSnapshot;
117 :
118 36 : RecallSnapshotCommand(IAudioEngine &engine, std::vector<EffectSnapshot> before_effects,
119 : float before_input_gain, float before_output_gain,
120 : std::vector<EffectSnapshot> after_effects, float after_input_gain,
121 : float after_output_gain)
122 45 : : engine_(engine),
123 27 : before_effects_(std::move(before_effects)),
124 27 : before_input_gain_(before_input_gain),
125 27 : before_output_gain_(before_output_gain),
126 27 : after_effects_(std::move(after_effects)),
127 27 : after_input_gain_(after_input_gain),
128 45 : after_output_gain_(after_output_gain) {}
129 :
130 : /** @brief Restore the after-recall state (redo). */
131 27 : bool execute() override {
132 27 : apply_state(after_effects_, after_input_gain_, after_output_gain_);
133 27 : return true;
134 : }
135 :
136 : /** @brief Restore the before-recall state (undo). */
137 6 : void undo() override { apply_state(before_effects_, before_input_gain_, before_output_gain_); }
138 :
139 : /** @brief Return "Recall Snapshot". */
140 3 : const char *description() const override { return "Recall Snapshot"; }
141 :
142 : private:
143 33 : void apply_state(const std::vector<EffectSnapshot> &state, float input_gain,
144 : float output_gain) {
145 33 : std::vector<std::shared_ptr<Effect>> new_effects;
146 33 : new_effects.reserve(state.size());
147 81 : for (const auto &snap : state) {
148 48 : snap.effect->set_enabled(snap.enabled);
149 48 : snap.effect->set_mix(snap.mix);
150 48 : auto ¶ms = snap.effect->params();
151 318 : for (int i = 0; i < static_cast<int>(params.size()) &&
152 162 : i < static_cast<int>(snap.param_values.size());
153 54 : ++i) {
154 162 : params[i].value = snap.param_values[i];
155 54 : }
156 48 : new_effects.push_back(snap.effect);
157 : }
158 33 : engine_.restore_effects_state(std::move(new_effects));
159 33 : engine_.set_input_gain(input_gain);
160 33 : engine_.set_output_gain(output_gain);
161 33 : }
162 :
163 : IAudioEngine &engine_;
164 : std::vector<EffectSnapshot> before_effects_;
165 : float before_input_gain_;
166 : float before_output_gain_;
167 : std::vector<EffectSnapshot> after_effects_;
168 : float after_input_gain_;
169 : float after_output_gain_;
170 : };
171 :
172 : } // namespace Amplitron
|