53 lines
2.3 KiB
C++
53 lines
2.3 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
// Single producer / single consumer. The SDL callback is a native thread: it must never
|
|
// allocate, block, log, or call FreeRTOS. Unsigned counters also work across wraparound.
|
|
class SdlAudioBuffer {
|
|
static constexpr uint32_t CAPACITY = 16384; // samples; ~171 ms of 48 kHz stereo
|
|
std::array<int16_t, CAPACITY> samples {};
|
|
std::atomic<uint32_t> read_position { 0 };
|
|
std::atomic<uint32_t> write_position { 0 };
|
|
|
|
public:
|
|
size_t write(const void* source, size_t count) {
|
|
const uint32_t write = write_position.load(std::memory_order_relaxed);
|
|
const uint32_t read = read_position.load(std::memory_order_acquire);
|
|
count = std::min(count, static_cast<size_t>(CAPACITY - (write - read)));
|
|
const size_t first = std::min(count, static_cast<size_t>(CAPACITY - write % CAPACITY));
|
|
const auto* bytes = static_cast<const uint8_t*>(source);
|
|
std::memcpy(samples.data() + write % CAPACITY, bytes, first * sizeof(int16_t));
|
|
std::memcpy(samples.data(), bytes + first * sizeof(int16_t), (count - first) * sizeof(int16_t));
|
|
write_position.store(write + count, std::memory_order_release);
|
|
return count;
|
|
}
|
|
|
|
size_t read(void* destination, size_t count) {
|
|
const uint32_t read = read_position.load(std::memory_order_relaxed);
|
|
const uint32_t write = write_position.load(std::memory_order_acquire);
|
|
count = std::min(count, static_cast<size_t>(write - read));
|
|
const size_t first = std::min(count, static_cast<size_t>(CAPACITY - read % CAPACITY));
|
|
auto* bytes = static_cast<uint8_t*>(destination);
|
|
std::memcpy(bytes, samples.data() + read % CAPACITY, first * sizeof(int16_t));
|
|
std::memcpy(bytes + first * sizeof(int16_t), samples.data(), (count - first) * sizeof(int16_t));
|
|
read_position.store(read + count, std::memory_order_release);
|
|
return count;
|
|
}
|
|
|
|
bool empty() const {
|
|
return read_position.load(std::memory_order_acquire) == write_position.load(std::memory_order_acquire);
|
|
}
|
|
|
|
// Only while the SDL device is closed and no caller is doing I/O.
|
|
void reset() {
|
|
read_position.store(0);
|
|
write_position.store(0);
|
|
}
|
|
};
|