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
|