Skip to content

Commit

Permalink
Add Python bindings and tests for RingBuffer; fix read/write head pos…
Browse files Browse the repository at this point in the history
…itions
  • Loading branch information
ideoforms committed Oct 24, 2024
1 parent 3f290ad commit 6ca7a85
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 9 deletions.
20 changes: 15 additions & 5 deletions source/include/signalflow/buffer/ringbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*--------------------------------------------------------------------------------*/

#include <math.h>
#include <mutex>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mutex>

enum signalflow_interpolation_mode_t : unsigned int;

Expand All @@ -27,8 +27,11 @@ class RingBuffer

void append(T value);
void extend(T *ptr, unsigned int count);
void extend(std::vector<T> vec);
T get(double index);
T operator[](double index) { return this->get(index); }
unsigned int get_capacity() { return this->capacity; }
unsigned int get_write_position() { return this->write_position; }

protected:
T *data = nullptr;
Expand All @@ -44,7 +47,7 @@ class RingQueue : public RingBuffer<T>
RingQueue(unsigned int capacity)
: RingBuffer<T>(capacity)
{
this->read_position = this->capacity - 826;
this->read_position = this->capacity - 1;
this->filled_count = 0;
}
T pop();
Expand All @@ -66,7 +69,7 @@ RingBuffer<T>::RingBuffer(unsigned int capacity)
throw std::runtime_error("RingBuffer must have a capacity greater than zero");
}
this->data = new T[capacity]();
this->write_position = 0;
this->write_position = capacity - 1;
this->capacity = capacity;
}

Expand All @@ -79,8 +82,8 @@ RingBuffer<T>::~RingBuffer()
template <class T>
void RingBuffer<T>::append(T value)
{
this->data[this->write_position] = value;
this->write_position = (this->write_position + 1) % this->capacity;
this->data[this->write_position] = value;
}

template <class T>
Expand All @@ -90,6 +93,13 @@ void RingBuffer<T>::extend(T *ptr, unsigned int count)
this->append(ptr[i]);
}

template <class T>
void RingBuffer<T>::extend(std::vector<T> vec)
{
for (auto item : vec)
this->append(item);
}

template <class T>
T RingBuffer<T>::get(double index)
{
Expand All @@ -113,9 +123,9 @@ template <class T>
T RingQueue<T>::pop()
{
mutex.lock();
T rv = this->data[this->read_position];
this->read_position = (this->read_position + 1) % this->capacity;
this->filled_count--;
T rv = this->data[this->read_position];
mutex.unlock();
return rv;
}
Expand Down
5 changes: 4 additions & 1 deletion source/src/node/io/input/miniaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ void AudioIn::init()

for (int channel = 0; channel < device.capture.internalChannels; channel++)
{
input_queue.push_back(new SampleRingQueue(device.capture.internalPeriodSizeInFrames * 8));
SampleRingQueue *queue = new SampleRingQueue(device.capture.internalPeriodSizeInFrames * 8);
std::vector<float> silence(device.capture.internalPeriodSizeInFrames, 0);
queue->extend(silence);
input_queue.push_back(queue);
}

this->start();
Expand Down
2 changes: 1 addition & 1 deletion source/src/node/processors/delays/comb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void CombDelay::process(Buffer &out, int num_frames)
signalflow_audio_thread_error("CombDelay: Delay time exceeds maximum. Reduce the delay_time, or increase max_delay_time.");
}

sample rv = input->out[channel][frame] + (feedback * buffers[channel]->get(-offset));
sample rv = input->out[channel][frame] + (feedback * buffers[channel]->get(-offset + 1));
out[channel][frame] = rv;
buffers[channel]->append(rv);
}
Expand Down
2 changes: 1 addition & 1 deletion source/src/node/processors/delays/onetap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void OneTapDelay::process(Buffer &out, int num_frames)
* through the current frame immediately
*-------------------------------------------------------------------------------*/
buffers[channel]->append(this->input->out[channel][frame]);
out[channel][frame] = buffers[channel]->get(-offset - 1);
out[channel][frame] = buffers[channel]->get(-offset);
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions source/src/python/buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

void init_python_buffer(py::module &m)
{
py::class_<SampleRingBuffer>(m, "SampleRingBuffer", "A circular buffer of audio samples with a single read/write head")
.def(py::init<unsigned int>(), "capacity"_a, R"pbdoc(Create a new ring buffer)pbdoc")
.def("append", &SampleRingBuffer::append, R"pbdoc(Append an item to the ring buffer.)pbdoc")
.def(
"extend", [](SampleRingBuffer &buf, std::vector<sample> vec) { buf.extend(vec); },
R"pbdoc(Extend the ring buffer.)pbdoc")
.def("get", &SampleRingBuffer::get, R"pbdoc(Retrieve an item from the ring buffer, with offset relative to the read head.)pbdoc")
.def("get_capacity", &SampleRingBuffer::get_capacity, R"pbdoc(Returns the capacity of the ring buffer.)pbdoc");

py::class_<SampleRingQueue>(m, "SampleRingQueue", "A circular queue of audio samples with separate read/write heads")
.def(py::init<unsigned int>(), "capacity"_a, R"pbdoc(Create a new ring queue)pbdoc")
.def("append", &SampleRingQueue::append, R"pbdoc(Append an item to the ring queue.)pbdoc")
.def(
"extend", [](SampleRingQueue &buf, std::vector<sample> vec) { buf.extend(vec); },
R"pbdoc(Extend the ring queue.)pbdoc")
.def("pop", &SampleRingQueue::pop, R"pbdoc(Pop an item from the ring queue.)pbdoc")
.def("get_capacity", &SampleRingQueue::get_capacity, R"pbdoc(Returns the capacity of the ring queue.)pbdoc")
.def("get_filled_count", &SampleRingQueue::get_filled_count, R"pbdoc(Returns the number of items filled in the ring queue.)pbdoc");

/*--------------------------------------------------------------------------------
* Buffer
*-------------------------------------------------------------------------------*/
Expand Down
36 changes: 35 additions & 1 deletion tests/test_buffer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from signalflow import Buffer, Buffer2D
from signalflow import Buffer, Buffer2D, SampleRingBuffer, SampleRingQueue
from signalflow import SIGNALFLOW_INTERPOLATION_MODE_NONE, SIGNALFLOW_INTERPOLATION_MODE_LINEAR
from signalflow import GraphNotCreatedException
import numpy as np
Expand Down Expand Up @@ -202,3 +202,37 @@ def test_buffer_2d(graph):
assert b2d.get2D(1.5, 1.00) == 5

# TODO: Test with no interpolation


def test_ring_buffer():
buf = SampleRingBuffer(128)
assert buf.get_capacity() == 128

assert buf.get(0) == 0.0
buf.append(7)
buf.append(9)
buf.append(8)
assert buf.get(0) == 8
assert buf.get(-1) == 9
assert buf.get(-2) == 7

buf.extend([1, 2, 3])
assert buf.get(0) == 3
assert buf.get(-1) == 2
assert buf.get(-2) == 1

def test_ring_queue():
queue = SampleRingQueue(128)
assert queue.get_capacity() == 128
assert queue.get_filled_count() == 0
queue.append(7)
queue.append(8)
assert queue.get_filled_count() == 2
assert queue.pop() == 7
assert queue.pop() == 8
assert queue.get_filled_count() == 0

queue.extend([1, 2, 3])
assert queue.pop() == 1
assert queue.pop() == 2
assert queue.pop() == 3

0 comments on commit 6ca7a85

Please sign in to comment.