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