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