LCOV - code coverage report
Current view: top level - src/audio/recorder - recorder_io.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 87.6 % 113 99
Test Date: 2026-06-07 15:51:50 Functions: 100.0 % 5 5

            Line data    Source code
       1              : #include <chrono>
       2              : #include <cstring>
       3              : 
       4              : #include "audio/recorder/recorder.h"
       5              : 
       6              : namespace Amplitron {
       7              : 
       8           72 : void Recorder::write_samples(const float* buffer, int num_samples) {
       9           72 :     if (!recording_ || paused_) return;
      10              : 
      11              :     // Push samples into lock-free ring buffer (real-time safe: no alloc, no I/O)
      12           63 :     int64_t wp = ring_write_pos_.load(std::memory_order_relaxed);
      13           63 :     int64_t rp = ring_read_pos_.load(std::memory_order_acquire);
      14           63 :     int64_t available_space = RING_BUFFER_SIZE - (wp - rp);
      15              : 
      16           63 :     int to_write = num_samples * channels_;
      17           63 :     if (to_write > static_cast<int>(available_space)) {
      18            0 :         to_write = static_cast<int>(available_space);
      19            0 :     }
      20              : 
      21       177717 :     for (int i = 0; i < to_write; ++i) {
      22       177654 :         ring_buffer_[static_cast<int>(wp % RING_BUFFER_SIZE)] = buffer[i];
      23       177654 :         wp++;
      24        59218 :     }
      25           63 :     ring_write_pos_.store(wp, std::memory_order_release);
      26           63 :     samples_written_ += num_samples;
      27              : 
      28              :     // Update waveform ring buffer (lock-free)
      29       175989 :     for (int i = 0; i < num_samples; ++i) {
      30       175926 :         float abs_val = std::fabs(buffer[i]);
      31       175926 :         if (abs_val > bin_peak_) bin_peak_ = abs_val;
      32       175926 :         bin_sample_count_++;
      33       175926 :         if (bin_sample_count_ >= samples_per_bin_) {
      34         3807 :             int pos = waveform_write_pos_.load() % WAVEFORM_SIZE;
      35         3807 :             waveform_buf_[pos].store(bin_peak_);
      36         3807 :             waveform_write_pos_.fetch_add(1);
      37         3807 :             current_peak_.store(bin_peak_);
      38         3807 :             bin_peak_ = 0.0f;
      39         3807 :             bin_sample_count_ = 0;
      40         1269 :         }
      41        58642 :     }
      42           24 : }
      43              : 
      44           72 : void Recorder::writer_thread_func() {
      45          116 :     while (true) {
      46          149 :         int64_t rp = ring_read_pos_.load(std::memory_order_relaxed);
      47          149 :         int64_t wp = ring_write_pos_.load(std::memory_order_acquire);
      48          149 :         int64_t available = wp - rp;
      49              : 
      50          149 :         if (available > 0) {
      51              :             // Drain available samples: convert float -> int16 PCM and write to disk
      52          162 :             while (available > 0) {
      53           65 :                 int chunk =
      54           99 :                     static_cast<int>(std::min(available, static_cast<int64_t>(pcm_buffer_.size())));
      55       180441 :                 for (int i = 0; i < chunk; ++i) {
      56       180342 :                     float s = ring_buffer_[static_cast<int>((rp + i) % RING_BUFFER_SIZE)];
      57       180342 :                     if (s > 1.0f) s = 1.0f;
      58       180342 :                     if (s < -1.0f) s = -1.0f;
      59       180342 :                     pcm_buffer_[i] = static_cast<int16_t>(s * 32767.0f);
      60        60114 :                 }
      61          132 :                 file_.write(reinterpret_cast<const char*>(pcm_buffer_.data()),
      62           99 :                             chunk * sizeof(int16_t));
      63           99 :                 rp += chunk;
      64           99 :                 available -= chunk;
      65              :             }
      66           63 :             ring_read_pos_.store(rp, std::memory_order_release);
      67           21 :         } else {
      68              :             // No data available — exit if stopped, otherwise poll briefly
      69           86 :             if (!writer_running_.load(std::memory_order_acquire)) {
      70           48 :                 break;
      71              :             }
      72           14 :             std::this_thread::sleep_for(std::chrono::milliseconds(5));
      73              :         }
      74           53 :     }
      75              : 
      76              :     // Final drain: flush any samples written between the last read and stop()
      77           72 :     int64_t rp = ring_read_pos_.load(std::memory_order_relaxed);
      78           72 :     int64_t wp = ring_write_pos_.load(std::memory_order_acquire);
      79           72 :     int64_t remaining = wp - rp;
      80           72 :     while (remaining > 0) {
      81            0 :         int chunk = static_cast<int>(std::min(remaining, static_cast<int64_t>(pcm_buffer_.size())));
      82            0 :         for (int i = 0; i < chunk; ++i) {
      83            0 :             float s = ring_buffer_[static_cast<int>((rp + i) % RING_BUFFER_SIZE)];
      84            0 :             if (s > 1.0f) s = 1.0f;
      85            0 :             if (s < -1.0f) s = -1.0f;
      86            0 :             pcm_buffer_[i] = static_cast<int16_t>(s * 32767.0f);
      87            0 :         }
      88            0 :         file_.write(reinterpret_cast<const char*>(pcm_buffer_.data()), chunk * sizeof(int16_t));
      89            0 :         rp += chunk;
      90            0 :         remaining -= chunk;
      91              :     }
      92           72 :     ring_read_pos_.store(rp, std::memory_order_release);
      93           72 : }
      94              : 
      95           15 : void Recorder::get_waveform(float* out, int count) const {
      96           15 :     int wp = waveform_write_pos_.load();
      97         6159 :     for (int i = 0; i < count; ++i) {
      98         6144 :         int idx = (wp - count + i + WAVEFORM_SIZE * 2) % WAVEFORM_SIZE;
      99         6144 :         out[i] = waveform_buf_[idx].load();
     100         2048 :     }
     101           15 : }
     102              : 
     103           96 : float Recorder::get_duration() const {
     104           96 :     int64_t total = samples_written_.load();
     105           96 :     if (sample_rate_ <= 0) return 0.0f;
     106           96 :     return static_cast<float>(total) / sample_rate_;
     107           32 : }
     108              : 
     109            9 : void Recorder::write_samples_stereo(const float* left, const float* right, int num_samples) {
     110            9 :     if (!recording_ || paused_) return;
     111              : 
     112            9 :     int64_t wp = ring_write_pos_.load(std::memory_order_relaxed);
     113            9 :     int64_t rp = ring_read_pos_.load(std::memory_order_acquire);
     114            9 :     int64_t available_space = RING_BUFFER_SIZE - (wp - rp);
     115              : 
     116              :     // Each frame = 2 floats (left + right interleaved)
     117            9 :     int frames_to_write = num_samples;
     118            9 :     if (frames_to_write * 2 > static_cast<int>(available_space)) {
     119            0 :         frames_to_write = static_cast<int>(available_space) / 2;
     120            0 :     }
     121              : 
     122         1353 :     for (int i = 0; i < frames_to_write; ++i) {
     123         1344 :         ring_buffer_[static_cast<int>(wp % RING_BUFFER_SIZE)] = left[i];
     124         1344 :         wp++;
     125         1344 :         ring_buffer_[static_cast<int>(wp % RING_BUFFER_SIZE)] = right[i];
     126         1344 :         wp++;
     127          448 :     }
     128            9 :     ring_write_pos_.store(wp, std::memory_order_release);
     129            9 :     samples_written_ += frames_to_write;
     130              : 
     131              :     // Update waveform display using left channel
     132         1353 :     for (int i = 0; i < frames_to_write; ++i) {
     133         1344 :         float abs_val = std::fabs(left[i]);
     134         1344 :         if (abs_val > bin_peak_) bin_peak_ = abs_val;
     135         1344 :         bin_sample_count_++;
     136         1344 :         if (bin_sample_count_ >= samples_per_bin_) {
     137           24 :             int pos = waveform_write_pos_.load() % WAVEFORM_SIZE;
     138           24 :             waveform_buf_[pos].store(bin_peak_);
     139           24 :             waveform_write_pos_.fetch_add(1);
     140           24 :             current_peak_.store(bin_peak_);
     141           24 :             bin_peak_ = 0.0f;
     142           24 :             bin_sample_count_ = 0;
     143            8 :         }
     144          448 :     }
     145            3 : }
     146              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1