Intrinsic functions

namespace soul::intrinsics

The intrinsics namespace contains low-level, commonly-used functions.

This is a special-case namespace, as the functions inside it can be used without needing to specify their namespace (i.e. you can just write abs(x), no need to write soul::intrinsics::abs(x)).

Many of these functions have reference implementations defined in this file, but which will be substituted for more optimal implementations by a JIT engine if possible. The [[intrin]] annotation is used as a hint to the performer back-end that this function should be replaced by a native implementation if one is available. (Note that some of the functions here have non-functional implementations and rely on the performer to use a native implementation for them to work at all).

Functions

abs

Familiar abs() function, accepting most scalar types.

[[ intrin: "abs" ]]

T.removeReference abs<T> (T n)

max

Returns the greater of two scalar values.

[[ intrin: "max" ]]

T.removeReference max<T> (T a,
                          T b)

min

Returns the lesser of two scalar values.

[[ intrin: "min" ]]

T.removeReference min<T> (T a,
                          T b)

min

Returns the lesser of two scalar values.

[[ intrin: "min" ]]

int32 min (int32 a,
           int32 b)

clamp

Clamps a scalar value to the nearest value within a given range.

[[ intrin: "clamp" ]]

T.removeReference clamp<T> (T n,
                            T low,
                            T high)

wrap

Performs a negative-number-aware modulo operation to wrap a number to a zero-based range.

[[ intrin: "wrap" ]]

T.removeReference wrap<T> (T n,
                           T range)

wrap

Performs a negative-number-aware integer modulo operation.

[[ intrin: "wrap" ]]

int32 wrap (int32 n,
            int32 range)

floor

Performs a C++-compatible floor function on a scalar floating point value.

[[ intrin: "floor" ]]

T.removeReference floor<T> (T n)

ceil

Performs a C++-compatible ceil function on a scalar floating point value.

[[ intrin: "ceil" ]]

T.removeReference ceil<T> (T n)

lerp

Returns a linearly-interpolated value between two scalar values.

T.removeReference lerp<T> (T start,
                           T stop,
                           T amount)

fmod

Performs a C++-compatible fmod function on some scalar floating point values.

[[ intrin: "fmod" ]]

T.removeReference fmod<T> (T x,
                           T y)

remainder

Performs a C++-compatible remainder function on some scalar floating point values.

[[ intrin: "remainder" ]]

T.removeReference remainder<T> (T x,
                                T y)

sqrt

Returns the square root of a scalar floating point value.

[[ intrin: "sqrt" ]]

T.removeReference sqrt<T> (T n)

pow

Raises a scalar floating point value to the given power.

[[ intrin: "pow" ]]

T.removeReference pow<T> (T a,
                          T b)

exp

Returns the exponential of a scalar floating point value.

[[ intrin: "exp" ]]

T.removeReference exp<T> (T n)

log

Returns the log-e of a scalar floating point value.

[[ intrin: "log" ]]

T.removeReference log<T> (T n)

log10

Returns the log 10 of a scalar floating point value.

[[ intrin: "log10" ]]

T.removeReference log10<T> (T n)

roundToInt

Rounds a floating point number up or down to the nearest integer.

[[ intrin: "roundToInt" ]]

int32 roundToInt (float32 n)

roundToInt

Rounds a floating point number up or down to the nearest integer.

[[ intrin: "roundToInt" ]]

int64 roundToInt (float64 n)

isnan

Returns true if the floating point argument is a NaN.

[[ intrin: "isnan" ]]

bool isnan<T> (T n)

isinf

Returns true if the floating point argument is an INF.

[[ intrin: "isinf" ]]

bool isinf<T> (T n)

addModulo2Pi

Adds a delta to a value, and returns the resulting value modulo PI/2. A typical use-case for this is in incrementing the phase of an oscillator.

[[ intrin: "addModulo2Pi" ]]

T.removeReference addModulo2Pi<T> (T value,
                                   T increment)

sum

Returns the sum of an array or vector of scalar values.

T.elementType sum<T> (T t)

product

Returns the product of an array or vector of scalar values.

T.elementType product<T> (T t)

read

Reads an element from an array, allowing the index to be any type of floating point type. If a floating point index is used, it will be rounded down to an integer index - for an interpolated read operation, see readLinearInterpolated(). Indexes beyond the range of the array will be wrapped.

[[ intrin: "read" ]]

Array.elementType read<Array, IndexType> (const Array& array,
                                          IndexType index)

readLinearInterpolated

Reads a linearly-interpolated value from an array of some kind of scalar values (probably a float or float-vector type). Indexes beyond the range of the array will be wrapped.

[[ intrin: "readLinearInterpolated" ]]

Array.elementType readLinearInterpolated<Array, IndexType> (const Array& array,
                                                            IndexType index)

get_array_size

NB: this is used internally, not something you'd want to call from user code

[[ intrin: "get_array_size" ]]

int32 get_array_size<Array> (const Array& array)

Miscellaneous audio utilities

This module is a collection of commonly-used audio helper functions and types.

namespace soul

Functions

dBtoGain

Converts a decibel level to a gain factor.

float32 dBtoGain (float32 decibels)

dBtoGain

Converts a decibel level to a gain factor.

float64 dBtoGain (float64 decibels)

