LCOV - code coverage report
Current view: top level - src/audio/recorder - recorder_session.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 96.2 % 104 100
Test Date: 2026-06-03 09:13:19 Functions: 100.0 % 6 6

            Line data    Source code
       1              : #include "audio/recorder/recorder.h"
       2              : #include "audio/recorder/recorder_impl.h"
       3              : #include <iostream>
       4              : #include <cmath>
       5              : #include <algorithm>
       6              : 
       7              : namespace Amplitron {
       8              : 
       9           78 : bool Recorder::start(const std::string& filepath, int sample_rate, int channels) {
      10           78 :     if (recording_) return false;
      11              : 
      12           78 :     filepath_ = filepath;
      13           78 :     sample_rate_ = sample_rate;
      14           78 :     channels_ = channels;
      15           78 :     samples_written_ = 0;
      16           78 :     paused_ = false;
      17           78 :     has_unsaved_ = false;
      18           78 :     pause_duration_ = 0.0f;
      19              : 
      20              :     // Reset waveform buffer
      21        40014 :     for (auto& v : waveform_buf_) v.store(0.0f);
      22           78 :     waveform_write_pos_ = 0;
      23           78 :     current_peak_ = 0.0f;
      24           78 :     bin_sample_count_ = 0;
      25           78 :     bin_peak_ = 0.0f;
      26              :     // ~60 waveform updates per second (at 48kHz, ~800 samples per bin)
      27           78 :     samples_per_bin_ = std::max(1, sample_rate / (WAVEFORM_SIZE * 2));
      28              : 
      29              :     // Initialize ring buffer and pre-allocated PCM conversion buffer
      30           78 :     ring_buffer_.resize(RING_BUFFER_SIZE, 0.0f);
      31           78 :     ring_write_pos_ = 0;
      32           78 :     ring_read_pos_ = 0;
      33           78 :     pcm_buffer_.resize(4096);
      34              : 
      35              :     // Ensure the parent directory exists for the target filepath
      36           26 :     {
      37           78 :         std::string parent;
      38           78 :         size_t sep = filepath.find_last_of("/\\");
      39           78 :         if (sep != std::string::npos) {
      40           75 :             parent = filepath.substr(0, sep);
      41           75 :             mkdirs(parent);
      42           25 :         }
      43           78 :     }
      44              : 
      45           78 :     file_.open(filepath, std::ios::binary);
      46           78 :     if (!file_.is_open()) {
      47            8 :         std::cerr << "Recorder: failed to open " << filepath << std::endl;
      48            6 :         return false;
      49              :     }
      50              : 
      51           72 :     write_wav_header();
      52           72 :     recording_ = true;
      53           72 :     start_time_ = std::chrono::steady_clock::now();
      54              : 
      55              :     // Start the disk writer thread (keeps file I/O off the real-time audio thread)
      56           72 :     writer_running_ = true;
      57           72 :     writer_thread_ = std::thread(&Recorder::writer_thread_func, this);
      58              : 
      59           96 :     std::cout << "Recording started: " << filepath << std::endl;
      60           72 :     return true;
      61           26 : }
      62              : 
      63           75 : void Recorder::stop() {
      64           75 :     if (!recording_) return;
      65           72 :     recording_ = false;
      66           72 :     paused_ = false;
      67              : 
      68              :     // Stop writer thread and wait for it to drain remaining buffered data
      69           72 :     writer_running_ = false;
      70           72 :     if (writer_thread_.joinable()) {
      71           72 :         writer_thread_.join();
      72           24 :     }
      73              : 
      74           72 :     finalize_wav_header();
      75           72 :     file_.close();
      76              : 
      77           72 :     has_unsaved_ = true;
      78              : 
      79           72 :     float dur = get_duration();
      80           96 :     std::cout << "Recording stopped: " << samples_written_.load() << " samples ("
      81           96 :               << dur << "s) saved to " << filepath_ << std::endl;
      82           25 : }
      83              : 
      84            9 : void Recorder::pause() {
      85            9 :     if (!recording_ || paused_) return;
      86            9 :     paused_ = true;
      87            9 :     pause_start_ = std::chrono::steady_clock::now();
      88            3 : }
      89              : 
      90            9 : void Recorder::resume() {
      91            9 :     if (!recording_ || !paused_) return;
      92            9 :     auto now = std::chrono::steady_clock::now();
      93            9 :     float elapsed = std::chrono::duration<float>(now - pause_start_).count();
      94            9 :     pause_duration_ += elapsed;
      95            9 :     paused_ = false;
      96            3 : }
      97              : 
      98           12 : bool Recorder::save_to(const std::string& dest_path) {
      99           12 :     if (!has_unsaved_) return false;
     100            9 :     if (filepath_ == dest_path) {
     101            3 :         has_unsaved_ = false;
     102            3 :         return true;
     103              :     }
     104              :     // Copy file to destination
     105            6 :     std::ifstream src(filepath_, std::ios::binary);
     106            6 :     std::ofstream dst(dest_path, std::ios::binary);
     107            6 :     if (!src.is_open() || !dst.is_open()) return false;
     108            3 :     dst << src.rdbuf();
     109              :     // Check for errors during copy
     110            3 :     if (!dst.good() || !src.good()) {
     111            0 :         std::cerr << "Recorder: failed to copy recording to " << dest_path << std::endl;
     112            0 :         src.close();
     113            0 :         dst.close();
     114            0 :         return false;
     115              :     }
     116            3 :     src.close();
     117            3 :     dst.close();
     118              :     // Remove temp file
     119            3 :     std::remove(filepath_.c_str());
     120              :     // Remove metadata sidecar if exists
     121            3 :     std::string meta = filepath_;
     122            3 :     size_t dot = meta.rfind('.');
     123            3 :     if (dot != std::string::npos) meta = meta.substr(0, dot);
     124            3 :     meta += ".meta.json";
     125            3 :     std::remove(meta.c_str());
     126            3 :     filepath_ = dest_path;
     127            3 :     has_unsaved_ = false;
     128            3 :     return true;
     129            8 : }
     130              : 
     131            9 : void Recorder::discard() {
     132            9 :     if (!has_unsaved_ && !recording_) return;
     133            9 :     if (recording_) stop();
     134            9 :     std::remove(filepath_.c_str());
     135              :     // Remove metadata sidecar if exists
     136            9 :     std::string meta = filepath_;
     137            9 :     size_t dot = meta.rfind('.');
     138            9 :     if (dot != std::string::npos) meta = meta.substr(0, dot);
     139            9 :     meta += ".meta.json";
     140            9 :     std::remove(meta.c_str());
     141            9 :     has_unsaved_ = false;
     142            9 :     filepath_.clear();
     143            9 : }
     144              : 
     145              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1