Line data Source code
1 : #include "gui/views/gui_tuner.h"
2 : #include "gui/theme/theme.h"
3 : #include "common.h"
4 : #include <imgui.h>
5 : #include <cmath>
6 : #include <cstdio>
7 :
8 : namespace Amplitron {
9 :
10 9 : void GuiTuner::render(bool& show) {
11 9 : ImGui::SetNextWindowSize(ImVec2(360, 320), ImGuiCond_FirstUseEver);
12 9 : bool open = show;
13 9 : if (!ImGui::Begin("Chromatic Tuner", &open)) {
14 0 : if (!open) show = false;
15 0 : ImGui::End();
16 2 : return;
17 : }
18 9 : if (!open) {
19 3 : show = false;
20 3 : ImGui::End();
21 3 : return;
22 : }
23 :
24 :
25 6 : const TunerProps& p = props_;
26 6 : ImDrawList* dl = ImGui::GetWindowDrawList();
27 6 : float win_w = ImGui::GetContentRegionAvail().x;
28 :
29 6 : if (p.has_signal && p.note_idx >= 0 && p.note_name_fn) {
30 : // ── Note name (large, centered) ──
31 1 : char note_buf[16];
32 3 : std::snprintf(note_buf, sizeof(note_buf), "%s%d", p.note_name_fn(p.note_idx), p.octave);
33 3 : ImVec2 note_size = ImGui::CalcTextSize(note_buf);
34 3 : float scale = 3.0f;
35 3 : float note_w = note_size.x * scale;
36 3 : ImVec2 note_pos = ImGui::GetCursorScreenPos();
37 3 : note_pos.x += (win_w - note_w) * 0.5f;
38 4 : dl->AddText(ImGui::GetFont(), ImGui::GetFontSize() * scale,
39 1 : note_pos, Theme::TEXT_PRIMARY, note_buf);
40 3 : ImGui::Dummy(ImVec2(0, ImGui::GetFontSize() * scale + 8));
41 :
42 : // ── Cents indicator ──
43 3 : float abs_cents = std::fabs(p.cents);
44 3 : ImVec4 cents_col = (abs_cents < 2.0f)
45 2 : ? ImVec4(0.2f, 0.9f, 0.3f, 1.0f)
46 1 : : (abs_cents < 15.0f)
47 3 : ? ImVec4(0.9f, 0.8f, 0.2f, 1.0f)
48 2 : : ImVec4(0.9f, 0.2f, 0.2f, 1.0f);
49 1 : char cents_buf[32];
50 3 : std::snprintf(cents_buf, sizeof(cents_buf), "%+.1f cents", p.cents);
51 3 : ImVec2 cents_size = ImGui::CalcTextSize(cents_buf);
52 3 : ImGui::SetCursorPosX((win_w - cents_size.x) * 0.5f);
53 3 : ImGui::TextColored(cents_col, "%s", cents_buf);
54 :
55 3 : ImGui::Spacing();
56 :
57 : // ── Cents deviation bar ──
58 3 : float bar_w = win_w - 40;
59 3 : float bar_h = 14;
60 3 : ImVec2 bar_pos = ImGui::GetCursorScreenPos();
61 3 : bar_pos.x += 20;
62 3 : dl->AddRectFilled(bar_pos, ImVec2(bar_pos.x + bar_w, bar_pos.y + bar_h),
63 : Theme::KNOB_BG, 4.0f);
64 :
65 3 : float cx = bar_pos.x + bar_w * 0.5f;
66 3 : dl->AddLine(ImVec2(cx, bar_pos.y - 2), ImVec2(cx, bar_pos.y + bar_h + 2),
67 : Theme::TEXT_DIM, 2.0f);
68 :
69 3 : float needle_norm = clamp(p.cents / 50.0f, -1.0f, 1.0f);
70 3 : float needle_x = cx + needle_norm * (bar_w * 0.5f);
71 3 : ImU32 needle_col = ImGui::ColorConvertFloat4ToU32(cents_col);
72 5 : dl->AddRectFilled(ImVec2(needle_x - 4, bar_pos.y - 3),
73 3 : ImVec2(needle_x + 4, bar_pos.y + bar_h + 3),
74 1 : needle_col, 3.0f);
75 3 : ImGui::Dummy(ImVec2(0, bar_h + 12));
76 :
77 : // ── Frequency ──
78 1 : char freq_buf[32];
79 3 : std::snprintf(freq_buf, sizeof(freq_buf), "%.1f Hz", p.freq);
80 3 : float freq_w = ImGui::CalcTextSize(freq_buf).x;
81 3 : ImGui::SetCursorPosX((win_w - freq_w) * 0.5f);
82 3 : ImGui::TextColored(Theme::TextSecondary(), "%s", freq_buf);
83 :
84 1 : } else {
85 : // ── No signal ──
86 3 : const char* dash = "---";
87 3 : ImVec2 dash_size = ImGui::CalcTextSize(dash);
88 3 : float scale = 3.0f;
89 3 : ImVec2 pos = ImGui::GetCursorScreenPos();
90 3 : pos.x += (win_w - dash_size.x * scale) * 0.5f;
91 3 : dl->AddText(ImGui::GetFont(), ImGui::GetFontSize() * scale, pos, Theme::TEXT_DIM, dash);
92 3 : ImGui::Dummy(ImVec2(0, ImGui::GetFontSize() * scale + 8));
93 :
94 3 : const char* waiting = "Play a note...";
95 3 : ImVec2 wt_size = ImGui::CalcTextSize(waiting);
96 3 : ImGui::SetCursorPosX((win_w - wt_size.x) * 0.5f);
97 3 : ImGui::TextColored(Theme::TextDim(), "%s", waiting);
98 3 : ImGui::Dummy(ImVec2(0, 40));
99 : }
100 :
101 6 : ImGui::Spacing();
102 6 : ImGui::Separator();
103 6 : ImGui::Spacing();
104 :
105 : // ── Mute toggle ──
106 6 : if (p.mute_on) {
107 0 : ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.15f, 0.15f, 1.0f));
108 0 : ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.75f, 0.2f, 0.2f, 1.0f));
109 0 : } else {
110 6 : ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.4f, 0.15f, 1.0f));
111 6 : ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.55f, 0.2f, 1.0f));
112 : }
113 6 : float btn_w = 140;
114 6 : ImGui::SetCursorPosX((win_w - btn_w) * 0.5f);
115 8 : if (ImGui::Button(p.mute_on ? "MUTE ON" : "MUTE OFF", ImVec2(btn_w, 30))) {
116 0 : if (p.on_mute_changed) p.on_mute_changed(!p.mute_on);
117 0 : }
118 6 : ImGui::PopStyleColor(2);
119 :
120 : // ── A4 Reference ──
121 6 : ImGui::Spacing();
122 6 : float a4_ref = p.a4_ref;
123 6 : ImGui::SetNextItemWidth(win_w - 20);
124 6 : if (ImGui::SliderFloat("A4 Reference", &a4_ref, 430.0f, 450.0f, "%.0f Hz")) {
125 0 : if (p.on_a4_ref_changed) p.on_a4_ref_changed(a4_ref);
126 0 : }
127 :
128 6 : ImGui::End();
129 3 : }
130 :
131 : } // namespace Amplitron
|