gainTodB

Converts a gain factor to a decibel level.

float32 gainTodB (float32 gain)

gainTodB

Converts a gain factor to a decibel level.

float64 gainTodB (float64 gain)

noteNumberToFrequency

Converts a MIDI note (usually in the range 0-127) to a frequency in Hz.

float32 noteNumberToFrequency (int32 note)

noteNumberToFrequency

Converts a MIDI note (usually in the range 0-127) to a frequency in Hz.

float32 noteNumberToFrequency (float32 note)

frequencyToNoteNumber

Converts a frequency in Hz to an equivalent MIDI note number.

float32 frequencyToNoteNumber (float32 frequency)

getSpeedRatioForPitchedSample

Returns the ratio by which a sample's playback must be sped-up in order to map from its native sample-rate and note to a target sample-rate and note.

float64 getSpeedRatioForPitchedSample (float64 sourceSampleRate,
                                       float32 sourceMIDINote,
                                       float64 targetSampleRate,
                                       float32 targetMIDINote)

tau2pole

Returns the coefficient for a filter pole, based on a t60 decay time in seconds.

float64 tau2pole (float64 t60,
                  float64 sampleRate)

tau2pole

Returns the coefficient for a filter pole, based on a t60 decay time in seconds.

float32 tau2pole (float32 t60,
                  float32 sampleRate)

pole2tau

Returns the t60 decay time in seconds for a filter pole coefficient in the range 0 to 1.

float64 pole2tau (float64 pole,
                  float64 sampleRate)

pole2tau

Returns the t60 decay time in seconds for a filter pole coefficient in the range 0 to 1.

float32 pole2tau (float32 pole,
                  float32 sampleRate)

namespace soul::audio_samples

This namespace contains some handy stuctures to use when declaring external variables which are going to be loaded with data from audio files.

Structures

An external variable declared with the type soul::audio_samples::Mono can be loaded with monoised data from an audio file.

struct Mono
{

float32[] frames;
float64 sampleRate;

}

An external variable declared with the type soul::audio_samples::Stereo can be loaded with stereo data from an audio file.

struct Stereo
{

float32<2>[] frames;
float64 sampleRate;

}

namespace soul::pan_law

This namespace contains various pan-related helper functions

Functions

linear

Applies a simple linear pan law to convert a pan position (-1.0 to 1.0) to a (left, right) pair of stereo gains.

float32<2> linear (float32 pan)

centre3dB

Applies a 3dB-centre pan law to convert a pan position (-1.0 to 1.0) to a (left, right) pair of stereo gains.

float32<2> centre3dB (float32 pan)

namespace soul::smoother

Functions for applying a smoothing filter on a changing value. A smoother is useful for avoiding discontinuities in control values such as parameter changes, etc.

Structures

struct State
{

float32 currentValue;
float32 targetValue;
float32 increment;
int32 steps;

}

Functions

reset

Resets a smoother::State object

void reset (State& state,
            float32 initialValue)

setTarget

Updates a smoother::State object with a new target value and the number of steps to reach the target

void setTarget (State& state,
                float32 targetValue,
                int32 steps)

tick

Advances the state of a smoother and returns the new smoothed value

float32 tick (State& state)

Musical note utilities

This module contains various structs which can be used to model note-control events coming from sources such as MIDI keyboards.

The SOUL policy is to keep the use of actual MIDI to a minimum, so we encourage incoming MIDI data to be converted into these strongly-typed structures, and to use them internally, rather than attempting to work with MIDI bytes like it's the 1980s.

namespace soul::note_events

This namespace contains some types which are handy for representing synthesiser note events. They do a similar job to MIDI events, but as strongly-typed structs instead of a group of bytes. Things like the midi::MPEParser class generate them.

The events do contain a channel number, in the same way that MIDI does, but there are no restrictions on its range.

Structures

Represents a note-on (key-down) event.

struct NoteOn
{

int32 channel;
float32 note;
float32 velocity;

}

Represents a note-off (key-up) event.

struct NoteOff
{

int32 channel;
float32 note;
float32 velocity;

}

Represents a change to the pitch that should be applied to any notes being played on the channel specified.

struct PitchBend
{

int32 channel;
float32 bendSemitones;

}

Represents a change to the pressure that should be applied to any notes being played on the channel specified.

struct Pressure
{

int32 channel;
float32 pressure;

}

Represents a change to the Y-axis parameter that should be applied to any notes being played on the channel specified.

struct Slide
{

int32 channel;
float32 slide;

}

Represents a change to a user-defined control parameter that should be applied to any notes being played on the channel specified.

struct Control
{

int32 channel;
int32 control;
float32 value;

}

namespace soul::voice_allocators

Simple voice allocation helpers, which take a single stream of input events, and redirect them to an array of target voice processors.

Specialisation Parameters

namespace soul::voice_allocators (int32 mpeMasterChannel = 0)

processor soul::voice_allocators::Basic

A simple voice-allocator which will find either an inactive voice, or the least-recently used active voice if it needs to steal one.

[[ main: false ]]

Specialisation Parameters

processor soul::voice_allocators::Basic (int32 voiceCount)

Structures

struct VoiceInfo
{

bool active;
int32 channel;
float32 note;
int32 voiceAge;
bool noteReleased;

}

