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