Line data Source code
1 : #pragma once
2 :
3 : #include "gui/commands/command_base.h"
4 : #include "audio/engine/audio_engine.h"
5 : #include "audio/effects/effect.h"
6 : #include <chrono>
7 :
8 : namespace Amplitron {
9 :
10 : /**
11 : * @brief Command that records a single parameter value change on an effect.
12 : *
13 : * Supports coalescing: rapid changes to the same parameter within 500 ms are
14 : * merged into one undo step. Uses shared_ptr<Effect> directly so it is robust
15 : * against effect chain reordering.
16 : */
17 6 : class ParameterChangeCommand : public Command {
18 : public:
19 : /**
20 : * @brief Construct a ParameterChangeCommand.
21 : * @param engine Reference to the AudioEngine.
22 : * @param effect Shared pointer to the target effect.
23 : * @param param_index Index of the parameter within the effect's param list.
24 : * @param old_value Value before the change (used by undo).
25 : * @param new_value Value after the change (used by execute).
26 : */
27 52 : ParameterChangeCommand(AudioEngine& engine, std::shared_ptr<Effect> effect,
28 : int param_index, float old_value, float new_value)
29 52 : : engine_(engine), effect_(std::move(effect)),
30 63 : param_index_(param_index), old_value_(old_value), new_value_(new_value) {}
31 :
32 : /** @brief Set the parameter to new_value_. */
33 12 : bool execute() override {
34 12 : auto& params = effect_->params();
35 12 : if (param_index_ >= 0 && param_index_ < static_cast<int>(params.size())) {
36 6 : params[param_index_].value = new_value_;
37 6 : int idx = -1;
38 6 : auto& fx = engine_.effects();
39 24 : for (int i = 0; i < static_cast<int>(fx.size()); ++i) {
40 18 : if (fx[i] == effect_) { idx = i; break; }
41 6 : }
42 6 : if (idx >= 0) engine_.push_param_change(idx, param_index_, new_value_);
43 2 : }
44 12 : return true;
45 : }
46 :
47 : /** @brief Restore the parameter to old_value_. */
48 12 : void undo() override {
49 12 : auto& params = effect_->params();
50 12 : if (param_index_ >= 0 && param_index_ < static_cast<int>(params.size())) {
51 6 : params[param_index_].value = old_value_;
52 6 : int idx = -1;
53 6 : auto& fx = engine_.effects();
54 24 : for (int i = 0; i < static_cast<int>(fx.size()); ++i) {
55 18 : if (fx[i] == effect_) { idx = i; break; }
56 6 : }
57 6 : if (idx >= 0) engine_.push_param_change(idx, param_index_, old_value_);
58 2 : }
59 12 : }
60 :
61 : /** @brief Return "Change Parameter". */
62 0 : const char* description() const override { return "Change Parameter"; }
63 :
64 : /**
65 : * @brief Attempt to coalesce @p other into this command.
66 : *
67 : * Merges if @p other is a ParameterChangeCommand targeting the same
68 : * effect and parameter index within 500 ms. On success, this command's
69 : * new_value_ and timestamp are updated; old_value_ is preserved.
70 : *
71 : * @return true if @p other was absorbed.
72 : */
73 21 : bool merge_with(const Command& other) override {
74 21 : auto* pc = dynamic_cast<const ParameterChangeCommand*>(&other);
75 21 : if (!pc) return false;
76 18 : if (pc->effect_.get() != effect_.get() || pc->param_index_ != param_index_)
77 6 : return false;
78 :
79 9 : auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(
80 9 : pc->timestamp_ - timestamp_);
81 9 : if (dt.count() > 500) return false;
82 :
83 6 : new_value_ = pc->new_value_;
84 6 : timestamp_ = pc->timestamp_;
85 6 : return true;
86 7 : }
87 :
88 : /** @brief Accessor for the target effect. */
89 3 : std::shared_ptr<Effect> effect() const { return effect_; }
90 :
91 : /** @brief Index of the parameter being changed. */
92 5 : int param_index() const { return param_index_; }
93 :
94 : /** @brief Value before the change. */
95 : float old_value() const { return old_value_; }
96 :
97 : /** @brief Value after the change. */
98 : float new_value() const { return new_value_; }
99 :
100 : private:
101 : AudioEngine& engine_;
102 : std::shared_ptr<Effect> effect_;
103 : int param_index_;
104 : float old_value_;
105 : float new_value_;
106 : };
107 :
108 : } // namespace Amplitron
|