Variables

int32 nextAllocatedVoiceAge = 1000000000

int32 nextUnallocatedVoiceAge = 1

int32 sustainCC = 64

VoiceInfo[ voiceCount] voiceInfo

bool[16] channelSustainActive

bool masterSustainActive

Mix and gain control utilities

This file provides a set of processors for common tasks like mixing together sources, applying fixed gains, or applying envelope shapes.

namespace soul::mixers

This namespace contains a set of processors for common tasks like mixing together sources.

processor soul::mixers::FixedSum

Simple processor which adds two sources together using fixed gains for each source.

Specialisation Parameters

processor soul::mixers::FixedSum (using SampleType,
                                  float32 gain1,
                                  float32 gain2)

Inputs

Outputs

processor soul::mixers::DynamicSum

Simple processor which adds two sources together using streams to control the gains to apply to each source.

Specialisation Parameters

processor soul::mixers::DynamicSum (using SampleType)

Inputs

  • stream in1 (SampleType)
  • stream in2 (SampleType)
  • stream gain1 (float32)
  • stream gain2 (float32)

Outputs

processor soul::mixers::DynamicMix

Simple processor which mixes together two sources, using a stream of values to indicate the ratio.

The mixRange constant allows you to set the range of values that will be passed in the mix stream, so e.g. mixRange = 1.0 means that mix will be in the range 0 to 1, and mixRange = 100 means the values will be 0 to 100. The mix stream is expected to contain values between 0 and mixRange, where mix = 0 produces 100% in1, and mix = mixRange produces 100% in2.

Specialisation Parameters

processor soul::mixers::DynamicMix (using SampleType,
                                    float32 mixRange)

Inputs

Outputs

namespace soul::gain

Utility processors for common tasks like applying gain in various ways.

processor soul::gain::FixedGain

Simple processor which applies a fixed gain to a signal.

Specialisation Parameters

processor soul::gain::FixedGain (using SampleType,
                                 float32 fixedGain)

Inputs

Outputs

processor soul::gain::DynamicGain

Simple processor which applies a changeable gain level to a signal.

Specialisation Parameters

processor soul::gain::DynamicGain (using SampleType)

Inputs

  • stream in (SampleType)
  • stream gain (float32)

Outputs

processor soul::gain::SmoothedGainParameter

Converts an input event parameter in decibels to a smoothed stream of raw gain levels.

Specialisation Parameters

processor soul::gain::SmoothedGainParameter (float32 slewRateSeconds)

Inputs

  • event volume (float32)

Outputs

  • stream gain (float32)

Variables

float32 targetGain

float32 currentGain

float32 increment

int32 remainingRampSamples

graph soul::gain::SmoothedGain

A graph that combines DynamicGain and SmoothedGainParameter

Specialisation Parameters

graph soul::gain::SmoothedGain (float32 slewRateSeconds = 0.1f)

Inputs

  • stream in (float32)
  •  volume ()

Outputs

  • stream out (float32)

Processor Instances

gainProcessor = soul::gain::DynamicGain float32

Connections

gainParameter.gain -> gainProcessor.gain

in -> gainProcessor.in

gainProcessor.out -> out

namespace soul::envelope

Generators for common envelope shapes.

processor soul::envelope::FixedAttackReleaseEnvelope

Creates an envelope which applies convex attack and release curves based on a stream of NoteOn and NoteOff events.

The envelope implements fixed-length attack and release ramps where the hold level is based on the velocity of the triggering NoteOn event, multiplied by the holdLevelMultiplier parameter.

Specialisation Parameters

processor soul::envelope::FixedAttackReleaseEnvelope (float32 holdLevelMultiplier,
                                                      float32 attackTimeSeconds,
                                                      float32 releaseTimeSeconds)

Outputs

  • stream levelOut (float32)

Variables

bool active = false

float32 targetLevel

RNGs and noise-generators

This module contains a range of simple RNG and noise generating functions and processors.

namespace soul::random

This namespace contains some random number generation helpers. We're assuming that nobody's going to be using these RNGs for security-critical cryptographic applications. All the algorithms here are chosen to be fast, and definitely not cryptographically strong!

Structures

State for a Park-Miller random number generator.

struct RandomNumberState
{

The current seed.

Top tip: when generating a seed, you might want to use the processor.id constant, to make sure that each instance of a processor has a differently-seeded RNG. If you want the RNG to be different each time the program runs, you could also throw the processor.session constant into the mix too.

For example:

processor MyProcessorUsingRandomNumbers { ...etc... soul::random::RandomNumberState rng; let mySeed = 12345; // Whenever seeding a RNG, you should pick a 'salt' value // that's as unique as possible void run() { // Initialising it like this will produce the same sequence of numbers for // every instance of this processor: rng.reset (mySeed); // This will result in each instance of this processor generating a different // sequence, but each time you load and run the program, you may get the // same sequences as the last run: rng.reset (processor.id + mySeed); // This will result in each instance of this processor generating the same // sequence, but it will be different each time you load and run the program: rng.reset (processor.session + mySeed); // This will result in each instance of this processor producing a different // sequence, and each will also be different each time you load and run the program: rng.reset (processor.session + processor.id + mySeed); } }
int64 seed;

}

Functions

reset

Resets an RNG state object with the given seed value.

void reset (RandomNumberState& state,
            int64 seed)

getNextInt32

Returns the next number in the full 32-bit integer range.

int32 getNextInt32 (RandomNumberState& state)

getNextUnipolar

Advances the given RNG state and returns a value 0 to 1.

float32 getNextUnipolar (RandomNumberState& state)

getNextBipolar

Advances the given RNG state and returns a value -1 to 1.

float32 getNextBipolar (RandomNumberState& state)

namespace soul::noise

This namespace contains generators for various flavours of noise.

processor soul::noise::White

White noise generator

Outputs

