LCOV - code coverage report
Current view: top level - src/audio/recorder - recorder_io.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 86.1 % 115 99
Test Date: 2026-06-03 09:13:19 Functions: 100.0 % 5 5

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

Generated by: LCOV version 2.0-1