Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ option(NEO_BUILD_TESTS "Build unit tests" OFF)

project(neuron VERSION 0.1.0 LANGUAGES CXX)

file(GLOB_RECURSE SRC_FILES "src/*.cpp")
file(GLOB_RECURSE SRC_FILES "src/**/*.cpp")

add_library(neuron STATIC ${SRC_FILES})

Expand Down Expand Up @@ -48,10 +48,7 @@ if (NEO_BUILD_TESTS)
add_neuron_test(parameter tests/core/parameter_test.cpp)

add_neuron_test(oscillator tests/dsp/generators/oscillator_test.cpp)
add_neuron_test(adsr tests/dsp/modulators/adsr_test.cpp)
add_neuron_test(saturator tests/dsp/processors/saturator_test.cpp)
add_neuron_test(wavefolder tests/dsp/processors/wavefolder_test.cpp)
add_neuron_test(filter tests/dsp/processors/filter_test.cpp)
add_neuron_test(filter tests/dsp/effectors/filter_test.cpp)

add_neuron_test(arithmetic tests/utils/arithmetic_test.cpp)
add_neuron_test(midi tests/utils/midi_test.cpp)
Expand Down
14 changes: 6 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,25 @@ SRC_DIR = src
# Header only modules are listed commented out
# below the others.

EFFECTOR_MOD_DIR = dsp/effectors
EFFECTOR_MODULES = \
filter \

GENERATOR_MOD_DIR = dsp/generators
GENERATOR_MODULES = \
oscillator \

MODULATOR_MOD_DIR = dsp/modulators
MODULATOR_MODULES = \
adsr \

PROCESSOR_MOD_DIR = dsp/processors
PROCESSOR_MODULES = \
filter \
saturator \
wavefolder \
lfo \

######################################
# source
######################################