  • stream out (float32)

processor soul::noise::Brown

Brown noise generator

Outputs

  • stream out (float32)

processor soul::noise::Pink

Pink noise generator

Outputs

  • stream out (float32)

Timeline event utilities

The timeline namespace contains various structs and functions which are used when dealing with positions and tempos along a timeline.

namespace soul::timeline

The timeline namespace contains various structs and functions which are used when dealing with positions and tempos along a timeline.

Structures

Represents a simple time-signature.

struct TimeSignature
{

int32 numerator;
int32 denominator;

}

Represents a tempo in BPM.

struct Tempo
{

float32 bpm;

}

Represents the state of a host which can play timeline-based material.

struct TransportState
{

In the absence of enums, the valid values for the state are: 0 = stopped, 1 = playing, 2 = recording.

int32 state;

}

Represents a position along a timeline, in terms of frames and also (where appropriate) quarter notes.

struct Position
{

A number of frames from the start of the timeline.

int64 currentFrame;

The number of quarter-notes since the beginning of the timeline. A host may not have a meaningful value for this, so it may just be 0. Bear in mind that a timeline may contain multiple changes of tempo and time-signature, so this value will not necessarily keep increasing at a constant rate.

float64 currentQuarterNote;

The number of quarter-notes from the beginning of the timeline to the start of the current bar. A host may not have a meaningful value for this, so it may just be 0. You can subtract this from currentQuarterNote to find out how which quarter-note the position represents within the current bar.

float64 lastBarStartQuarterNote;

}

Functions

quarterNotesPerBeat

float32 quarterNotesPerBeat (TimeSignature timeSig)

beatsPerQuarterNote

float32 beatsPerQuarterNote (TimeSignature timeSig)

secondsPerBeat

float32 secondsPerBeat (Tempo tempo)

secondsPerQuarterNote

float32 secondsPerQuarterNote (Tempo tempo,
                               TimeSignature timeSig)

framesPerBeat

float64 framesPerBeat (Tempo tempo,
                       float64 sampleRate)

framesPerQuarterNote

float64 framesPerQuarterNote (Tempo tempo,
                              TimeSignature timeSig,
                              float64 sampleRate)

isStopped

bool isStopped (TransportState t)

isPlaying

bool isPlaying (TransportState t)

isRecording

bool isRecording (TransportState t)

Oscillators

This module contains a collection of oscillator algorithms for generating various wave-shapes. The collection covers low frequency designs and high quality alias free algotithms.

namespace soul::oscillators

The oscillators namespace is parameterised with a SampleType and PhaseType. The SampleType specifies the output type, and can be either float32 or float64. The PhaseType is used internally to track phase position, so higher resolution data types can be used to reduce frequency inaccuracy for very critical applications.

By default the SampleType and PhaseType are float32.

Specialisation Parameters

namespace soul::oscillators (using SampleType = float32,
                             using PhaseType = float32)

Variables

float32 minFreqHz = 0.0f

float32 maxFreqHz = 22000.0f

float32 defaultFreqHz = 1000.0f

namespace soul::oscillators::phasor

A unipolar ramp (phasor) oscillator. This is a non-bandlimited oscillator that will cause aliasing, but is used internally by the BLEP oscillator.

Structures

struct State
{

The phase uses a range 0 to 1.0

PhaseType phase;

The increment of phase per frame

PhaseType phaseIncrement;

}

Functions

reset

Resets a phasor::State object

void reset (State& s)

update

Updates a phasor::State object for a new rate

void update (State& s,
             float64 samplePeriod,
             float64 freqHz)

process

Increments a phasor::State object and returns the new phase

PhaseType process (State& s)

processor soul::oscillators::phasor::Processor

A processor that produces a stream of phase values as its output.

Specialisation Parameters

processor soul::oscillators::phasor::Processor (float32 initialFrequency = 1000)

Inputs

  • event frequencyIn (float32)

Outputs

Variables

State s

processor soul::oscillators::Sine

A simple sinewave oscillator which receives input events to control its frequency.

Specialisation Parameters

processor soul::oscillators::Sine (float32 initialFrequency = 1000)

Inputs

