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
|