LCOV - code coverage report
Current view: top level - src/gui/commands - command_param.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 85.2 % 61 52
Test Date: 2026-06-07 15:51:50 Functions: 85.7 % 7 6

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

Generated by: LCOV version 2.0-1