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 117 : 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 162 : while (available > 0) {
52 166 : int chunk = static_cast<int>(std::min(available,
53 99 : static_cast<int64_t>(pcm_buffer_.size())));
54 180441 : 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 99 : chunk * sizeof(int16_t));
62 99 : rp += chunk;
63 99 : available -= chunk;
64 : }
65 63 : ring_read_pos_.store(rp, std::memory_order_release);
66 20 : } else {
67 : // No data available — exit if stopped, otherwise poll briefly
68 88 : if (!writer_running_.load(std::memory_order_acquire)) {
69 48 : break;
70 : }
71 16 : 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
|