  • event frequencyIn (float32)

Outputs

Variables

float32 phaseIncrement = float (initialFrequency * twoPi * processor.period)

namespace soul::oscillators::poly_blep

A semi band-limited oscillator with sawtooth, square and triangle wave shapes using the PolyBLEP (Polynomial Band-Limited Step) technique. You may want to oversample this oscillator, in order to reduce aliasing.

namespace soul::oscillators::poly_blep::shapers

Contains different wave-shaping functions.

Functions

polyblep

Generates a polyblep

PhaseType polyblep (PhaseType phase,
                    PhaseType phaseIncrement)

sawtooth

Generates a sawtooth wave from a phasor state

PhaseType sawtooth (phasor::State& s)

square

Generates a square wave from a phasor state

PhaseType square (phasor::State& s)

namespace soul::oscillators::poly_blep::Shape

Contains constants for different wave-shapes

Variables

int32 sawtooth = 0

int32 triangle = 1

int32 square = 2

processor soul::oscillators::poly_blep::Processor

A processor which generates a dynamically adjustable wave-shape.

Specialisation Parameters

processor soul::oscillators::poly_blep::Processor (int32 initialShape = 0,
                                                   float32 initialFrequency = 1000)

Inputs

  • event shapeIn (float32)
  • event frequencyIn (float32)

Outputs

Variables

wrap<3> shape = wrap<3> (initialShape)

namespace soul::oscillators::quadrature

A quadrature sinusoidal oscillator producing sine and cosine outputs simultaneously. The updateInterval defines the samples between updates to the frequency taking effect. https://vicanek.de/articles/QuadOsc.pdf

Specialisation Parameters

namespace soul::oscillators::quadrature (int32 updateInterval = 16)

Structures

struct State
{

}

struct Coeffs
{

}

Functions

reset

Resets a quadrature::State object.

void reset (State& s)

update

Updates a quadrature::State object for a new rate.

void update (Coeffs& c,
             float64 freqHz,
             float64 sampleRate)

process

Generates the next samples for a quadrature::State object.

SampleType[2] process (State& s,
                       Coeffs& c)

processor soul::oscillators::quadrature::Processor

A processor that generates a pair of sine/cosine output streams.

Specialisation Parameters

processor soul::oscillators::quadrature::Processor (float32 initialFrequency = 1000)

Inputs

  • event frequencyIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

bool recalc = true

namespace soul::oscillators::lfo

A tempo-syncable LFO with some common shapes and options for uni-polar or bi-polar output. Unipolar LFO shapes run between 0 and 1, whilst Bipolar run between -1 and 1.

namespace soul::oscillators::lfo::shapers

Various LFO shape generator functions.

namespace soul::oscillators::lfo::Shape

A set of constants to specify different LFO shapes.

Variables

int32 triangle = 0

int32 square = 1

int32 rampUp = 2

int32 rampDown = 3

int32 sine = 4

int32 sampleAndHold = 5

namespace soul::oscillators::lfo::Polarity

Polarity control constants.

Variables

int32 unipolar = 0

int32 bipolar = 1

processor soul::oscillators::lfo::Processor

A processor which implements an LFO with events to control its parameters. Changes to the frequency and depth are smoothed, but expect discontinuities if the shape or polarity are updated.

Specialisation Parameters

processor soul::oscillators::lfo::Processor (int32 initialShape = 0,
                                             int32 initialPolarity = 0,
                                             float32 initialDepth = 1.0f,
                                             float32 initialFrequency = 1.0f)

Inputs

Outputs

Variables

PhaseType phase

float32 phaseIncrement = float32 (initialFrequency * processor.period)

int32 shape = initialShape

int32 polarity = initialPolarity

int32 smoothingSamples = int (float (processor.frequency) * 0.02f)

bool transportRunning = false

bool qnMode = false

bool timelineSync = false

float64 qnScalar = 1.0

float64 qnPos = 0.0

float32 qnPhaseIncrement = (120.0f / 60.0f) * (1.0f / 44100.0f)

PhaseType prevPhase = 1.0f

SampleType noiseSample

Filters

This module contains a collection of filter implementations, including more traditional biquad based filters and TPT variants, which are more suitable for modulation.

Notes:

  • Filter coefficients are typically labelled a and b. Different literature uses different conventions for what a and b mean. This code uses the following:
    • b: feed-forward, numerator, zero, FIR coefficients.
    • a: feed-back, denominator, pole, IIR coefficients.
  • Although each filter comes with a Processor, they are designed so that their state, coefficients and functions can be used independently.
  • Each filter has a State struct and a Coeffs struct.
  • process() functions generate an output y for an input x.
  • update() functions take high level parameters such as mode, frequency, Q, mode and update the filter coefficients.
  • reset() functions reset the filter state/histories to 0.
  • clear() functions clears filter coefficients.
  • The filters are designed for channel-wise vectorisation, which can be achieved by specialising the filter's namespace with a vector type.
  • Some filters are suitable for modulation, others are not, check the comments at the top of the namespace.

namespace soul::filters

Specialisation Parameters

namespace soul::filters (using SampleType = float32,
                         using CoeffType = float64,
                         int32 updateInterval = 16)

Variables

float32 minFreqHz = 5.0f

Processor parameter min/max/defaults

float32 maxFreqHz = 22000.0f

float32 defaultFreqHz = 1000.0f

float32 defaultQuality = 0.707107f

float32 defaultGain = 0.f

float64 normalisedFreqLimit = 0.49

The frequency upper-bound for update functions. (Just below nyquist, e.g 0.49 * 44100 = ~22kHz).

namespace soul::filters::dc_blocker

Highpass filter for removing DC offset.

y[n] = x[n] - x[n-1] + b0 * y[n-1]

https://ccrma.stanford.edu/~jos/fp/DC_Blocker.html

Structures

Holds a set of filter coefficients.

struct Coeffs
{

}

Holds the filter state.

struct State
{

SampleType[1] x;
SampleType[1] y;

}

Functions

reset

Resets a filter state.

void reset (State& s)

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             float64 freqHz)

