#include "AudioRing.h" #include #include #include struct MusubiRing { float *data; uint32_t capacity; // frames, power of two uint32_t mask; uint32_t channels; _Atomic uint64_t writeIndex; // frames written in total _Atomic uint64_t readIndex; // frames read in total }; static uint32_t next_power_of_two(uint32_t value) { uint32_t result = 1; while (result < value) result <<= 1; return result; } MusubiRing *musubi_ring_create(uint32_t capacityFrames, uint32_t channels) { if (capacityFrames == 0 || channels == 0) return NULL; MusubiRing *ring = calloc(1, sizeof(MusubiRing)); if (!ring) return NULL; ring->capacity = next_power_of_two(capacityFrames); ring->mask = ring->capacity - 1; ring->channels = channels; ring->data = calloc((size_t)ring->capacity * channels, sizeof(float)); if (!ring->data) { free(ring); return NULL; } atomic_init(&ring->writeIndex, 0); atomic_init(&ring->readIndex, 0); return ring; } void musubi_ring_destroy(MusubiRing *ring) { if (!ring) return; free(ring->data); free(ring); } uint32_t musubi_ring_channels(const MusubiRing *ring) { return ring->channels; } uint32_t musubi_ring_capacity(const MusubiRing *ring) { return ring->capacity; } uint32_t musubi_ring_available(const MusubiRing *ring) { uint64_t w = atomic_load_explicit(&((MusubiRing *)ring)->writeIndex, memory_order_acquire); uint64_t r = atomic_load_explicit(&((MusubiRing *)ring)->readIndex, memory_order_acquire); return (uint32_t)(w - r); } // Copies `frames` frames between the ring (starting at frame index `start`) and a linear buffer. static void copy_frames(MusubiRing *ring, uint64_t start, float *linear, uint32_t frames, int toRing) { uint32_t offset = (uint32_t)(start & ring->mask); uint32_t first = ring->capacity - offset; if (first > frames) first = frames; size_t frameBytes = sizeof(float) * ring->channels; float *slot = ring->data + (size_t)offset * ring->channels; if (toRing) { memcpy(slot, linear, first * frameBytes); memcpy(ring->data, linear + (size_t)first * ring->channels, (frames - first) * frameBytes); } else { memcpy(linear, slot, first * frameBytes); memcpy(linear + (size_t)first * ring->channels, ring->data, (frames - first) * frameBytes); } } uint32_t musubi_ring_write(MusubiRing *ring, const float *interleaved, uint32_t frames) { uint64_t w = atomic_load_explicit(&ring->writeIndex, memory_order_relaxed); uint64_t r = atomic_load_explicit(&ring->readIndex, memory_order_acquire); uint32_t space = ring->capacity - (uint32_t)(w - r); if (frames > space) frames = space; if (frames == 0) return 0; copy_frames(ring, w, (float *)interleaved, frames, 1); atomic_store_explicit(&ring->writeIndex, w + frames, memory_order_release); return frames; } uint32_t musubi_ring_read(MusubiRing *ring, float *interleaved, uint32_t frames) { uint64_t r = atomic_load_explicit(&ring->readIndex, memory_order_relaxed); uint64_t w = atomic_load_explicit(&ring->writeIndex, memory_order_acquire); uint32_t available = (uint32_t)(w - r); if (frames > available) frames = available; if (frames == 0) return 0; copy_frames(ring, r, interleaved, frames, 0); atomic_store_explicit(&ring->readIndex, r + frames, memory_order_release); return frames; } uint32_t musubi_ring_skip(MusubiRing *ring, uint32_t frames) { uint64_t r = atomic_load_explicit(&ring->readIndex, memory_order_relaxed); uint64_t w = atomic_load_explicit(&ring->writeIndex, memory_order_acquire); uint32_t available = (uint32_t)(w - r); if (frames > available) frames = available; atomic_store_explicit(&ring->readIndex, r + frames, memory_order_release); return frames; }