LCOV - code coverage report
Current view: top level - src/gui/views - gui_recording.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 69.9 % 136 95
Test Date: 2026-06-03 09:13:19 Functions: 40.0 % 5 2

            Line data    Source code
       1              : #include "gui/views/gui_recording.h"
       2              : #include "gui/dialogs/file_dialog.h"
       3              : #include "gui/theme/theme.h"
       4              : #include "common.h"
       5              : #include <imgui.h>
       6              : #include <algorithm>
       7              : #include <cmath>
       8              : #include <cstdint>
       9              : 
      10              : namespace Amplitron {
      11              : 
      12            9 : void GuiRecording::render() {
      13            9 :     const RecordingProps& p = props_;
      14              : 
      15            9 :     float font_scale = ImGui::GetFontSize() / 14.0f;
      16           12 :     float base_h = ImGui::GetFrameHeight()
      17            9 :                  + ImGui::GetStyle().WindowPadding.y * 2.0f
      18            9 :                  + ImGui::GetStyle().WindowBorderSize * 2.0f;
      19            9 :     float panel_height = p.is_recording ? (base_h + 80.0f) : base_h;
      20              : 
      21            9 :     ImGui::BeginChild("RecordingPanel", ImVec2(0, panel_height), true,
      22              :                        ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
      23              : 
      24            9 :     if (p.is_recording) {
      25              :         // ── Pause / Resume ──
      26            6 :         if (p.is_paused) {
      27            3 :             ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.2f, 0.5f, 0.2f, 1.0f));
      28            3 :             ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.7f, 0.3f, 1.0f));
      29            3 :             if (ImGui::Button("RESUME", ImVec2(80 * font_scale, 0)) && p.on_resume) p.on_resume();
      30            3 :             ImGui::PopStyleColor(2);
      31            1 :         } else {
      32            3 :             ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.6f, 0.5f, 0.1f, 1.0f));
      33            3 :             ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.7f, 0.2f, 1.0f));
      34            3 :             if (ImGui::Button("PAUSE", ImVec2(80 * font_scale, 0)) && p.on_pause) p.on_pause();
      35            3 :             ImGui::PopStyleColor(2);
      36              :         }
      37              : 
      38            6 :         ImGui::SameLine();
      39              : 
      40              :         // ── Stop ──
      41            6 :         ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.7f, 0.1f, 0.1f, 1.0f));
      42            6 :         ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
      43            6 :         if (ImGui::Button("STOP", ImVec2(80 * font_scale, 0))) {
      44            0 :             if (p.on_stop) p.on_stop();
      45            0 :             set_state([](RecordingState& st) { st.needs_save = true; });
      46            0 :         }
      47            6 :         ImGui::PopStyleColor(2);
      48              : 
      49            6 :         ImGui::SameLine();
      50              : 
      51              :         // ── Blink indicator ──
      52            6 :         float t = static_cast<float>(ImGui::GetTime());
      53            6 :         if (p.is_paused) {
      54            3 :             ImGui::TextColored(ImVec4(0.8f, 0.7f, 0.2f, 1.0f), "  PAUSED");
      55            1 :         } else {
      56            3 :             float blink = (std::sin(t * 4.0f) > 0.0f) ? 1.0f : 0.3f;
      57            3 :             ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.15f, 0.15f, blink));
      58            3 :             ImGui::Text("  REC");
      59            3 :             ImGui::PopStyleColor();
      60              :         }
      61              : 
      62            6 :         ImGui::SameLine();
      63              : 
      64              :         // ── Timer ──
      65            6 :         int mins = static_cast<int>(p.duration) / 60;
      66            6 :         int secs = static_cast<int>(p.duration) % 60;
      67            6 :         int ms   = static_cast<int>((p.duration - static_cast<int>(p.duration)) * 10);
      68            6 :         ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "  %02d:%02d.%d", mins, secs, ms);
      69              : 
      70            6 :         ImGui::SameLine();
      71              : 
      72              :         // ── Peak meter ──
      73            6 :         float peak = p.current_peak;
      74           16 :         ImGui::TextColored(peak > 0.9f ? ImVec4(1, 0.2f, 0.2f, 1) :
      75            6 :                            peak > 0.6f ? ImVec4(1, 0.8f, 0.2f, 1) :
      76            2 :                                          ImVec4(0.2f, 0.8f, 0.2f, 1),
      77              :                            "  Peak: %.1f dB",
      78            2 :                            peak > 0.0001f ? 20.0f * std::log10(peak) : -96.0f);
      79              : 
      80            6 :         ImGui::SameLine(ImGui::GetContentRegionAvail().x - 120);
      81            6 :         int64_t file_bytes = p.samples_written * 2 * p.channels;
      82            6 :         if (file_bytes > 1024 * 1024)
      83            0 :             ImGui::Text("%.1f MB", file_bytes / (1024.0f * 1024.0f));
      84              :         else
      85            6 :             ImGui::Text("%.0f KB", file_bytes / 1024.0f);
      86              : 
      87              :         // ── Waveform ──
      88            6 :         ImGui::Spacing();
      89            6 :         ImVec2 wave_pos = ImGui::GetCursorScreenPos();
      90            6 :         float  wave_w   = ImGui::GetContentRegionAvail().x;
      91            6 :         float  wave_h   = 50.0f * font_scale;
      92            6 :         ImDrawList* draw = ImGui::GetWindowDrawList();
      93              : 
      94            8 :         draw->AddRectFilled(wave_pos,
      95            6 :                             ImVec2(wave_pos.x + wave_w, wave_pos.y + wave_h),
      96              :                             IM_COL32(20, 18, 16, 255), 4.0f);
      97              : 
      98            6 :         float center_y = wave_pos.y + wave_h * 0.5f;
      99           10 :         draw->AddLine(ImVec2(wave_pos.x, center_y),
     100            6 :                       ImVec2(wave_pos.x + wave_w, center_y),
     101              :                       IM_COL32(60, 55, 48, 255));
     102              : 
     103            6 :         ImU32 wave_color       = p.is_paused ? IM_COL32(180, 160, 50, 200) : IM_COL32(200, 80, 60, 220);
     104            6 :         ImU32 wave_color_bright = p.is_paused ? IM_COL32(220, 200, 80, 255) : IM_COL32(255, 100, 70, 255);
     105              : 
     106            6 :         if (p.waveform_buf && p.waveform_size > 0) {
     107            6 :             int num_bars = std::max(1, static_cast<int>(wave_w));
     108            6 :             float samples_per_pixel = static_cast<float>(p.waveform_size) / num_bars;
     109         2214 :             for (int i = 0; i < num_bars; ++i) {
     110         2208 :                 int idx = static_cast<int>(i * samples_per_pixel);
     111         2208 :                 if (idx >= p.waveform_size) idx = p.waveform_size - 1;
     112         2208 :                 float val   = p.waveform_buf[idx];
     113         2208 :                 float bar_h = val * wave_h * 0.48f;
     114         2208 :                 if (bar_h < 0.5f) continue;
     115            0 :                 float x   = wave_pos.x + i;
     116            0 :                 ImU32 col = val > 0.8f ? wave_color_bright : wave_color;
     117            0 :                 draw->AddLine(ImVec2(x, center_y - bar_h), ImVec2(x, center_y + bar_h), col);
     118            0 :             }
     119            2 :         }
     120              : 
     121            8 :         draw->AddRect(wave_pos,
     122            6 :                       ImVec2(wave_pos.x + wave_w, wave_pos.y + wave_h),
     123              :                       IM_COL32(70, 65, 55, 255), 4.0f);
     124            6 :         ImGui::Dummy(ImVec2(wave_w, wave_h));
     125              : 
     126            5 :     } else if (p.has_unsaved) {
     127              :         // ── Unsaved recording ──
     128            0 :         {
     129            0 :             float avail  = ImGui::GetContentRegionAvail().y;
     130            0 :             float row_h  = ImGui::GetFrameHeight();
     131            0 :             float offset = std::max(0.0f, (avail - row_h) * 0.5f);
     132            0 :             ImGui::SetCursorPosY(ImGui::GetCursorPosY() + offset);
     133              :         }
     134              : 
     135            0 :         float content_w = ImGui::CalcTextSize("Recording complete").x
     136            0 :                         + ImGui::CalcTextSize("  999.9 s  |  ").x
     137            0 :                         + 100.0f + 80.0f
     138            0 :                         + ImGui::GetStyle().ItemSpacing.x * 3.0f;
     139            0 :         float start_x = (ImGui::GetContentRegionAvail().x - content_w) * 0.5f;
     140            0 :         if (start_x > 0.0f) ImGui::SetCursorPosX(start_x);
     141              : 
     142            0 :         ImGui::TextColored(Theme::Gold(), "Recording complete");
     143            0 :         ImGui::SameLine();
     144            0 :         ImGui::Text("  %.1f s  |  ", p.duration);
     145            0 :         ImGui::SameLine();
     146              : 
     147            0 :         ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.2f, 0.5f, 0.2f, 1.0f));
     148            0 :         ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.7f, 0.3f, 1.0f));
     149            0 :         if (ImGui::Button("Save As...", ImVec2(100 * font_scale, 0))) {
     150            0 :             set_state([](RecordingState& st) { st.needs_save = true; });
     151            0 :         }
     152            0 :         ImGui::PopStyleColor(2);
     153              : 
     154            0 :         ImGui::SameLine();
     155            0 :         ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.4f, 0.1f, 0.1f, 1.0f));
     156            0 :         ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.6f, 0.15f, 0.15f, 1.0f));
     157            0 :         if (ImGui::Button("Discard", ImVec2(80 * font_scale, 0))) {
     158            0 :             if (p.on_discard) p.on_discard();
     159            0 :             set_state([](RecordingState& st) { st.status_msg = "Recording discarded."; });
     160            0 :         }
     161            0 :         ImGui::PopStyleColor(2);
     162              : 
     163            0 :     } else {
     164              :         // ── Ready ──
     165            1 :         {
     166            3 :             float avail  = ImGui::GetContentRegionAvail().y;
     167            3 :             constexpr float row_h = 28.0f;
     168            3 :             float offset = std::max(0.0f, (avail - row_h) * 0.5f);
     169            3 :             ImGui::SetCursorPosY(ImGui::GetCursorPosY() + offset);
     170              :         }
     171            3 :         ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0.5f, 0.05f, 0.05f, 1.0f));
     172            3 :         ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.7f, 0.1f,  0.1f,  1.0f));
     173            3 :         ImGui::PushStyleColor(ImGuiCol_ButtonActive,  ImVec4(0.9f, 0.15f, 0.15f, 1.0f));
     174            3 :         if (ImGui::Button("REC", ImVec2(90 * font_scale, 0))) {
     175            0 :             if (p.on_start) p.on_start();
     176            0 :         }
     177            3 :         ImGui::PopStyleColor(3);
     178              : 
     179            3 :         ImGui::SameLine();
     180            4 :         ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
     181              :                            "  Ready to record  |  WAV 16-bit %d Hz",
     182            3 :                            p.sample_rate);
     183              :     }
     184              : 
     185            9 :     ImGui::EndChild();
     186            9 : }
     187              : 
     188            3 : void GuiRecording::render_save_dialog(std::function<void(const std::string& dest)> on_save_done) {
     189            3 :     if (!state_.needs_save) return;
     190            3 :     state_.needs_save = false;
     191              : 
     192            8 :     std::string dest = show_save_dialog("recording.wav", "WAV Audio", "wav");
     193            3 :     if (!dest.empty() && on_save_done)
     194            0 :         on_save_done(dest);
     195            3 : }
     196              : 
     197              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1