// This file is part of the 64k demo project. // It implements a lock-free ring buffer for audio streaming. #include "ring_buffer.h" #include #include 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_)); }