aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nu/lfringbuffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/nu/lfringbuffer.c')
-rw-r--r--Src/replicant/nu/lfringbuffer.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/Src/replicant/nu/lfringbuffer.c b/Src/replicant/nu/lfringbuffer.c
new file mode 100644
index 00000000..4c38575b
--- /dev/null
+++ b/Src/replicant/nu/lfringbuffer.c
@@ -0,0 +1,158 @@
+#include "lfringbuffer.h"
+#include "foundation/error.h"
+#include <stdlib.h>
+
+typedef struct LFRingBufferHeader
+{
+ volatile size_t used; /* number of bytes written, in elements */
+ size_t size; /* in elements */
+ size_t write_position;
+ size_t read_position;
+} LFRingBufferHeader;
+
+typedef struct LFRingBuffer
+{
+ LFRingBufferHeader header;
+ float data[1];
+} LFRingBuffer;
+
+/* create a ring buffer with the desired number of elements */
+int lfringbuffer_create(lfringbuffer_t *out_ring_buffer, size_t number_of_elements)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)malloc(sizeof(LFRingBufferHeader) + sizeof(float) * number_of_elements);
+ if (!ring_buffer)
+ return NErr_OutOfMemory;
+
+ ring_buffer->header.used = 0;
+ ring_buffer->header.size = number_of_elements;
+ ring_buffer->header.write_position = 0;
+ ring_buffer->header.read_position = 0;
+ *out_ring_buffer = (lfringbuffer_t)ring_buffer;
+ return NErr_Success;
+}
+
+int lfringbuffer_destroy(lfringbuffer_t ring_buffer)
+{
+ free(ring_buffer);
+ return NErr_Success;
+}
+
+/* ----- Read functions ----- */
+/* get how many elements can currently be read */
+size_t lfringbuffer_read_available(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.used;
+}
+
+/* retrieve a pointer that can be read from.
+you might have to call this twice, because of ring buffer wraparound
+call lfringbuffer_read_update() when you are done */
+int lfringbuffer_read_get(lfringbuffer_t rb, size_t elements_requested, const float **out_buffer, size_t *elements_available)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ size_t end, to_copy;
+ int ret = NErr_Success;
+
+ /* can only read how many bytes we have available */
+ to_copy=ring_buffer->header.used;
+ if (to_copy > elements_requested)
+ {
+ to_copy = elements_requested;
+ ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
+ }
+
+ /* can only read until the end of the buffer */
+ end = ring_buffer->header.size-ring_buffer->header.read_position;
+ if (to_copy > end)
+ {
+ to_copy = end;
+ ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
+ }
+
+ *out_buffer = ring_buffer->data + ring_buffer->header.read_position;
+ *elements_available = to_copy;
+
+ ring_buffer->header.read_position += to_copy;
+ if (ring_buffer->header.read_position == ring_buffer->header.size)
+ ring_buffer->header.read_position=0;
+
+ return ret;
+}
+
+/* call to acknowledge that you have read the bytes */
+void lfringbuffer_read_update(lfringbuffer_t rb, size_t elements_read)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ nx_atomic_sub(elements_read, &ring_buffer->header.used);
+}
+static void lfringbuffer_read_set_position(lfringbuffer_t rb, size_t position)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ intptr_t bytes_to_flush = (intptr_t)(position - (size_t)ring_buffer->header.read_position);
+ if (bytes_to_flush < 0)
+ bytes_to_flush += ring_buffer->header.size;
+ lfringbuffer_read_update(rb, bytes_to_flush);
+}
+
+/* ----- Write functions ----- */
+/* get how many elements can currently be written */
+size_t lfringbuffer_write_available(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.size - ring_buffer->header.used;
+}
+
+/* retrieve a pointer that can be written to.
+you might have to call this twice, because of ring buffer wraparound
+call lfringbuffer_write_update() when you are done */
+int lfringbuffer_write_get(lfringbuffer_t rb, size_t elements_requested, float **out_buffer, size_t *elements_available)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ size_t end, to_copy;
+ int ret = NErr_Success;
+
+ /* can only write how many bytes we have available */
+ to_copy=ring_buffer->header.size - ring_buffer->header.used;
+ if (to_copy > elements_requested)
+ {
+ to_copy = elements_requested;
+ ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
+ }
+
+ /* can only read until the end of the buffer */
+ end = ring_buffer->header.size-ring_buffer->header.write_position;
+ if (to_copy > end)
+ {
+ to_copy = end;
+ ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
+ }
+
+ *out_buffer = ring_buffer->data + ring_buffer->header.write_position;
+ *elements_available = to_copy;
+
+ ring_buffer->header.write_position += to_copy;
+ if (ring_buffer->header.write_position == ring_buffer->header.size)
+ ring_buffer->header.write_position=0;
+
+ return ret;
+}
+
+/* call to acknowledge that you have written the bytes */
+void lfringbuffer_write_update(lfringbuffer_t rb, size_t elements_read)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ nx_atomic_add(elements_read, &ring_buffer->header.used);
+}
+
+size_t lfringbuffer_write_get_position(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.write_position;
+}