diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..6c5c7ae --- /dev/null +++ b/buffer.h @@ -0,0 +1,171 @@ +/** @file */ + +#ifndef LLSM_BUFFER_H +#define LLSM_BUFFER_H + +#include +#include + +/** @defgroup group_ringbuffer llsm_ringbuffer + * @{ */ +/** @brief A circular array structure for storing audio samples streaming in + * and streaming out in real time. */ +typedef struct { + FP_TYPE* data; /**< the audio samples */ + int capacity; /**< the size of the ring buffer */ + int curr; /**< the current access position */ +} llsm_ringbuffer; + +/** @brief Create an empty ring buffer with a given size. */ +static inline llsm_ringbuffer* llsm_create_ringbuffer(int capacity) { + assert(capacity > 0); + llsm_ringbuffer* ret = (llsm_ringbuffer*)malloc(sizeof(llsm_ringbuffer)); + ret -> capacity = capacity; + ret -> data = (FP_TYPE*)calloc(capacity, sizeof(FP_TYPE)); + ret -> curr = 0; + return ret; +} + +/** @brief Delete and free a ring buffer. */ +static inline void llsm_delete_ringbuffer(llsm_ringbuffer* dst) { + if(dst == NULL) return; + free(dst -> data); + free(dst); +} + +/** @brief Access the sample at an index relative to the current position; + * the index is expected to be negative. */ +static inline FP_TYPE llsm_ringbuffer_read(llsm_ringbuffer* src, int idx) { + assert(idx < 0 && idx >= -src -> capacity); + return src -> data[(src -> curr + idx + src -> capacity) % src -> capacity]; +} + +/** @brief Modify the sample at an index relative to the current position; + * the index is expected to be negative. */ +static inline void llsm_ringbuffer_write(llsm_ringbuffer* dst, int idx, FP_TYPE x) { + assert(idx < 0 && idx >= -dst -> capacity); + dst -> data[(dst -> curr + idx + dst -> capacity) % dst -> capacity] = x; +} + +/** @brief Append a sample to the ring buffer and push the current position + * forward by one sample. */ +static inline void llsm_ringbuffer_append(llsm_ringbuffer* dst, FP_TYPE x) { + dst -> data[dst -> curr] = x; + dst -> curr = (dst -> curr + 1) % dst -> capacity; +} + +/** @brief Push the current position forward by several samples without + * modifying the content. */ +static inline void llsm_ringbuffer_forward(llsm_ringbuffer* dst, int size) { + dst -> curr = (dst -> curr + size) % dst -> capacity; +} + +/** @brief Load a contiguous subset of samples into a destination pointer; + * the subset starts from a negative index (lag). */ +static inline void llsm_ringbuffer_readchunk(llsm_ringbuffer* src, + int lag, int size, FP_TYPE* dst) { + assert(size > 0); + assert(lag + size <= 0); + assert(lag > -src -> capacity); + int base = src -> curr + src -> capacity; + for(int i = 0; i < size; i ++) + dst[i] = src -> data[(base + lag + i) % src -> capacity]; +} + +/** @brief Write a contiguous subset of samples from a source pointer; + * the subset starts from a negative index (lag). */ +static inline void llsm_ringbuffer_writechunk(llsm_ringbuffer* dst, + int lag, int size, FP_TYPE* src) { + assert(size > 0); + assert(lag + size <= 0); + assert(lag >= -dst -> capacity); + int base = dst -> curr + dst -> capacity; + for(int i = 0; i < size; i ++) + dst -> data[(base + lag + i) % dst -> capacity] = src[i]; +} + +/** @brief Append a contiguous subset of samples from a source pointer. + * This will move the current position forward by the size of the data. */ +static inline void llsm_ringbuffer_appendchunk(llsm_ringbuffer* dst, + int size, FP_TYPE* src) { + assert(size > 0); + assert(size <= dst -> capacity); + llsm_ringbuffer_forward(dst, size); + llsm_ringbuffer_writechunk(dst, -size, size, src); +} + +/** @brief Append a chunk of zeros. This will move the current position forward + * by the number of zeros. */ +static inline void llsm_ringbuffer_appendblank(llsm_ringbuffer* dst, int size) { + assert(size > 0); + assert(size <= dst -> capacity); + llsm_ringbuffer_forward(dst, size); + int base = dst -> curr + dst -> capacity; + for(int i = 0; i < size; i ++) + dst -> data[(base - size + i) % dst -> capacity] = 0; +} +/** @} */ + +/** @defgroup group_vringbuffer llsm_vringbuffer + * @{ */ +/** @brief A circular array structure for storing structural objects streaming + * int and streaming out in real time. A destructor function has to be + * specified (and hence the name virtual ring buffer). */ +typedef struct { + void** data; + int capacity; + int curr; + llsm_fdestructor destructor; +} llsm_vringbuffer; + +/** @brief Create an empty virtual ring buffer with a given size. */ +static inline llsm_vringbuffer* llsm_create_vringbuffer(int capacity, + llsm_fdestructor destructor) { + llsm_vringbuffer* ret = (llsm_vringbuffer*)malloc(sizeof(llsm_vringbuffer)); + ret -> capacity = capacity; + ret -> curr = 0; + ret -> data = (void**)malloc(sizeof(void*) * capacity); + ret -> destructor = destructor; + for(int i = 0; i < capacity; i ++) ret -> data[i] = NULL; + return ret; +} + +/** @brief Delete and free a virtual ring buffer, including all its stored + * objects. */ +static inline void llsm_delete_vringbuffer(llsm_vringbuffer* dst) { + if(dst == NULL) return; + for(int i = 0; i < dst -> capacity; i ++) + if(dst -> data[i] != NULL) + dst -> destructor(dst -> data[i]); + free(dst -> data); + free(dst); +} + +/** @brief Access the object at an index relative to the current position; + * the index is expected to be negative. */ +static inline void* llsm_vringbuffer_read(llsm_vringbuffer* src, int idx) { + assert(idx < 0 && idx >= -src -> capacity); + return src -> data[(src -> curr + idx + src -> capacity) % src -> capacity]; +} + +/** @brief Replace the object at an index relative to the current position by + * a given pointer; no actual deep copy is performed; the index is expected + * to be negative. */ +static inline void llsm_vringbuffer_write(llsm_vringbuffer* dst, int idx, void* x) { + assert(idx < 0 && idx >= -dst -> capacity); + void** currptr = & dst -> data[(dst -> curr + idx + dst -> capacity) % dst -> capacity]; + if(*currptr != NULL) dst -> destructor(*currptr); + *currptr = x; +} + +/** @brief Append an object to the virtual ring buffer and push the current + * position forward by one sample; no actual deep copy is performed. */ +static inline void llsm_vringbuffer_append(llsm_vringbuffer* dst, void* x) { + if(dst -> data[dst -> curr] != NULL) + dst -> destructor(dst -> data[dst -> curr]); + dst -> data[dst -> curr] = x; + dst -> curr = (dst -> curr + 1) % dst -> capacity; +} +/** @} */ + +#endif diff --git a/makefile b/makefile index 3c11270..fb79647 100644 --- a/makefile +++ b/makefile @@ -5,7 +5,7 @@ AR = ar ARFLAGS = -rv CFLAGS_DBG = -DFP_TYPE=float -Og -g -std=c99 -Wall -fPIC CFLAGS_REL = -DFP_TYPE=float -Ofast -std=c99 -Wall -fPIC -CFLAGS = CFLAGS_DBG +CFLAGS = $(CFLAGS_DBG) OUT_DIR = ./build OBJS = $(OUT_DIR)/container.o \ @@ -55,6 +55,8 @@ test-dsputils: $(OUT_DIR)/test-structs \ $(OUT_DIR)/test-dsputils $(OUT_DIR)/test-harmonic +$(OUT_DIR)/test-structs: buffer.h + $(OUT_DIR)/test-%: test/test-%.c $(TARGET_A) \ $(LIBPYIN_A) $(LIBGVPS_A) $(CIGLET_O) test/verify-utils.h $(CC) $(CFLAGS) -o $(OUT_DIR)/test-$* test/test-$*.c \ diff --git a/test/test-structs.c b/test/test-structs.c index fcc7a4c..9dd3df1 100644 --- a/test/test-structs.c +++ b/test/test-structs.c @@ -1,4 +1,5 @@ #include "../llsm.h" +#include "../buffer.h" #include #include #include @@ -125,10 +126,59 @@ void test_chunk() { llsm_delete_aoptions(opt); } +void test_buffer() { + llsm_ringbuffer* rb1 = llsm_create_ringbuffer(4096); + FP_TYPE x[100]; + FP_TYPE y[200]; + for(int i = 0; i < 100; i ++) + x[i] = (FP_TYPE)rand() / RAND_MAX - 0.5; + for(int i = 0; i < 100; i ++) { + // The following two realizations of appending 100 samples should + // give equivalent results. + if(i % 2 == 0) { + llsm_ringbuffer_appendchunk(rb1, 100, x); + } else { + llsm_ringbuffer_forward(rb1, 100); + llsm_ringbuffer_writechunk(rb1, -100, 100, x); + } + // The following two realizations of appending 100 zeros should + // give equivalent results. + if(i % 3 == 0) { + llsm_ringbuffer_appendblank(rb1, 100); + } else { + for(int j = 0; j < 100; j ++) + llsm_ringbuffer_append(rb1, 0); + } + // The following two realizations of reading 200 samples should + // give equivalent results. + if(i % 4 == 0) { + for(int j = 0; j < 200; j ++) + y[j] = llsm_ringbuffer_read(rb1, j - 200); + } else { + llsm_ringbuffer_readchunk(rb1, -200, 200, y); + } + + for(int j = 0; j < 100; j ++) + assert(y[j] == x[j]); + for(int j = 100; j < 200; j ++) + assert(y[j] == 0); + + if(i <= 5) continue; + + llsm_ringbuffer_readchunk(rb1, -1300, 200, y); + for(int j = 0; j < 100; j ++) + assert(y[j] == 0); + for(int j = 100; j < 200; j ++) + assert(y[j] == x[j - 100]); + } + llsm_delete_ringbuffer(rb1); +} + int main() { test_container(); test_hmframe(); test_nmframe(); test_chunk(); + test_buffer(); return 0; }