processor soul::filters::dc_blocker::Processor

Specialisation Parameters

processor soul::filters::dc_blocker::Processor (float64 frequency = 30.0f)

Inputs

Outputs

namespace soul::filters::biquad

Biquadratic (two-pole-two-zero) IIR filter building block.

Direct Form I (DFI):

y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a2 * y[n-2]

Transposed Direct Form II (TDFII):

y[n] = b0 * x[n] + s1 s1 = b1 * x[n] + a1 * y[n] + s2 s2 = b2 * x[n] + a2 * y[n]

Structures

Holds a set of filter coefficients.

struct Coeffs
{

CoeffType<3> b;

feed-forward, numerator, zero, FIR coefficients

CoeffType<3> a;

}

Holds the filter state.

struct State
{

SampleType[2] x;
SampleType[2] y;

}

Functions

set

Initialises a set of coefficients.

void set (Coeffs& c,
          CoeffType b0,
          CoeffType b1,
          CoeffType b2,
          CoeffType a0,
          CoeffType a1,
          CoeffType a2)

setNonNormalised

Sets the coefficients, normalising based on the first feedback coefficient v.a[0]

void setNonNormalised (Coeffs& c,
                       const Coeffs& v)

setNormalised

Sets the coefficients, assuming v is already normalised.

void setNormalised (Coeffs& c,
                    Coeffs v)

clear

Clears a set of coefficients.

void clear (Coeffs& c)

reset

Resets a filter state.

void reset (State& s)

processDFI

SampleType processDFI (State& s,
                       SampleType x,
                       Coeffs& c)

processTDFII

SampleType processTDFII (State& s,
                         SampleType x,
                         Coeffs& c)

processOnePole

Like processTDFII, but optimised for c.b[2] and c.a[2] == 0.

SampleType processOnePole (State& s,
                           SampleType x,
                           Coeffs& c)

processCascadeDFI

SampleType processCascadeDFI<StateArrayType, CoeffsArrayType> (SampleType x,
                                                               StateArrayType& s,
                                                               CoeffsArrayType& c)

processCascadeTDFII

SampleType processCascadeTDFII<StateArrayType, CoeffsArrayType> (SampleType x,
                                                                 StateArrayType& s,
                                                                 CoeffsArrayType& c)

namespace soul::filters::onepole

First-order IIR filter.

y[n] = b0 * x[n] - a1 * y[n-1]

Coefficients derived from Pirkle. This filter is not suitable for modulation.

Functions

update

Updates a set of coefficients for the given settings.

void update (biquad::Coeffs& c,
             float64 sampleRate,
             int32 mode,
             float64 freqHz)

namespace soul::filters::onepole::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

int32 allpass = 2

processor soul::filters::onepole::Processor

A processor to render a onepole filter.

Specialisation Parameters

processor soul::filters::onepole::Processor (int32 initialMode = 0,
                                             float32 initialFrequency = defaultFreqHz)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event modeIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

int32 mode = initialMode

bool recalc = true

namespace soul::filters::rbj_eq

RBJ biquad EQ, 2nd Order IIR Filter.

This filter is not suitable for modulation. See https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html

Functions

update

Updates a set of coefficients for the given settings.

void update (biquad::Coeffs& c,
             float64 sampleRate,
             int32 mode,
             float64 freqHz,
             float64 quality,
             float64 gaindB)

namespace soul::filters::rbj_eq::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

int32 bandpass = 2

int32 lowShelf = 3

int32 highShelf = 4

int32 peaking = 5

int32 notch = 6

int32 allpass = 7

processor soul::filters::rbj_eq::Processor

Specialisation Parameters

processor soul::filters::rbj_eq::Processor (int32 initialMode = 0,
                                            float32 initialFrequency = defaultFreqHz,
                                            float32 initialQuality = defaultQuality,
                                            float32 initialGain = defaultGain)

Inputs

  • stream in (SampleType)
  • event modeIn (float32)
  • event frequencyIn (float32)
  • event qualityIn (float32)
  • event gainIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

float32 quality = initialQuality

float32 gain = initialGain

int32 mode = initialMode

bool recalc = true

namespace soul::filters::sos_cascade

Generic second-order-section cascade biquad processors.

processor soul::filters::sos_cascade::Processor

Supply an array of coefficients for each SOS. The size of the array should be a multiple of 6, and the coefficients are expected to be normalised already

Specialisation Parameters

processor soul::filters::sos_cascade::Processor (const CoeffType[] coeffs)

Inputs

Outputs

Variables

numSOS = size (coeffs) / 6

namespace soul::filters::butterworth

N-th order Butterworth filter, made by cascading multiple second order sections.

This filter is not suitable for modulation.

Functions

update

Updates a set of coefficients for the given settings.

void update<CoeffsArrayType> (CoeffsArrayType& coeffs,
                              float64 sampleRate,
                              int32 order,
                              int32 mode,
                              float64 freqHz)

