Skip to content

Commit

Permalink
Add WavetableBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
ideoforms committed Aug 13, 2024
1 parent 3cdcc8a commit 5f4afe0
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 42 deletions.
192 changes: 150 additions & 42 deletions auxiliary/libs/signalflow-stubs/signalflow.pyi

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions source/include/signalflow/buffer/wavetable-buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

/**-------------------------------------------------------------------------
* @file wavetable-buffer.h
* @brief WavetableBuffer stores an amplitude envelope from -1..1.
*
*-----------------------------------------------------------------------*/

#include "signalflow/buffer/buffer.h"
#include "signalflow/core/constants.h"
#include "signalflow/core/util.h"

#define SIGNALFLOW_DEFAULT_WAVETABLE_BUFFER_LENGTH 2048

namespace signalflow
{
/**-------------------------------------------------------------------------
* A WavetableBuffer is a mono buffer with a fixed number of samples,
* which can be sampled at a position [0,1] to give a bipolar [-1, 1]
* amplitude value, intended for use with the Wavetable node.
*-----------------------------------------------------------------------*/
class WavetableBuffer : public Buffer
{
public:
WavetableBuffer(int length = SIGNALFLOW_DEFAULT_WAVETABLE_BUFFER_LENGTH);

/**------------------------------------------------------------------------
* Initialise a buffer with the envelope `shape`.
*
* Supported buffer shapes:
*
* sine: Sine wave
* triangle: Triangle wave
*
* @param name One of the recognised shapes above
* @param num_frames Length of buffer
*
*------------------------------------------------------------------------*/
WavetableBuffer(std::string shape, int num_frames = SIGNALFLOW_DEFAULT_WAVETABLE_BUFFER_LENGTH);
WavetableBuffer(const std::function<float(float)> f);
WavetableBuffer(std::vector<float> samples);

/**------------------------------------------------------------------------
* @param position An envelope position between [0, 1].
* @return A frame position
*------------------------------------------------------------------------*/
virtual double offset_to_frame(double offset) override;
virtual double frame_to_offset(double frame) override;
};

}
1 change: 1 addition & 0 deletions source/include/signalflow/signalflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <signalflow/buffer/fftbuffer.h>
#include <signalflow/buffer/ringbuffer.h>
#include <signalflow/buffer/waveshaper-buffer.h>
#include <signalflow/buffer/wavetable-buffer.h>

/*------------------------------------------------------------------------
* Operators
Expand Down
1 change: 1 addition & 0 deletions source/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(SRC ${SRC}
${CMAKE_CURRENT_SOURCE_DIR}/buffer/buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer/envelope-buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer/waveshaper-buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer/wavetable-buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer/buffer2d.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer/fftbuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/graph.cpp
Expand Down
72 changes: 72 additions & 0 deletions source/src/buffer/wavetable-buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "signalflow/buffer/wavetable-buffer.h"

namespace signalflow
{

WavetableBuffer::WavetableBuffer(int length)
: Buffer(1, length)
{
/*-------------------------------------------------------------------------
* Initialise to a flat envelope at maximum amplitude.
*-----------------------------------------------------------------------*/
this->fill(1.0);
}

WavetableBuffer::WavetableBuffer(std::string shape, int num_frames)
: WavetableBuffer(num_frames)
{
if (shape == "sine")
{
for (int x = 0; x < num_frames; x++)
{
this->data[0][x] = sinf(2 * M_PI * x / num_frames);
}
}
else if (shape == "triangle")
{
for (int x = 0; x < num_frames / 2; x++)
this->data[0][x] = (float) 4 * x / num_frames - 1;
for (int x = 0; x < num_frames / 2; x++)
this->data[0][(num_frames / 2) + x] = 1 - 4 * (float) x / (num_frames);
}
else if (shape == "saw")
{
for (int x = 0; x < num_frames; x++)
this->data[0][x] = 2.0 * x / num_frames - 1.0;
}
else if (shape == "square")
{
for (int x = 0; x < num_frames; x++)
{
if (x < num_frames / 2)
this->data[0][x] = -1.0;
else
this->data[0][x] = 1.0;
}
}
else
{
throw std::runtime_error("Invalid wavetable buffer shape: " + shape);
}
}

WavetableBuffer::WavetableBuffer(const std::function<float(float)> f)
: Buffer(1, 1024)
{
this->fill(f);
}

WavetableBuffer::WavetableBuffer(std::vector<float> samples)
: Buffer(samples) {}

double WavetableBuffer::offset_to_frame(double offset)
{
return signalflow_scale_lin_lin(offset, 0, 1, 0, this->num_frames - 1);
}

double WavetableBuffer::frame_to_offset(double frame)
{
return signalflow_scale_lin_lin(frame, 0, this->num_frames - 1, 0, 1);
}

}
16 changes: 16 additions & 0 deletions source/src/python/buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,22 @@ void init_python_buffer(py::module &m)
.def_property_readonly("frame_offsets", &Buffer::get_frame_offsets,
R"pbdoc(Returns a list containing the offset in the envelope buffer for each frame, ranging over 0..1.)pbdoc");

py::class_<WavetableBuffer, Buffer, BufferRefTemplate<WavetableBuffer>>(m,
"WavetableBuffer",
"Buffer encapsulating a single cycle of a wavetable")
.def(py::init<int>(), "num_frames"_a,
R"pbdoc(Create a wavetable buffer containing the given number of samples.)pbdoc")
.def(py::init<std::vector<float>>(), "samples"_a,
R"pbdoc(Create n wavetable buffer containing the specified 1D array of samples.)pbdoc")
.def(py::init<std::string>(), "shape"_a,
R"pbdoc(Create a wavetable buffer with the specified shape, one of: rectangular, triangle, hanning, linear-decay.)pbdoc")
.def(py::init<std::string, int>(), "shape"_a, "num_frames"_a,
R"pbdoc(Create a wavetable buffer with the specified shape and number of frames.)pbdoc")
.def(py::init<const std::function<float(float)>>(), "function"_a,
R"pbdoc(Create a wavetable buffer filled with the output of a given function.)pbdoc")
.def_property_readonly("frame_offsets", &Buffer::get_frame_offsets,
R"pbdoc(Returns a list containing the offset in the wavetable buffer for each frame, ranging over 0..1.)pbdoc");

/*--------------------------------------------------------------------------------
* Buffer
*-------------------------------------------------------------------------------*/
Expand Down

0 comments on commit 5f4afe0

Please sign in to comment.