diff options
Diffstat (limited to 'src/audio/ring_buffer.cc')
| -rw-r--r-- | src/audio/ring_buffer.cc | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/audio/ring_buffer.cc b/src/audio/ring_buffer.cc new file mode 100644 index 0000000..25cf853 --- /dev/null +++ b/src/audio/ring_buffer.cc @@ -0,0 +1,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_)); +} |