namespace soul::filters::butterworth::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

processor soul::filters::butterworth::Processor

Butterworth processor. The order must be > 0

Specialisation Parameters

processor soul::filters::butterworth::Processor (int32 order,
                                                 int32 initialMode = 0,
                                                 float32 initialFrequency = defaultFreqHz)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event modeIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

int32 mode = initialMode

bool recalc = true

bool clear = true

namespace soul::filters::analytic

Analytic filter / IIR Hilbert transformer.

https://dsp.stackexchange.com/a/59157

Increasing numFilters will increase the accuracy of the quadrature output across the pass band but introduce more delay with respect to the input signal. The filter passband is from transitionBandwidthHz to nyquist - transitionBandwidthHz.

Specialisation Parameters

namespace soul::filters::analytic (int32 numFilters = 6,
                                   float32 transitionBandwidthHz = 20.0f)

Structures

Holds the filter state.

struct State
{

SampleType[1] x;

}

Functions

update

Updates a filter state with the given sample rate.

void update (State& s,
             float64 sampleRate)

process

SampleType[2] process (State& s,
                       SampleType x)

namespace soul::filters::analytic::polyphase_iir_design

Polyphase IIR Designer. Based on HIIR http://ldesoras.free.fr/prod.html#src_hiir

Structures

Holds a set of filter coefficients.

struct Coeffs
{

}

struct TransitionParams
{

float64 k;
float64 q;

}

Functions

compute

Coeffs compute (float64 transition)

Variables

numCoefficients = numFilters * 2

order = numCoefficients * 2 + 1

namespace soul::filters::analytic::dual_apf

Parallel 2nd-order all-pass.

y[n] = c * (x[n] + y[n - 2]) - x[n - 2]

Structures

Holds the filter state.

struct State
{

SampleType[2] x1;
SampleType[2] y1;
SampleType[2] x2;
SampleType[2] y2;
CoeffType[2] c;

}

Functions

process

SampleType[2] process (State& s,
                       SampleType[2] x)

processor soul::filters::analytic::Processor

Inputs

Outputs

Variables

State s

namespace soul::filters::complex_resonator

Complex Resonator filter.

See https://ccrma.stanford.edu/~jos/smac03maxjos/smac03maxjos.pdf This filter is suitable for modulation

Structures

Holds the filter state.

struct State
{

SampleType yReal;
SampleType yImag;

}

Holds a set of filter coefficients.

struct Coeffs
{

complex64 v;

}

Functions

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             float64 freqHz,
             float64 t60)

process

SampleType[2] process (State& s,
                       Coeffs& c,
                       SampleType x)

processor soul::filters::complex_resonator::Processor

Specialisation Parameters

processor soul::filters::complex_resonator::Processor (float32 initialFrequency = defaultFreqHz,
                                                       float32 initialDecay = 1.0f,
                                                       float32 initialGain = 1.0f)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event decayIn (float32)
  • event gainIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

float32 decay = initialDecay

float32 gain = initialGain

bool recalc = true

namespace soul::filters::tpt

This namespace contains a set of "Topology preserving transform" filters.

namespace soul::filters::tpt::onepole

"Topology preserving transform" one-pole filter.

Derived from work by Zavalishin and Pirkle. This filter is suitable for modulation.

Structures

Holds a set of filter coefficients.

struct Coeffs
{

}

Holds the filter state.

struct State
{

}

Functions

clear

Clears a set of coefficients.

void clear (Coeffs& c)

reset

Resets a filter state.

void reset (State& s)

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             float64 freqHz)

processLPF

SampleType processLPF (State& s,
                       SampleType x,
                       Coeffs& c)

processHPF

SampleType processHPF (State& s,
                       SampleType x,
                       Coeffs& c)

processAPF

SampleType processAPF (State& s,
                       SampleType x,
                       Coeffs& c)

namespace soul::filters::tpt::onepole::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

int32 allpass = 2

processor soul::filters::tpt::onepole::Processor

Specialisation Parameters

processor soul::filters::tpt::onepole::Processor (int32 initialMode = 0,
                                                  float32 initialFrequency = defaultFreqHz)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event modeIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

int32 mode = initialMode

bool recalc = true

namespace soul::filters::tpt::svf

"Topology preserving transform" multi-mode state variable filter (SVF).

Derived from work by Zavalishin and Pirkle. This filter is suitable for modulation.

Structures

Holds a set of filter coefficients.

struct Coeffs
{

}

Holds the filter state.

struct State
{

SampleType[2] z;

}

Functions

reset

Resets a filter state.

void reset (State& s)

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             float64 freqHz,
             float64 quality)

process

SampleType[3] process (State& s,
                       SampleType x,
                       Coeffs& c)

namespace soul::filters::tpt::svf::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

int32 bandpass = 2

processor soul::filters::tpt::svf::Processor

Specialisation Parameters

processor soul::filters::tpt::svf::Processor (float32 initialFrequency = defaultFreqHz,
                                              float32 initialQuality = defaultQuality)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event qualityIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

float32 quality = initialQuality

bool recalc = true

namespace soul::filters::tpt::butterworth

N-th order Butterworth filter, made by cascading TPT filters. This filter is suitable for modulation

Functions

update

Updates a set of coefficients for the given settings.

