Skip to content

Commit

Permalink
Add Accumulator node
Browse files Browse the repository at this point in the history
  • Loading branch information
ideoforms committed Aug 16, 2024
1 parent 4e5bbed commit 767c027
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 34 deletions.
87 changes: 59 additions & 28 deletions auxiliary/libs/signalflow-stubs/signalflow.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ from __future__ import annotations
import numpy
import typing
import typing_extensions
__all__ = ['ADSREnvelope', 'ASREnvelope', 'Abs', 'Add', 'AllpassDelay', 'AmplitudeToDecibels', 'AudioGraph', 'AudioGraphConfig', 'AudioIOException', 'AudioIn', 'AudioOut', 'AudioOut_Abstract', 'AudioOut_Dummy', 'AzimuthPanner', 'BeatCutter', 'BiquadFilter', 'Buffer', 'Buffer2D', 'BufferLooper', 'BufferPlayer', 'BufferRecorder', 'CPUUsageAboveLimitException', 'ChannelArray', 'ChannelCrossfade', 'ChannelMixer', 'ChannelPanner', 'ChannelSelect', 'Clip', 'ClockDivider', 'CombDelay', 'Compressor', 'Constant', 'Cos', 'Counter', 'CrossCorrelate', 'DCFilter', 'DecibelsToAmplitude', 'DetectSilence', 'DeviceNotFoundException', 'Divide', 'EQ', 'Envelope', 'EnvelopeBuffer', 'Equal', 'Euclidean', 'FFT', 'FFTBuffer', 'FFTBufferPlayer', 'FFTContinuousPhaseVocoder', 'FFTContrast', 'FFTConvolve', 'FFTCrossFade', 'FFTFindPeaks', 'FFTLFO', 'FFTLPF', 'FFTMagnitudePhaseArray', 'FFTNode', 'FFTNoiseGate', 'FFTOpNode', 'FFTPhaseVocoder', 'FFTRandomPhase', 'FFTScaleMagnitudes', 'FFTTonality', 'FFTTransform', 'FeedbackBufferReader', 'FeedbackBufferWriter', 'FlipFlop', 'Fold', 'FrequencyToMidiNote', 'Gate', 'Granulator', 'GraphAlreadyCreatedException', 'GraphNotCreatedException', 'GreaterThan', 'GreaterThanOrEqual', 'IFFT', 'If', 'Impulse', 'ImpulseSequence', 'Index', 'InsufficientBufferSizeException', 'InvalidChannelCountException', 'KDTree', 'KDTreeMatch', 'LFO', 'Latch', 'LessThan', 'LessThanOrEqual', 'Line', 'Logistic', 'Maximiser', 'MidiNoteToFrequency', 'Modulo', 'MoogVCF', 'MouseDown', 'MouseX', 'MouseY', 'Multiply', 'NearestNeighbour', 'Node', 'NodeAlreadyPlayingException', 'NodeNotPlayingException', 'NodeRegistry', 'NotEqual', 'OneTapDelay', 'OnsetDetector', 'Patch', 'PatchFinishedPlaybackException', 'PatchRegistry', 'PatchSpec', 'PinkNoise', 'Pow', 'RMS', 'RandomBrownian', 'RandomChoice', 'RandomCoin', 'RandomExponential', 'RandomExponentialDist', 'RandomGaussian', 'RandomImpulse', 'RandomImpulseSequence', 'RandomUniform', 'RectangularEnvelope', 'Resample', 'Round', 'RoundToScale', 'SIGNALFLOW_DEFAULT_BLOCK_SIZE', 'SIGNALFLOW_DEFAULT_FFT_HOP_SIZE', 'SIGNALFLOW_DEFAULT_FFT_SIZE', 'SIGNALFLOW_DEFAULT_SAMPLE_RATE', 'SIGNALFLOW_DEFAULT_TRIGGER', 'SIGNALFLOW_EVENT_DISTRIBUTION_POISSON', 'SIGNALFLOW_EVENT_DISTRIBUTION_UNIFORM', 'SIGNALFLOW_FILTER_TYPE_BAND_PASS', 'SIGNALFLOW_FILTER_TYPE_HIGH_PASS', 'SIGNALFLOW_FILTER_TYPE_HIGH_SHELF', 'SIGNALFLOW_FILTER_TYPE_LOW_PASS', 'SIGNALFLOW_FILTER_TYPE_LOW_SHELF', 'SIGNALFLOW_FILTER_TYPE_NOTCH', 'SIGNALFLOW_FILTER_TYPE_PEAK', 'SIGNALFLOW_INTERPOLATION_MODE_COSINE', 'SIGNALFLOW_INTERPOLATION_MODE_LINEAR', 'SIGNALFLOW_INTERPOLATION_MODE_NONE', 'SIGNALFLOW_MAX_CHANNELS', 'SIGNALFLOW_MAX_FFT_SIZE', 'SIGNALFLOW_NODE_BUFFER_SIZE', 'SIGNALFLOW_NODE_INITIAL_OUTPUT_CHANNELS', 'SIGNALFLOW_NODE_STATE_ACTIVE', 'SIGNALFLOW_NODE_STATE_STOPPED', 'SIGNALFLOW_PATCH_STATE_ACTIVE', 'SIGNALFLOW_PATCH_STATE_STOPPED', 'SVFilter', 'SampleAndHold', 'SawLFO', 'SawOscillator', 'ScaleLinExp', 'ScaleLinLin', 'SegmentPlayer', 'SegmentedGranulator', 'Sequence', 'Sin', 'SineLFO', 'SineOscillator', 'Smooth', 'SpatialEnvironment', 'SpatialPanner', 'SpatialSpeaker', 'SquareLFO', 'SquareOscillator', 'Squiz', 'StereoBalance', 'StereoPanner', 'StereoWidth', 'StochasticNode', 'Stutter', 'Subtract', 'Sum', 'Tan', 'Tanh', 'TimeShift', 'TriangleLFO', 'TriangleOscillator', 'TriggerMult', 'TriggerRoundRobin', 'UnknownTriggerNameException', 'VampAnalysis', 'WaveShaper', 'WaveShaperBuffer', 'Wavetable', 'Wavetable2D', 'WavetableBuffer', 'WetDry', 'WhiteNoise', 'Wrap', 'amplitude_to_db', 'clip', 'db_to_amplitude', 'fold', 'frequency_to_midi_note', 'midi_note_to_frequency', 'random_exponential', 'random_integer', 'random_seed', 'random_uniform', 'save_block_to_text_file', 'save_block_to_wav_file', 'scale_exp_lin', 'scale_lin_exp', 'scale_lin_lin', 'signalflow_event_distribution_t', 'signalflow_filter_type_t', 'signalflow_interpolation_mode_t', 'signalflow_node_state_t', 'signalflow_patch_state_t', 'wrap']
__all__ = ['ADSREnvelope', 'ASREnvelope', 'Abs', 'Accumulator', 'Add', 'AllpassDelay', 'AmplitudeToDecibels', 'AudioGraph', 'AudioGraphConfig', 'AudioIOException', 'AudioIn', 'AudioOut', 'AudioOut_Abstract', 'AudioOut_Dummy', 'AzimuthPanner', 'BeatCutter', 'BiquadFilter', 'Buffer', 'Buffer2D', 'BufferLooper', 'BufferPlayer', 'BufferRecorder', 'CPUUsageAboveLimitException', 'ChannelArray', 'ChannelCrossfade', 'ChannelMixer', 'ChannelPanner', 'ChannelSelect', 'Clip', 'ClockDivider', 'CombDelay', 'Compressor', 'Constant', 'Cos', 'Counter', 'CrossCorrelate', 'DCFilter', 'DecibelsToAmplitude', 'DetectSilence', 'DeviceNotFoundException', 'Divide', 'EQ', 'Envelope', 'EnvelopeBuffer', 'Equal', 'Euclidean', 'FFT', 'FFTBuffer', 'FFTBufferPlayer', 'FFTContinuousPhaseVocoder', 'FFTContrast', 'FFTConvolve', 'FFTCrossFade', 'FFTFindPeaks', 'FFTLFO', 'FFTLPF', 'FFTMagnitudePhaseArray', 'FFTNode', 'FFTNoiseGate', 'FFTOpNode', 'FFTPhaseVocoder', 'FFTRandomPhase', 'FFTScaleMagnitudes', 'FFTTonality', 'FFTTransform', 'FeedbackBufferReader', 'FeedbackBufferWriter', 'FlipFlop', 'Fold', 'FrequencyToMidiNote', 'Gate', 'Granulator', 'GraphAlreadyCreatedException', 'GraphNotCreatedException', 'GreaterThan', 'GreaterThanOrEqual', 'HistoryBufferWriter', 'IFFT', 'If', 'Impulse', 'ImpulseSequence', 'Index', 'InsufficientBufferSizeException', 'InvalidChannelCountException', 'KDTree', 'KDTreeMatch', 'LFO', 'Latch', 'LessThan', 'LessThanOrEqual', 'Line', 'Logistic', 'Maximiser', 'MidiNoteToFrequency', 'Modulo', 'MoogVCF', 'MouseDown', 'MouseX', 'MouseY', 'Multiply', 'NearestNeighbour', 'Node', 'NodeAlreadyPlayingException', 'NodeNotPlayingException', 'NodeRegistry', 'NotEqual', 'OneTapDelay', 'OnsetDetector', 'Patch', 'PatchFinishedPlaybackException', 'PatchRegistry', 'PatchSpec', 'PinkNoise', 'Pow', 'RMS', 'RandomBrownian', 'RandomChoice', 'RandomCoin', 'RandomExponential', 'RandomExponentialDist', 'RandomGaussian', 'RandomImpulse', 'RandomImpulseSequence', 'RandomUniform', 'RectangularEnvelope', 'Resample', 'Round', 'RoundToScale', 'SIGNALFLOW_DEFAULT_BLOCK_SIZE', 'SIGNALFLOW_DEFAULT_FFT_HOP_SIZE', 'SIGNALFLOW_DEFAULT_FFT_SIZE', 'SIGNALFLOW_DEFAULT_SAMPLE_RATE', 'SIGNALFLOW_DEFAULT_TRIGGER', 'SIGNALFLOW_EVENT_DISTRIBUTION_POISSON', 'SIGNALFLOW_EVENT_DISTRIBUTION_UNIFORM', 'SIGNALFLOW_FILTER_TYPE_BAND_PASS', 'SIGNALFLOW_FILTER_TYPE_HIGH_PASS', 'SIGNALFLOW_FILTER_TYPE_HIGH_SHELF', 'SIGNALFLOW_FILTER_TYPE_LOW_PASS', 'SIGNALFLOW_FILTER_TYPE_LOW_SHELF', 'SIGNALFLOW_FILTER_TYPE_NOTCH', 'SIGNALFLOW_FILTER_TYPE_PEAK', 'SIGNALFLOW_INTERPOLATION_MODE_COSINE', 'SIGNALFLOW_INTERPOLATION_MODE_LINEAR', 'SIGNALFLOW_INTERPOLATION_MODE_NONE', 'SIGNALFLOW_MAX_CHANNELS', 'SIGNALFLOW_MAX_FFT_SIZE', 'SIGNALFLOW_NODE_BUFFER_SIZE', 'SIGNALFLOW_NODE_INITIAL_OUTPUT_CHANNELS', 'SIGNALFLOW_NODE_STATE_ACTIVE', 'SIGNALFLOW_NODE_STATE_STOPPED', 'SIGNALFLOW_PATCH_STATE_ACTIVE', 'SIGNALFLOW_PATCH_STATE_STOPPED', 'SVFilter', 'SampleAndHold', 'SawLFO', 'SawOscillator', 'ScaleLinExp', 'ScaleLinLin', 'SegmentPlayer', 'SegmentedGranulator', 'SelectInput', 'Sequence', 'Sin', 'SineLFO', 'SineOscillator', 'Smooth', 'SpatialEnvironment', 'SpatialPanner', 'SpatialSpeaker', 'SquareLFO', 'SquareOscillator', 'Squiz', 'StereoBalance', 'StereoPanner', 'StereoWidth', 'StochasticNode', 'Stutter', 'Subtract', 'Sum', 'Tan', 'Tanh', 'TimeShift', 'TriangleLFO', 'TriangleOscillator', 'TriggerMult', 'TriggerRoundRobin', 'UnknownTriggerNameException', 'VampAnalysis', 'WaveShaper', 'WaveShaperBuffer', 'Wavetable', 'Wavetable2D', 'WavetableBuffer', 'WetDry', 'WhiteNoise', 'Wrap', 'amplitude_to_db', 'clip', 'db_to_amplitude', 'fold', 'frequency_to_midi_note', 'midi_note_to_frequency', 'random_exponential', 'random_integer', 'random_seed', 'random_uniform', 'save_block_to_text_file', 'save_block_to_wav_file', 'scale_exp_lin', 'scale_lin_exp', 'scale_lin_lin', 'signalflow_event_distribution_t', 'signalflow_filter_type_t', 'signalflow_interpolation_mode_t', 'signalflow_node_state_t', 'signalflow_patch_state_t', 'wrap']
class ADSREnvelope(Node):
"""
Attack-decay-sustain-release envelope. Sustain portion is held until gate is zero.
Expand All @@ -29,6 +29,12 @@ class Abs(Node):
"""
def __init__(self, a: Node = 0) -> None:
...
class Accumulator(Node):
"""
Accumulator with decay.
"""
def __init__(self, strike_force: Node = 0.5, decay_coefficient: Node = 0.9999, trigger: Node = None) -> None:
...
class Add(Node):
"""
Add each sample of a to each sample of b. Can also be written as a + b
Expand Down Expand Up @@ -299,100 +305,100 @@ class Buffer:
"""
A buffer of audio samples, containing one or more channels.
"""
def __add__(self, value: float) -> typing_extensions.Buffer:
def __add__(self: typing_extensions.Buffer, value: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` added to `value`.
"""
def __div__(self, value: float) -> typing_extensions.Buffer:
def __div__(self: typing_extensions.Buffer, value: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` divided by `value`.
"""
def __getitem__(self, arg0: int) -> typing_extensions.Buffer:
def __getitem__(self: typing_extensions.Buffer, arg0: int) -> typing_extensions.Buffer:
...
@typing.overload
def __init__(self) -> None:
def __init__(self: typing_extensions.Buffer) -> None:
"""
Create a null Buffer with no memory allocated.
"""
@typing.overload
def __init__(self, filename: str) -> None:
def __init__(self: typing_extensions.Buffer, filename: str) -> None:
"""
Load a Buffer from an audio file.
"""
@typing.overload
def __init__(self, num_channels: int, num_frames: int) -> None:
def __init__(self: typing_extensions.Buffer, num_channels: int, num_frames: int) -> None:
"""
Allocate a buffer with `num_channels` channels and `num_frames` frames.
"""
@typing.overload
def __init__(self, num_channels: int, num_frames: int, data: list[list[float]]) -> None:
def __init__(self: typing_extensions.Buffer, num_channels: int, num_frames: int, data: list[list[float]]) -> None:
"""
Allocate a buffer with `num_channels` channels and `num_frames` frames, containing the floating-point samples in `data`.
"""
@typing.overload
def __init__(self, arg0: list[list[float]]) -> None:
def __init__(self: typing_extensions.Buffer, arg0: list[list[float]]) -> None:
"""
Allocate a buffer with `num_channels` channels and `num_frames` frames, containing the floating-point samples in `data`.
"""
@typing.overload
def __init__(self, data: list[float]) -> None:
def __init__(self: typing_extensions.Buffer, data: list[float]) -> None:
"""
Allocate a buffer containing the floating-point samples in `data`.
"""
@typing.overload
def __init__(self, function: typing.Callable[[float], float]) -> None:
def __init__(self: typing_extensions.Buffer, function: typing.Callable[[float], float]) -> None:
"""
Allocate a buffer filled with the output of the function `function`.
"""
@typing.overload
def __init__(self, num_frames: int, function: typing.Callable[[float], float]) -> None:
def __init__(self: typing_extensions.Buffer, num_frames: int, function: typing.Callable[[float], float]) -> None:
"""
Allocate a mono buffer with `num_frames` frames, filled with the output of the function `function`.
"""
@typing.overload
def __init__(self, num_channels: int, num_frames: int, function: typing.Callable[[float], float]) -> None:
def __init__(self: typing_extensions.Buffer, num_channels: int, num_frames: int, function: typing.Callable[[float], float]) -> None:
"""
Allocate a buffer with `num_channels` channels and `num_frames` frames, filled with the output of the function `function`.
"""
def __len__(self) -> int:
def __len__(self: typing_extensions.Buffer) -> int:
"""
Returns the length of the buffer `self`, in frames.
"""
def __mul__(self, value: float) -> typing_extensions.Buffer:
def __mul__(self: typing_extensions.Buffer, value: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` multiplied by `value`.
"""
def __radd__(self, value_: float) -> typing_extensions.Buffer:
def __radd__(self: typing_extensions.Buffer, value_: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` added to `value`.
"""
def __rmul__(self, value_: float) -> typing_extensions.Buffer:
def __rmul__(self: typing_extensions.Buffer, value_: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` multiplied by `value`.
"""
def __str__(self) -> str:
def __str__(self: typing_extensions.Buffer) -> str:
...
def __sub__(self, value: float) -> typing_extensions.Buffer:
def __sub__(self: typing_extensions.Buffer, value: float) -> typing_extensions.Buffer:
"""
Returns a new Buffer containing the samples in `self` subtracted by `value`.
"""
@typing.overload
def fill(self, sample: float) -> None:
def fill(self: typing_extensions.Buffer, sample: float) -> None:
...
@typing.overload
def fill(self, function: typing.Callable[[float], float]) -> None:
def fill(self: typing_extensions.Buffer, function: typing.Callable[[float], float]) -> None:
...
def get(self, channel: int, frame: float) -> float:
def get(self: typing_extensions.Buffer, channel: int, frame: float) -> float:
...
def get_frame(self, channel: int, frame: float) -> float:
def get_frame(self: typing_extensions.Buffer, channel: int, frame: float) -> float:
...
def load(self, filename: str) -> None:
def load(self: typing_extensions.Buffer, filename: str) -> None:
...
def save(self, filename: str) -> None:
def save(self: typing_extensions.Buffer, filename: str) -> None:
...
def set(self, channel: int, frame: int, value: float) -> bool:
def set(self: typing_extensions.Buffer, channel: int, frame: int, value: float) -> bool:
...
def split(self, num_frames_per_part: int) -> list[typing_extensions.Buffer]:
def split(self: typing_extensions.Buffer, num_frames_per_part: int) -> list[typing_extensions.Buffer]:
...
@property
def data(self) -> numpy.ndarray[numpy.float32]:
Expand Down Expand Up @@ -831,6 +837,12 @@ class GreaterThanOrEqual(Node):
"""
def __init__(self, a: Node = 0, b: Node = 0) -> None:
...
class HistoryBufferWriter(Node):
"""
Writes a rolling history buffer of a given duration. At a given moment in time, the contents of the buffer will be equal to the past N seconds of the audio generated by `input`. This is useful for (e.g.) a visual display of a rolling waveform or LFO window. `downsample` can be used to downsample the input; for example, with `downsample` of 10, a 1-second buffer can be used to display 10 seconds of historical audio.
"""
def __init__(self, buffer: ... = None, input: Node = 0.0, downsample: int = 1) -> None:
...
class IFFT(FFTOpNode):
"""
Inverse Fast Fourier Transform. Requires an FFT* input, generates a time-domain output.
Expand Down Expand Up @@ -1627,11 +1639,30 @@ class SegmentedGranulator(Node):
"""
def __init__(self, buffer: ... = None, onset_times: list[float] = 0, durations: list[float] = 0, index: Node = 0.0, rate: Node = 1.0, clock: Node = 0, max_grains: Node = 2048) -> None:
...
class SelectInput(Node):
"""
Select from the outputs of one or more `inputs` nodes, based on the input index specified in `index`. Unlike `ChannelSelect`, inputs may be multichannel, and `index` can be modulated in real time.
"""
@typing.overload
def __init__(self, index: Node = 0) -> None:
...
@typing.overload
def __init__(self, inputs: ..., index: Node = 0) -> None:
...
@typing.overload
def __init__(self, inputs: list[Node], index: Node = 0) -> None:
...
@typing.overload
def __init__(self, inputs: list[int], index: Node = 0) -> None:
...
@typing.overload
def __init__(self, inputs: list[float], index: Node = 0) -> None:
...
class Sequence(Node):
"""
Outputs the elements in `sequence`, incrementing position on each `clock`.
"""
def __init__(self, sequence: list[float] = [], clock: Node = None) -> None:
def __init__(self: typing.Sequence, sequence: list[float] = [], clock: Node = None) -> None:
...
class Sin(Node):
"""
Expand Down
28 changes: 28 additions & 0 deletions source/include/signalflow/node/envelope/accumulator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "signalflow/core/constants.h"
#include "signalflow/node/node.h"

namespace signalflow
{

/**--------------------------------------------------------------------------------*
* Accumulator with decay.
*---------------------------------------------------------------------------------*/
class Accumulator : public Node
{
public:
Accumulator(NodeRef strike_force = 0.5, NodeRef decay_coefficient = 0.9999, NodeRef trigger = nullptr);
virtual void trigger(std::string name = SIGNALFLOW_DEFAULT_TRIGGER, float value = SIGNALFLOW_NULL_FLOAT) override;
virtual void process(Buffer &out, int num_frames) override;

private:
NodeRef strike_force;
NodeRef decay_coefficient;
NodeRef _trigger;
double current_value;
};

REGISTER(Accumulator, "accumulator")

}
1 change: 1 addition & 0 deletions source/include/signalflow/signalflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
/*------------------------------------------------------------------------
* Envelopes and lifecycle
*-----------------------------------------------------------------------*/
#include <signalflow/node/envelope/accumulator.h>
#include <signalflow/node/envelope/adsr.h>
#include <signalflow/node/envelope/asr.h>
#include <signalflow/node/envelope/detect-silence.h>
Expand Down
1 change: 1 addition & 0 deletions source/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set(SRC ${SRC}
${CMAKE_CURRENT_SOURCE_DIR}/node/stochastic/random-brownian.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/stochastic/random-coin.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/stochastic/random-choice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/envelope/accumulator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/envelope/adsr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/envelope/asr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/envelope/envelope.cpp
Expand Down
Loading

0 comments on commit 767c027

Please sign in to comment.