1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
// This file is part of the 64k demo project.
// It implements a lock-free ring buffer for audio streaming.
#include "ring_buffer.h"
#include <algorithm>
#include <cstring>
AudioRingBuffer::AudioRingBuffer()
: capacity_(RING_BUFFER_CAPACITY_SAMPLES),
write_pos_(0),
read_pos_(0),
total_read_(0) {
memset(buffer_, 0, sizeof(buffer_));
}
AudioRingBuffer::~AudioRingBuffer() {
// Nothing to clean up (static buffer)
}
int AudioRingBuffer::available_write() const {
const int write = write_pos_.load(std::memory_order_acquire);
const int read = read_pos_.load(std::memory_order_acquire);
if (write >= read) {
return capacity_ - (write - read) - 1; // -1 to avoid full/empty ambiguity
} else {
return read - write - 1;
}
}
int AudioRingBuffer::available_read() const {
const int write = write_pos_.load(std::memory_order_acquire);
const int read = read_pos_.load(std::memory_order_acquire);
if (write >= read) {
return write - read;
} else {
return capacity_ - (read - write);
}
}
int AudioRingBuffer::write(const float* samples, int count) {
const int avail = available_write();
const int to_write = std::min(count, avail);
if (to_write <= 0) {
return 0;
}
const int write = write_pos_.load(std::memory_order_acquire);
const int space_to_end = capacity_ - write;
if (to_write <= space_to_end) {
// Write in one chunk
memcpy(&buffer_[write], samples, to_write * sizeof(float));
write_pos_.store((write + to_write) % capacity_, std::memory_order_release);
} else {
// Write in two chunks (wrap around)
memcpy(&buffer_[write], samples, space_to_end * sizeof(float));
const int remainder = to_write - space_to_end;
memcpy(&buffer_[0], samples + space_to_end, remainder * sizeof(float));
write_pos_.store(remainder, std::memory_order_release);
}
return to_write;
}
int AudioRingBuffer::read(float* samples, int count) {
const int avail = available_read();
const int to_read = std::min(count, avail);
if (to_read > 0) {
const int read = read_pos_.load(std::memory_order_acquire);
const int space_to_end = capacity_ - read;
if (to_read <= space_to_end) {
// Read in one chunk
memcpy(samples, &buffer_[read], to_read * sizeof(float));
read_pos_.store((read + to_read) % capacity_, std::memory_order_release);
} else {
// Read in two chunks (wrap around)
memcpy(samples, &buffer_[read], space_to_end * sizeof(float));
const int remainder = to_read - space_to_end;
memcpy(samples + space_to_end, &buffer_[0], remainder * sizeof(float));
read_pos_.store(remainder, std::memory_order_release);
}
total_read_.fetch_add(to_read, std::memory_order_release);
}
// Fill remainder with silence if not enough samples available
if (to_read < count) {
memset(samples + to_read, 0, (count - to_read) * sizeof(float));
}
return to_read;
}
void AudioRingBuffer::clear() {
write_pos_.store(0, std::memory_order_release);
read_pos_.store(0, std::memory_order_release);
// Note: Don't reset total_read_ - it tracks absolute playback time
memset(buffer_, 0, sizeof(buffer_));
}
|