diff --git a/README.md b/README.md index 6a2a7a78..36007f3e 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ The following Node classes are currently included with the base distribution: | **Buffer: Granulation** | [SegmentedGranulator](https://signalflow.dev/library/buffer/granulation/segmentedgranulator/), [Granulator](https://signalflow.dev/library/buffer/granulation/granulator/) | | **Control** | [MouseX](https://signalflow.dev/library/control/mousex/), [MouseY](https://signalflow.dev/library/control/mousey/), [MouseDown](https://signalflow.dev/library/control/mousedown/) | | **Envelope** | [ADSREnvelope](https://signalflow.dev/library/envelope/adsrenvelope/), [ASREnvelope](https://signalflow.dev/library/envelope/asrenvelope/), [DetectSilence](https://signalflow.dev/library/envelope/detectsilence/), [Envelope](https://signalflow.dev/library/envelope/envelope/), [Line](https://signalflow.dev/library/envelope/line/), [RectangularEnvelope](https://signalflow.dev/library/envelope/rectangularenvelope/) | -| **FFT** | [FFTContinuousPhaseVocoder](https://signalflow.dev/library/fft/fftcontinuousphasevocoder/), [FFTConvolve](https://signalflow.dev/library/fft/fftconvolve/), [FFTContrast](https://signalflow.dev/library/fft/fftcontrast/), [FFTMagnitudePhaseArray](https://signalflow.dev/library/fft/fftmagnitudephasearray/), [FFTRandomPhase](https://signalflow.dev/library/fft/fftrandomphase/), [FFTScaleMagnitudes](https://signalflow.dev/library/fft/fftscalemagnitudes/), [FFTTransform](https://signalflow.dev/library/fft/ffttransform/), [FFT](https://signalflow.dev/library/fft/fft/), [FFTNode](https://signalflow.dev/library/fft/fftnode/), [FFTOpNode](https://signalflow.dev/library/fft/fftopnode/), [FFTFindPeaks](https://signalflow.dev/library/fft/fftfindpeaks/), [IFFT](https://signalflow.dev/library/fft/ifft/), [FFTLPF](https://signalflow.dev/library/fft/fftlpf/), [FFTNoiseGate](https://signalflow.dev/library/fft/fftnoisegate/), [FFTPhaseVocoder](https://signalflow.dev/library/fft/fftphasevocoder/), [FFTTonality](https://signalflow.dev/library/fft/ffttonality/), [FFTZeroPhase](https://signalflow.dev/library/fft/fftzerophase/) | +| **FFT** | [FFTContinuousPhaseVocoder](https://signalflow.dev/library/fft/fftcontinuousphasevocoder/), [FFTConvolve](https://signalflow.dev/library/fft/fftconvolve/), [FFTContrast](https://signalflow.dev/library/fft/fftcontrast/), [FFTCrossFade](https://signalflow.dev/library/fft/fftcrossfade/), [FFTLFO](https://signalflow.dev/library/fft/fftlfo/), [FFTMagnitudePhaseArray](https://signalflow.dev/library/fft/fftmagnitudephasearray/), [FFTRandomPhase](https://signalflow.dev/library/fft/fftrandomphase/), [FFTScaleMagnitudes](https://signalflow.dev/library/fft/fftscalemagnitudes/), [FFTTransform](https://signalflow.dev/library/fft/ffttransform/), [FFT](https://signalflow.dev/library/fft/fft/), [FFTNode](https://signalflow.dev/library/fft/fftnode/), [FFTOpNode](https://signalflow.dev/library/fft/fftopnode/), [FFTFindPeaks](https://signalflow.dev/library/fft/fftfindpeaks/), [IFFT](https://signalflow.dev/library/fft/ifft/), [FFTLPF](https://signalflow.dev/library/fft/fftlpf/), [FFTNoiseGate](https://signalflow.dev/library/fft/fftnoisegate/), [FFTPhaseVocoder](https://signalflow.dev/library/fft/fftphasevocoder/), [FFTTonality](https://signalflow.dev/library/fft/ffttonality/), [FFTZeroPhase](https://signalflow.dev/library/fft/fftzerophase/) | | **Operators** | [Add](https://signalflow.dev/library/operators/add/), [AmplitudeToDecibels](https://signalflow.dev/library/operators/amplitudetodecibels/), [DecibelsToAmplitude](https://signalflow.dev/library/operators/decibelstoamplitude/), [ChannelArray](https://signalflow.dev/library/operators/channelarray/), [ChannelCrossfade](https://signalflow.dev/library/operators/channelcrossfade/), [ChannelMixer](https://signalflow.dev/library/operators/channelmixer/), [ChannelSelect](https://signalflow.dev/library/operators/channelselect/), [Equal](https://signalflow.dev/library/operators/equal/), [NotEqual](https://signalflow.dev/library/operators/notequal/), [GreaterThan](https://signalflow.dev/library/operators/greaterthan/), [GreaterThanOrEqual](https://signalflow.dev/library/operators/greaterthanorequal/), [LessThan](https://signalflow.dev/library/operators/lessthan/), [LessThanOrEqual](https://signalflow.dev/library/operators/lessthanorequal/), [Modulo](https://signalflow.dev/library/operators/modulo/), [Abs](https://signalflow.dev/library/operators/abs/), [If](https://signalflow.dev/library/operators/if/), [Divide](https://signalflow.dev/library/operators/divide/), [FrequencyToMidiNote](https://signalflow.dev/library/operators/frequencytomidinote/), [MidiNoteToFrequency](https://signalflow.dev/library/operators/midinotetofrequency/), [Multiply](https://signalflow.dev/library/operators/multiply/), [Pow](https://signalflow.dev/library/operators/pow/), [RoundToScale](https://signalflow.dev/library/operators/roundtoscale/), [Round](https://signalflow.dev/library/operators/round/), [ScaleLinExp](https://signalflow.dev/library/operators/scalelinexp/), [ScaleLinLin](https://signalflow.dev/library/operators/scalelinlin/), [Subtract](https://signalflow.dev/library/operators/subtract/), [Sum](https://signalflow.dev/library/operators/sum/), [TimeShift](https://signalflow.dev/library/operators/timeshift/), [Sin](https://signalflow.dev/library/operators/sin/), [Cos](https://signalflow.dev/library/operators/cos/), [Tan](https://signalflow.dev/library/operators/tan/), [Tanh](https://signalflow.dev/library/operators/tanh/) | | **Oscillators** | [Constant](https://signalflow.dev/library/oscillators/constant/), [Impulse](https://signalflow.dev/library/oscillators/impulse/), [LFO](https://signalflow.dev/library/oscillators/lfo/), [SawLFO](https://signalflow.dev/library/oscillators/sawlfo/), [SawOscillator](https://signalflow.dev/library/oscillators/sawoscillator/), [SineLFO](https://signalflow.dev/library/oscillators/sinelfo/), [SineOscillator](https://signalflow.dev/library/oscillators/sineoscillator/), [SquareLFO](https://signalflow.dev/library/oscillators/squarelfo/), [SquareOscillator](https://signalflow.dev/library/oscillators/squareoscillator/), [TriangleLFO](https://signalflow.dev/library/oscillators/trianglelfo/), [TriangleOscillator](https://signalflow.dev/library/oscillators/triangleoscillator/), [Wavetable](https://signalflow.dev/library/oscillators/wavetable/), [Wavetable2D](https://signalflow.dev/library/oscillators/wavetable2d/) | | **Processors** | [Clip](https://signalflow.dev/library/processors/clip/), [Fold](https://signalflow.dev/library/processors/fold/), [Smooth](https://signalflow.dev/library/processors/smooth/), [WetDry](https://signalflow.dev/library/processors/wetdry/), [Wrap](https://signalflow.dev/library/processors/wrap/) | diff --git a/docs/library/fft/fftlfo/index.md b/docs/library/fft/fftlfo/index.md new file mode 100644 index 00000000..83132573 --- /dev/null +++ b/docs/library/fft/fftlfo/index.md @@ -0,0 +1,13 @@ +title: FFTLFO node documentation +description: FFTLFO: FFT LFO. Requires an FFT* input. + +[Reference library](../../index.md) > [FFT](../index.md) > [FFTLFO](index.md) + +# FFTLFO + +```python +FFTLFO(input=0, frequency=1.0, spectral_cycles=1.0) +``` + +FFT LFO. Requires an FFT* input. + diff --git a/docs/library/fft/index.md b/docs/library/fft/index.md index 1acd5e1b..1f1be112 100644 --- a/docs/library/fft/index.md +++ b/docs/library/fft/index.md @@ -6,6 +6,7 @@ - **[FFTConvolve](fftconvolve/index.md)**: Frequency-domain convolution, using overlap-add. Useful for convolution reverb, with the input buffer containing an impulse response. Requires an FFT* input. - **[FFTContrast](fftcontrast/index.md)**: FFT Contrast. Requires an FFT* input. - **[FFTCrossFade](fftcrossfade/index.md)**: FFT FFTCrossFade. Requires two FFT* inputs. +- **[FFTLFO](fftlfo/index.md)**: FFT LFO. Requires an FFT* input. - **[FFTMagnitudePhaseArray](fftmagnitudephasearray/index.md)**: Fixed mag/phase array. - **[FFTRandomPhase](fftrandomphase/index.md)**: Randomise phase values. - **[FFTScaleMagnitudes](fftscalemagnitudes/index.md)**: Randomise phase values. diff --git a/docs/library/index.md b/docs/library/index.md index 1907922b..81e47f5d 100644 --- a/docs/library/index.md +++ b/docs/library/index.md @@ -56,6 +56,7 @@ - **[FFTConvolve](fft/fftconvolve/index.md)**: Frequency-domain convolution, using overlap-add. Useful for convolution reverb, with the input buffer containing an impulse response. Requires an FFT* input. - **[FFTContrast](fft/fftcontrast/index.md)**: FFT Contrast. Requires an FFT* input. - **[FFTCrossFade](fft/fftcrossfade/index.md)**: FFT FFTCrossFade. Requires two FFT* inputs. +- **[FFTLFO](fft/fftlfo/index.md)**: FFT LFO. Requires an FFT* input. - **[FFTMagnitudePhaseArray](fft/fftmagnitudephasearray/index.md)**: Fixed mag/phase array. - **[FFTRandomPhase](fft/fftrandomphase/index.md)**: Randomise phase values. - **[FFTScaleMagnitudes](fft/fftscalemagnitudes/index.md)**: Randomise phase values. diff --git a/source/include/signalflow/node/fft/fft-lfo.h b/source/include/signalflow/node/fft/fft-lfo.h new file mode 100644 index 00000000..b381d2c8 --- /dev/null +++ b/source/include/signalflow/node/fft/fft-lfo.h @@ -0,0 +1,26 @@ +#pragma once + +#include "signalflow/node/fft/fftnode.h" + +namespace signalflow +{ + +/**--------------------------------------------------------------------------------* + * FFT LFO. + * Requires an FFT* input. + *---------------------------------------------------------------------------------*/ +class FFTLFO : public FFTOpNode +{ +public: + FFTLFO(NodeRef input = 0, NodeRef frequency = 1.0, NodeRef spectral_cycles = 1.0); + virtual void process(Buffer &out, int num_frames); + +private: + NodeRef frequency; + NodeRef spectral_cycles; + double phase; +}; + +REGISTER(FFTLFO, "fft-lfo") + +} diff --git a/source/include/signalflow/signalflow.h b/source/include/signalflow/signalflow.h index 3f7622c6..46be3645 100644 --- a/source/include/signalflow/signalflow.h +++ b/source/include/signalflow/signalflow.h @@ -174,6 +174,7 @@ #include #include #include +#include #include #include #include diff --git a/source/src/CMakeLists.txt b/source/src/CMakeLists.txt index a5230172..9b5638f6 100644 --- a/source/src/CMakeLists.txt +++ b/source/src/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC ${SRC} ${CMAKE_CURRENT_SOURCE_DIR}/node/fft/fft-random-phase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/fft/fft-scale-magnitudes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/fft/fft-cross-fade.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/fft/fft-lfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/processors/delays/comb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/processors/delays/allpass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/processors/delays/onetap.cpp diff --git a/source/src/node/fft/fft-lfo.cpp b/source/src/node/fft/fft-lfo.cpp new file mode 100644 index 00000000..0532dc63 --- /dev/null +++ b/source/src/node/fft/fft-lfo.cpp @@ -0,0 +1,49 @@ +#include "signalflow/core/graph.h" +#include "signalflow/node/fft/fft-lfo.h" + +namespace signalflow +{ + +FFTLFO::FFTLFO(NodeRef input, NodeRef frequency, NodeRef spectral_cycles) + : FFTOpNode(input), frequency(frequency), spectral_cycles(spectral_cycles) +{ + this->name = "fft-contrast"; + + this->create_input("frequency", this->frequency); + this->create_input("spectral_cycles", this->spectral_cycles); + this->phase = 0.0; +} + +void FFTLFO::process(Buffer &out, int num_frames) +{ + FFTNode *fftnode = (FFTNode *) this->input.get(); + this->num_hops = fftnode->num_hops; + // TODO: Could this update per hop if I index based on hop? + float increment_per_bin = this->spectral_cycles->out[0][0] / (M_PI * 2); + + for (int hop = 0; hop < this->num_hops; hop++) + { + float frequency = this->frequency->out[0][0]; + + for (int frame = 0; frame < this->fft_size; frame++) + { + if (frame < this->num_bins) + { + float phase_accum = phase + (increment_per_bin * frame / num_frames); + out[hop][frame] = input->out[hop][frame] * (sinf(M_PI * 2 * phase_accum) * 2 + 1.0); + } + else + { + out[hop][frame] = input->out[hop][frame]; + } + + phase += frequency / this->graph->get_sample_rate(); + while (phase > 1.0) + { + phase -= 1.0; + } + } + } +} + +} diff --git a/source/src/python/nodes.cpp b/source/src/python/nodes.cpp index 928f94be..16a08eea 100644 --- a/source/src/python/nodes.cpp +++ b/source/src/python/nodes.cpp @@ -115,6 +115,9 @@ void init_python_nodes(py::module &m) py::class_>(m, "FFTCrossFade", "FFT FFTCrossFade. Requires two FFT* inputs.") .def(py::init(), "inputA"_a = 0, "inputB"_a = 0, "crossfade"_a = 0.0); + py::class_>(m, "FFTLFO", "FFT LFO. Requires an FFT* input.") + .def(py::init(), "input"_a = 0, "frequency"_a = 1.0, "spectral_cycles"_a = 1.0); + py::class_>(m, "FFTMagnitudePhaseArray", "Fixed mag/phase array.") .def(py::init, std::vector>(), "input"_a = 0, "magnitudes"_a = 0, "phases"_a = 0) .def("set_magnitudes", &FFTMagnitudePhaseArray::set_magnitudes);