LCOV - code coverage report
Current view: top level - src/gui/pedalboard - pedal_widget.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 89.8 % 137 123
Test Date: 2026-06-03 09:13:19 Functions: 45.5 % 11 5

            Line data    Source code
       1              : #include "gui/pedalboard/pedal_widget.h"
       2              : #include "gui/views/gui_midi.h"
       3              : #include "audio/engine/audio_engine.h"
       4              : #include "gui/theme/theme.h"
       5              : #include "gui/commands/command.h"
       6              : #include "gui/commands/command_history.h"
       7              : #include "audio/effects/tuner.h"
       8              : #include "audio/effects/amp_simulator.h"
       9              : #include "gui/dialogs/file_dialog.h"
      10              : #include "gui/components/led.h"
      11              : #include "gui/components/footswitch.h"
      12              : #include "gui/components/screen.h"
      13              : #include <cstring>
      14              : #include <cmath>
      15              : 
      16              : namespace Amplitron {
      17              : 
      18              : /** @brief Construct PedalWidget and look up color scheme for the effect type. */
      19          172 : PedalWidget::PedalWidget(AudioEngine& engine, std::shared_ptr<Effect> effect, int index)
      20          172 :     : engine_(engine), effect_(std::move(effect)), index_(index) {
      21          129 :     assign_colors();
      22          129 : }
      23              : 
      24              : /** @brief Look up pedal_color_ and led_color_ from the theme's effect color table. */
      25          129 : void PedalWidget::assign_colors() {
      26          129 :     const auto* entry = get_effect_color(effect_->name());
      27          129 :     pedal_color_ = entry->pedal_color;
      28          129 :     led_color_ = entry->led_color;
      29          129 : }
      30              : 
      31              : /** @brief Render the full pedal widget (body, knobs, switch, LED). @return true if remove requested. */
      32          171 : bool PedalWidget::render(float zoom) {
      33          171 :     bool should_remove = false;
      34              : 
      35          171 :     ImGui::PushID(index_);
      36              : 
      37          171 :     bool is_amp = (std::strcmp(effect_->name(), "Amp Sim") == 0);
      38          171 :     bool is_mb_comp = (std::strcmp(effect_->name(), "MultiBand Compressor") == 0);
      39          171 :     bool enabled = effect_->is_enabled();
      40          171 :     bool is_looper = !is_amp && (std::strcmp(effect_->name(), "Looper") == 0);
      41              : 
      42          171 :     float pedal_width = is_mb_comp ? (Theme::PEDAL_WIDTH * 2.2f * zoom) : (Theme::PEDAL_WIDTH * zoom);
      43          171 :     float pedal_height = Theme::PEDAL_HEIGHT * zoom;
      44              : 
      45          171 :     ImVec2 cursor = ImGui::GetCursorScreenPos();
      46          171 :     ImDrawList* dl = ImGui::GetWindowDrawList();
      47              : 
      48              :     // Pedal body
      49          171 :     ImVec2 p0 = cursor;
      50          171 :     ImVec2 p1 = ImVec2(cursor.x + pedal_width, cursor.y + pedal_height);
      51              : 
      52              :     // Shadow
      53          228 :     dl->AddRectFilled(
      54          228 :         ImVec2(p0.x + 4 * zoom, p0.y + 4 * zoom),
      55          171 :         ImVec2(p1.x + 4 * zoom, p1.y + 4 * zoom),
      56           57 :         Theme::PEDAL_SHADOW, Theme::ROUNDING_MD * zoom
      57              :     );
      58              : 
      59          171 :     if (is_amp) {
      60           12 :         render_amp_cabinet(dl, p0, p1, pedal_width, pedal_height, zoom);
      61            4 :     } else {
      62          159 :         render_standard_pedal(dl, p0, p1, pedal_width, enabled, zoom);
      63              :     }
      64              : 
      65              :     // Dim the pedal body when bypassed so the inactive state is immediately obvious
      66          171 :     if (!enabled && !is_amp) {
      67            6 :         dl->AddRectFilled(p0, p1, Theme::PEDAL_BYPASS_OVERLAY, Theme::ROUNDING_MD * zoom);
      68            2 :     }
      69              : 
      70              :     // --- Tuner custom display ---
      71          171 :     bool is_tuner = !is_amp && (std::strcmp(effect_->name(), "Tuner") == 0);
      72          115 :     if (is_tuner) {
      73            3 :         ScreenProps props;
      74            3 :         props.type = ScreenType::Tuner;
      75            3 :         props.effect = effect_;
      76            3 :         props.index = index_;
      77            3 :         props.engine = &engine_;
      78            3 :         props.gui_midi = gui_midi_;
      79            5 :         props.on_commit_param_change = [this](int pi, float old_val, float new_val) {
      80            0 :             commit_param_change(pi, old_val, new_val);
      81            2 :         };
      82            3 :         ScreenComponent::render(dl, p0, pedal_width, zoom, props);
      83            3 :     }
      84              : 
      85              :     // --- IR Cabinet custom display ---
      86          171 :     bool is_ir_cab = !is_amp && (std::strcmp(effect_->name(), "Cabinet") == 0);
      87          115 :     if (is_ir_cab) {
      88            3 :         ScreenProps props;
      89            3 :         props.type = ScreenType::Cabinet;
      90            3 :         props.effect = effect_;
      91            3 :         props.index = index_;
      92            3 :         props.engine = &engine_;
      93            3 :         props.gui_midi = gui_midi_;
      94            5 :         props.on_commit_param_change = [this](int pi, float old_val, float new_val) {
      95            0 :             commit_param_change(pi, old_val, new_val);
      96            2 :         };
      97            3 :         ScreenComponent::render(dl, p0, pedal_width, zoom, props);
      98            3 :     }
      99              : 
     100          171 :     if (is_looper) {
     101            3 :         ScreenProps props;
     102            3 :         props.type = ScreenType::Looper;
     103            3 :         props.effect = effect_;
     104            3 :         props.index = index_;
     105            3 :         props.engine = &engine_;
     106            3 :         props.gui_midi = gui_midi_;
     107            5 :         props.on_commit_param_change = [this](int pi, float old_val, float new_val) {
     108            0 :             commit_param_change(pi, old_val, new_val);
     109            2 :         };
     110            3 :         ScreenComponent::render(dl, p0, pedal_width, zoom, props);
     111          171 :     } else if (is_mb_comp) {
     112            3 :         ScreenProps props;
     113            3 :         props.type = ScreenType::MultiBandCompressor;
     114            3 :         props.effect = effect_;
     115            3 :         props.index = index_;
     116            3 :         props.engine = &engine_;
     117            3 :         props.gui_midi = gui_midi_;
     118            5 :         props.on_commit_param_change = [this](int pi, float old_val, float new_val) {
     119            0 :             commit_param_change(pi, old_val, new_val);
     120            2 :         };
     121            3 :         ScreenComponent::render(dl, p0, pedal_width, zoom, props);
     122            3 :     } else {
     123          165 :         render_knobs(dl, p0, pedal_width, is_amp, is_tuner, is_ir_cab, zoom);
     124              :     }
     125              : 
     126          171 :     render_footswitch_and_extras(dl, p0, p1, pedal_width, pedal_height, is_amp, enabled, should_remove, zoom);
     127              : 
     128          171 :     ImGui::PopID();
     129          171 :     return should_remove;
     130            0 : }
     131              : 
     132          162 : void PedalWidget::render_standard_pedal(ImDrawList* dl, ImVec2 p0, ImVec2 p1, float pedal_width, bool enabled, float zoom) {
     133              :     // ========== STANDARD PEDAL VISUAL ==========
     134          162 :     ImU32 body_color = ImGui::ColorConvertFloat4ToU32(pedal_color_);
     135          162 :     dl->AddRectFilled(p0, p1, body_color, Theme::ROUNDING_MD * zoom);
     136          162 :     dl->AddRect(p0, p1, Theme::PEDAL_BORDER, Theme::ROUNDING_MD * zoom, 0, 2.0f * zoom);
     137              : 
     138              :     // Metallic top plate
     139          162 :     ImVec2 plate_p0 = ImVec2(p0.x + 8 * zoom, p0.y + 8 * zoom);
     140          162 :     ImVec2 plate_p1 = ImVec2(p1.x - 8 * zoom, p0.y + 45 * zoom);
     141          216 :     dl->AddRectFilled(plate_p0, plate_p1,
     142           54 :         Theme::PEDAL_PLATE, Theme::ROUNDING_SM * zoom);
     143              : 
     144              :     // Effect name
     145          162 :     ImGui::SetCursorScreenPos(ImVec2(p0.x + 12 * zoom, p0.y + 14 * zoom));
     146          162 :     ImGui::PushStyleColor(ImGuiCol_Text, Theme::TextPrimary());
     147          162 :     ImGui::Text("%s", effect_->name());
     148          162 :     ImGui::PopStyleColor();
     149              : 
     150              :     // Reusable status LED indicator
     151          162 :     float led_x = p0.x + pedal_width - 25 * zoom;
     152          162 :     float led_y = p0.y + 20 * zoom;
     153              : 
     154          162 :     LedProps led_props;
     155          162 :     led_props.enabled = enabled;
     156          162 :     led_props.led_color = led_color_;
     157          162 :     led_props.tooltip = enabled ? "Effect active" : "Effect bypassed";
     158              : 
     159           54 :     char led_id[64];
     160          162 :     std::snprintf(led_id, sizeof(led_id), "##led_%d", index_);
     161          162 :     LedComponent::render(led_id, led_props, zoom, ImVec2(led_x, led_y));
     162          162 : }
     163              : 
     164          171 : void PedalWidget::render_footswitch_and_extras(ImDrawList* dl, ImVec2 p0, ImVec2 p1, float pedal_width, float pedal_height, bool is_amp, bool enabled, bool& should_remove, float zoom) {
     165           57 :     (void)p1;
     166          114 :     (void)should_remove;
     167           57 :     (void)dl;
     168          171 :     bool is_looper = !is_amp && (std::strcmp(effect_->name(), "Looper") == 0);
     169              : 
     170              :     // Footswitch (toggle on/off) — amps are always on, no footswitch
     171          166 :     if (!is_amp && !is_looper) {
     172          156 :         float switch_y = p0.y + pedal_height - Theme::SWITCH_BOTTOM_OFFSET * zoom;
     173          156 :         float switch_x = p0.x + (pedal_width - 50 * zoom) / 2;
     174          156 :         ImVec2 sw_center = ImVec2(switch_x + 25 * zoom, switch_y + 15 * zoom);
     175              : 
     176          156 :         FootswitchProps fs_props;
     177          156 :         fs_props.enabled = enabled;
     178          156 :         fs_props.tooltip_prefix = "";
     179          260 :         fs_props.on_clicked = [this, enabled]() {
     180            0 :             bool new_enabled = !enabled;
     181            0 :             effect_->set_enabled(new_enabled);
     182            0 :             engine_.push_effect_enabled(index_, new_enabled ? 1.0f : 0.0f);
     183          104 :         };
     184              : 
     185           52 :         char sw_id[64];
     186          156 :         std::snprintf(sw_id, sizeof(sw_id), "##switch_%d", index_);
     187          156 :         FootswitchComponent::render(sw_id, fs_props, zoom, sw_center);
     188          156 :     }
     189          171 : }
     190              : 
     191            0 : void PedalWidget::commit_param_change(int param_index, float old_val, float new_val) {
     192            0 :     if (!history_) return;
     193            0 :     auto cmd = std::make_unique<ParameterChangeCommand>(
     194            0 :         engine_, effect_, param_index, old_val, new_val);
     195            0 :     history_->push_executed(std::move(cmd));
     196            0 : }
     197              : 
     198              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1