CPP_SOURCES += $(addsuffix .cpp, $(SRC_DIR)/$(EFFECTOR_MOD_DIR)/$(EFFECTOR_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(SRC_DIR)/$(GENERATOR_MOD_DIR)/$(GENERATOR_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(SRC_DIR)/$(MODULATOR_MOD_DIR)/$(MODULATOR_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(SRC_DIR)/$(PROCESSOR_MOD_DIR)/$(PROCESSOR_MODULES))

######################################
# building variables
Expand Down
60 changes: 54 additions & 6 deletions include/neuron/core/base.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#pragma once

#ifdef NEO_PLUGIN_SUPPORT
#include "neuron/core/context.h"
#include "neuron/dsp/modulators/modulator.h"

#if NEO_PLUGIN_SUPPORT
#include <atomic>
#endif

namespace neuron {

/**
* Describes a Neuron DSP component, capable of processing, or
* generating signals.
* Describes a "neuron", i.e. a DSP component capable of generating signals,
* processing signals, or modulating parameters of other neurons.
*/
template<class N, typename P>
class Neuron {
Expand All @@ -18,15 +21,60 @@ namespace neuron {
*/
~Neuron() = default;

#ifdef NEO_PLUGIN_SUPPORT
/**
* Sets the DSP context for this neuron, holding information
* such as sample rate, block size, and number of channels.
*/
void SetContext(Context context)
{
m_context = context;
static_cast<N*>(this)->SetContextImpl(context);
}

/**
* Attaches a modulator object to a given parameter of this neuron.
*
* CAUTION: Modulators MUST be evaluated in the audio callback before
* any other neurons. If modulator A is modulating modulator B, then A
* must be evaluated before B is. A and B must both be evaluated before any
* of the neurons they modulate are evaluated.
*/
template<class M>
void AttachModulator(P parameter, Modulator<M>* modulator)
{
static_cast<N*>(this)->AttachModulatorImpl(parameter, modulator);
}

/**
* Detaches a modulator object from a given parameter of this neuron.
*/
void DetachModulator(P parameter)
{
static_cast<N*>(this)->DetachModulatorImpl(parameter);
}

/**
* Sets the modulation depth of a given parameter of this neuron, which is a value
* between -1.0f and 1.0f.
*
* NOTE: If no modulator has been attached then this will have no effect.
*/
void SetModulationDepth(P parameter, float depth)
{
static_cast<N*>(this)->SetModulationDepthImpl(parameter, depth);
}

#if NEO_PLUGIN_SUPPORT
/**
* Attach a source via an atomic pointer to a given parameter.
*/
void AttachParameterToSource(const P parameter, std::atomic<float>* source)
void AttachParameterToSource(P parameter, std::atomic<float>* source)
{
static_cast<N*>(this)->AttachParameterToSourceImpl(parameter, source);
}
#endif
};

protected:
Context m_context = DEFAULT_CONTEXT;
};
}
43 changes: 43 additions & 0 deletions include/neuron/core/buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

/**
* A lightweight view into a contiguous block of data, acting
* as a non-owning reference to memory.
*/
namespace neuron {

template<typename DataType>
class Buffer {
public:
constexpr Buffer() noexcept
: m_data(nullptr)
, m_size(0)
{
}
constexpr Buffer(DataType* data, int size) noexcept
: m_data(data)
, m_size(size)
{
}

constexpr DataType& operator[](int index) noexcept { return m_data[index]; }
constexpr const DataType& operator[](int index) const noexcept { return m_data[index]; }

constexpr int size() const noexcept { return m_size; }
constexpr DataType* data() noexcept { return m_data; }
constexpr const DataType* data() const noexcept { return m_data; }

// Iterator support for range-based loops
constexpr DataType* begin() noexcept { return m_data; }
constexpr DataType* end() noexcept { return m_data + m_size; }
constexpr const DataType* begin() const noexcept { return m_data; }
constexpr const DataType* end() const noexcept { return m_data + m_size; }

constexpr bool empty() const noexcept { return m_size == 0; }

private:
DataType* m_data;
int m_size;
};

}
10 changes: 4 additions & 6 deletions include/neuron/core/context.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#pragma once

#include <cstddef>

namespace neuron {

/**
Expand All @@ -10,15 +8,15 @@ namespace neuron {
* that use the sample rate to calculate phase positions.
*/
struct Context {
size_t sampleRate;
size_t numChannels;
size_t blockSize;
float sampleRate;
int numChannels;
int blockSize;
};

/**
* The common default context, using a sample rate of 44.1kHz, stereo
* channel configuration, and a buffer size of 16 samples.
*/
static Context DEFAULT_CONTEXT = { 44100, 2, 16 };
static Context DEFAULT_CONTEXT = { 44100.0f, 2, 16 };

}
8 changes: 4 additions & 4 deletions include/neuron/core/parameter.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#pragma once

#ifdef NEO_PLUGIN_SUPPORT
#if NEO_PLUGIN_SUPPORT
#include <atomic>
#endif

namespace neuron {

#ifdef NEO_PLUGIN_SUPPORT
#if NEO_PLUGIN_SUPPORT
/**
* A read-only parameter used by a DSP component to allow more
* control and flexibility in shaping its sound.
Expand Down Expand Up @@ -82,8 +82,8 @@ namespace neuron {
* CAUTION: This empty value is used as a safe initializer for the pointer,
* which is what is used by the JUCE library.
*/
std::atomic<T> m_initial_source { 0.0f };
std::atomic<T>* m_parameter = &m_initial_source;
std::atomic<T> m_initialSource { 0.0f };
std::atomic<T>* m_parameter = &m_initialSource;
};

#else
Expand Down
94 changes: 94 additions & 0 deletions include/neuron/dsp/effectors/channel_router.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#pragma once

#include "neuron/core/base.h"
#include "neuron/core/buffer.h"
#include "neuron/core/context.h"
#include "neuron/core/parameter.h"
#include "neuron/core/sample.h"

namespace neuron {

/**
* Channel routing modes.
*/
enum ChannelMode {
CHANNEL_STEREO = 0, // Pass through stereo (or sum to mono if enabled)
CHANNEL_LEFT = 1, // Output left channel to both outputs
CHANNEL_RIGHT = 2, // Output right channel to both outputs
CHANNEL_SWAP = 3, // Swap left and right channels
};

enum ChannelRouterParameter {
CHANNEL_ROUTER_MODE,
CHANNEL_ROUTER_MONO,
CHANNEL_ROUTER_INVERT_LEFT,
CHANNEL_ROUTER_INVERT_RIGHT,
};

/**
* The ChannelRouter class handles stereo channel routing including:
* - Channel mode selection (stereo, left, right, swap)
* - Mono summing
* - Per-channel phase inversion
*
* This is a stereo effector that processes left and right buffers together.
*/
class ChannelRouter : public Neuron<ChannelRouter, ChannelRouterParameter> {
public:
/**
* Creates a channel router effector.
*
* @param context The DSP context
*/
explicit ChannelRouter(Context context);

/**
* Sets the channel routing mode.
*/
void SetMode(ChannelMode mode);

/**
* Enables or disables mono summing.
*/
void SetMono(bool mono);

/**
* Enables or disables left channel phase inversion.
*/
void SetInvertLeft(bool invert);

/**
* Enables or disables right channel phase inversion.
*/
void SetInvertRight(bool invert);

/**
* Processes stereo buffers in-place.
*
* @param left Left channel buffer (modified in place)
* @param right Right channel buffer (modified in place)
*/
void Effect(Buffer<Sample>& left, Buffer<Sample>& right);

protected:
friend class Neuron<ChannelRouter, ChannelRouterParameter>;
void SetContextImpl(Context context);
template<class M>
void AttachModulatorImpl(ChannelRouterParameter /* parameter */, Modulator<M>* /* modulator */)
{
// Channel router parameters are not typically modulated
}
void DetachModulatorImpl(ChannelRouterParameter parameter);
void SetModulationDepthImpl(ChannelRouterParameter parameter, float depth);
#if NEO_PLUGIN_SUPPORT
void AttachParameterToSourceImpl(ChannelRouterParameter parameter, std::atomic<float>* source);
#endif

private:
Parameter<float> p_mode;
Parameter<float> p_mono;
Parameter<float> p_invertLeft;
Parameter<float> p_invertRight;
};

}
Loading
Loading