void update<SVFCoeffsArrayType> (SVFCoeffsArrayType& svfCoeffs,
                                 onepole::Coeffs& onepoleCoeffs,
                                 float64 sampleRate,
                                 int32 order,
                                 float64 freqHz)

process

SampleType process<StateArrayType, CoeffsArrayType> (SampleType x,
                                                     StateArrayType& svfStates,
                                                     CoeffsArrayType& svfCoeffs,
                                                     onepole::State& onepoleState,
                                                     onepole::Coeffs& onepoleCoeffs,
                                                     int32 mode,
                                                     bool oddOrder)

namespace soul::filters::tpt::butterworth::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

processor soul::filters::tpt::butterworth::Processor

Butterworth processor. The order must be > 1

Specialisation Parameters

processor soul::filters::tpt::butterworth::Processor (int32 order,
                                                      int32 initialMode = 0,
                                                      float32 initialFrequency = defaultFreqHz)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event modeIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

int32 mode = initialMode

bool recalc = true

namespace soul::filters::tpt::crossover

4th-order Linkwitz-Riley crossover filter, which outputs two bands of audio.

The channels should sum together to produce a flat response. This filter is suitable for modulation.

Structures

Holds the filter state.

struct State
{

svf::State svf1;
svf::State svf2;

}

Holds a set of filter coefficients.

struct Coeffs
{

}

processor soul::filters::tpt::crossover::Processor

Specialisation Parameters

processor soul::filters::tpt::crossover::Processor (float32 initialFrequency = defaultFreqHz)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)

Outputs

Functions

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             float64 freqHz)

Variables

float32 frequency = initialFrequency

bool recalc = true

namespace soul::filters::tpt::simper_eq

SVF EQ.

Based on the work of Andy Simper: https://cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf

This filter is suitable for modulation.

Structures

Holds a set of filter coefficients.

struct Coeffs
{

}

Holds the filter state.

struct State
{

SampleType ic1eq;
SampleType ic2eq;

}

Functions

reset

Resets a filter state.

void reset (State& s)

update

Updates a set of coefficients for the given settings.

void update (Coeffs& c,
             float64 sampleRate,
             int32 mode,
             float64 freqHz,
             float64 quality,
             float64 gain)

process

SampleType process (State& s,
                    SampleType x,
                    Coeffs& c)

namespace soul::filters::tpt::simper_eq::Mode

Constants for use in specifying the filter mode.

Variables

int32 lowpass = 0

int32 highpass = 1

int32 bandpass = 2

int32 lowShelf = 3

int32 highShelf = 4

int32 peaking = 5

int32 notch = 6

int32 allpass = 7

int32 bell = 8

processor soul::filters::tpt::simper_eq::Processor

Specialisation Parameters

processor soul::filters::tpt::simper_eq::Processor (int32 initialMode = 0,
                                                    float32 initialFrequency = defaultFreqHz,
                                                    float32 initialQuality = defaultQuality,
                                                    float32 initialGain = defaultGain)

Inputs

  • stream in (SampleType)
  • event frequencyIn (float32)
  • event qualityIn (float32)
  • event gainIn (float32)
  • event modeIn (float32)

Outputs

Variables

float32 frequency = initialFrequency

float32 quality = initialQuality

float32 gain = initialGain

int32 mode = initialMode

bool recalc = true

MIDI utilities

In general, the SOUL policy towards MIDI is to avoid it as much as is humanly possible, so most of these helper functions are concerned with converting MIDI messages to soul::note_events types, and then the other libraries use these strongly-typed events to model note actions, rather than dealing with raw MIDI events.

namespace soul::midi

Various MIDI-related types and functions.

Structures

This type is used to represent a packed short MIDI message. When you create an input event endpoint and would like it to receive MIDI, this is the type that you should use for it.

struct Message
{

int32 midiBytes;

}

Functions

getByte1

Extracts the first MIDI byte from a Message object

int32 getByte1 (Message m)

getByte2

Extracts the second MIDI byte from a Message object

int32 getByte2 (Message m)

getByte3

Extracts the third MIDI byte from a Message object

int32 getByte3 (Message m)

processor soul::midi::MPEParser

This event processor receives incoming MIDI events, parses them as MPE, and then emits a stream of note events using the types in soul::note_events. A synthesiser can then handle the resulting events without needing to go near any actual MIDI or MPE data.

[[ main: false ]]

Inputs

Variables

int32 MPESlideControllerID = 74

Frequency-domain utilities

namespace soul::DFT

Discrete Fourier Transform functions.

Functions

forward

Performs a real forward DFT from an input buffer to an output buffer.

void forward<SampleBuffer> (const SampleBuffer& inputData,
                            SampleBuffer& outputData)

inverse

Performs a real inverse DFT from an input buffer to an output buffer.

void inverse<SampleBuffer> (const SampleBuffer& inputData,
                            SampleBuffer& outputData)

performComplex

For internal use by the other functions: performs a O(N^2) complex DFT.

void performComplex<SampleBuffer> (const SampleBuffer& inputReal,
                                   const SampleBuffer& inputImag,
                                   SampleBuffer& outputReal,
                                   SampleBuffer& outputImag,
                                   SampleBuffer.elementType scaleFactor)