You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.8 KiB
96 lines
3.8 KiB
#include "AudioRing.h"
|
|
#include <stdatomic.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|