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-07 15:51:50 Functions: 100.0 % 6 6

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

Generated by: LCOV version 2.0-1