LCOV - code coverage report
Current view: top level - src/gui/views - gui_recording.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 70.2 % 131 92
Test Date: 2026-06-07 15:51:50 Functions: 40.0 % 5 2

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

Generated by: LCOV version 2.0-1