From 66e4e2a982ecaf5691aee78fc0b8386a8136d3ed Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Wed, 8 Nov 2023 20:23:07 +0000 Subject: [PATCH] Documentation: Update library; add per-platform buttons --- .../scripts/generate-node-python-bindings.py | 11 +- docs/installation/index.md | 12 +- docs/installation/linux.md | 27 ----- docs/installation/linux/buttons.md | 1 + docs/installation/linux/index.md | 5 + docs/installation/macos/buttons.md | 7 ++ docs/installation/macos/index.md | 5 + docs/{node/library.md => library/index.md} | 106 +++++++++--------- mkdocs.yml | 2 + 9 files changed, 85 insertions(+), 91 deletions(-) delete mode 100644 docs/installation/linux.md create mode 100644 docs/installation/linux/buttons.md create mode 100644 docs/installation/linux/index.md create mode 100644 docs/installation/macos/buttons.md create mode 100644 docs/installation/macos/index.md rename docs/{node/library.md => library/index.md} (75%) diff --git a/auxiliary/scripts/generate-node-python-bindings.py b/auxiliary/scripts/generate-node-python-bindings.py index 3efa3ea9..71e88c9b 100755 --- a/auxiliary/scripts/generate-node-python-bindings.py +++ b/auxiliary/scripts/generate-node-python-bindings.py @@ -74,7 +74,6 @@ def generate_class_bindings(class_name, parameter_sets, superclass="Node", class output = output[:-1] + ";\n" return output - def generate_all_bindings(): output_markdown = "" folder_last = "" @@ -96,11 +95,17 @@ def generate_all_bindings(): class_categories = {} class_category = None + def folder_name_to_title(folder_name): + folder_parts = [part.title() for part in folder_name.split("/")] + folder_title = ": ".join(folder_parts) + return folder_title + for source_file in source_files: folder = re.sub(".*node/", "", source_file) folder = os.path.dirname(folder) if folder != folder_last: - output_markdown += "\n## " + folder + "\n\n" + folder_title = folder_name_to_title(folder) + output_markdown += "\n## " + folder_title + "\n\n" folder_last = folder class_category = folder class_categories[class_category] = [] @@ -127,7 +132,7 @@ def extract_docs(doxygen): # start or end of comment if re.search(r"^\s*/\*", line) or re.search("\*/\s*$", line): continue - line = re.sub("^\s*\*\s*", "", line) + line = re.sub(r"^\s*\*\s*", "", line) output = output + line + " " return output.strip() diff --git a/docs/installation/index.md b/docs/installation/index.md index 47db3867..96598412 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -10,19 +10,15 @@ SignalFlow supports macOS, Linux (including Raspberry Pi), and has alpha support ### macOS -If you are an existing Python user and confident with the command line: - -[macOS: Install from the command line](macos/command-line.md){ .md-button } - -If you're new to Python or getting started from scratch: - -[macOS: Easy install with Visual Studio Code](macos/easy.md){ .md-button } +{% + include-markdown "installation/macos/buttons.md" +%} --- ### Linux -[Linux: Install from the command line](linux/command-line.md){ .md-button } +{% include-markdown "installation/linux/buttons.md" %} --- diff --git a/docs/installation/linux.md b/docs/installation/linux.md deleted file mode 100644 index 6f3a811d..00000000 --- a/docs/installation/linux.md +++ /dev/null @@ -1,27 +0,0 @@ -# Getting started - -## Requirements - -SignalFlow supports macOS, Linux (including Raspberry Pi), and has alpha support for Windows. - -Python 3.8 or above is required. On macOS, we recommend installing an up-to-date version of Python3 [using Homebrew](https://docs.python-guide.org/starting/install3/osx/): `brew install python3`. - -## Installation - -On macOS and Linux x86_64, SignalFlow can be installed using `pip`: - -``` -pip3 install signalflow -``` - -Verify that the installation has worked correctly by using the `signalflow` command-line tool to play a test tone through your default system audio output: - -``` -signalflow test -``` - -For more detailed installation information, including Windows install and compilation from source, see the [README](https://github.com/ideoforms/signalflow). - -## Examples - -[Several example scripts](https://github.com/ideoforms/signalflow/tree/master/examples) are included within the repo, covering simple control and modulation, FM synthesis, sample granulation, MIDI control, chaotic functions, etc. diff --git a/docs/installation/linux/buttons.md b/docs/installation/linux/buttons.md new file mode 100644 index 00000000..b74bde21 --- /dev/null +++ b/docs/installation/linux/buttons.md @@ -0,0 +1 @@ +[Linux: Install from the command line](command-line.md){ .md-button } \ No newline at end of file diff --git a/docs/installation/linux/index.md b/docs/installation/linux/index.md new file mode 100644 index 00000000..4e82c827 --- /dev/null +++ b/docs/installation/linux/index.md @@ -0,0 +1,5 @@ +# Installation on Linux + +{% + include-markdown "installation/linux/buttons.md" +%} \ No newline at end of file diff --git a/docs/installation/macos/buttons.md b/docs/installation/macos/buttons.md new file mode 100644 index 00000000..1c4c26e0 --- /dev/null +++ b/docs/installation/macos/buttons.md @@ -0,0 +1,7 @@ +If you're new to Python or getting started from scratch: + +[macOS: Easy install with Visual Studio Code](easy.md){ .md-button } + +If you are an existing Python user and confident with the command line: + +[macOS: Install from the command line](command-line.md){ .md-button } diff --git a/docs/installation/macos/index.md b/docs/installation/macos/index.md new file mode 100644 index 00000000..89633d9d --- /dev/null +++ b/docs/installation/macos/index.md @@ -0,0 +1,5 @@ +# Installation on macOS + +{% + include-markdown "installation/macos/buttons.md" +%} \ No newline at end of file diff --git a/docs/node/library.md b/docs/library/index.md similarity index 75% rename from docs/node/library.md rename to docs/library/index.md index fff4a59c..37fe7df9 100644 --- a/docs/node/library.md +++ b/docs/library/index.md @@ -1,13 +1,13 @@ # Node reference library -## analysis +## Analysis -- **CrossCorrelate**: CrossCorrelate `(input=nullptr, buffer=nullptr, hop_size=0)` -- **OnsetDetector**: OnsetDetector `(input=0.0, threshold=2.0, min_interval=0.1)` -- **VampAnalysis**: VampAnalysis `(input=0.0, plugin_id="vamp-example-plugins:spectralcentroid:linearcentroid")` +- **CrossCorrelate**: Outputs the cross-correlation of the input signal with the given buffer. If hop_size is zero, calculates the cross-correlation every sample. `(input=nullptr, buffer=nullptr, hop_size=0)` +- **OnsetDetector**: Simple time-domain onset detector. Outputs an impulse when an onset is detected in the input. Maintains short-time and long-time averages. An onset is registered when the short-time average is threshold x the long-time average. min_interval is the minimum interval between onsets, in seconds. `(input=0.0, threshold=2.0, min_interval=0.1)` +- **VampAnalysis**: Feature extraction using the Vamp plugin toolkit. `(input=0.0, plugin_id="vamp-example-plugins:spectralcentroid:linearcentroid")` -## buffer +## Buffer - **BeatCutter**: Cuts a buffer into segment_count segments, and stutters/jumps with the given probabilities. `(buffer=nullptr, segment_count=8, stutter_probability=0.0, stutter_count=1, jump_probability=0.0, duty_cycle=1.0, rate=1.0, segment_rate=1.0)` - **BufferLooper**: Read and write from a buffer concurrently, with controllable overdub. `(buffer=nullptr, input=0.0, feedback=0.0, loop_playback=false, loop_record=false)` @@ -19,13 +19,13 @@ - **Granulator**: Granulator. Generates a grain from the given buffer each time a clock signal is received, with the given duration/rate/pan parameters. The input buffer can be mono or stereo. `(buffer=nullptr, clock=0, pos=0, duration=0.1, pan=0.0, rate=1.0, max_grains=2048)` - **SegmentPlayer**: Trigger segments of a buffer at the given onset positions. `(buffer=nullptr, onsets={})` -## control +## Control -- **MouseX**: Outputs the normalised cursor X position, from 0 to 1. `()` -- **MouseY**: Outputs the normalised cursor Y position, from 0 to 1. `()` -- **MouseDown**: Outputs 1 if the left mouse button is down, 0 otherwise. `(button_index=0)` +- **MouseX**: Outputs the normalised cursor X position, from 0 to 1. Currently only supported on macOS. `()` +- **MouseY**: Outputs the normalised cursor Y position, from 0 to 1. Currently only supported on macOS. `()` +- **MouseDown**: Outputs 1 if the left mouse button is down, 0 otherwise. Currently only supported on macOS. `(button_index=0)` -## envelope +## Envelope - **ADSREnvelope**: Attack-decay-sustain-release envelope. Sustain portion is held until gate is zero. `(attack=0.1, decay=0.1, sustain=0.5, release=0.1, gate=0)` - **ASREnvelope**: Attack-sustain-release envelope. `(attack=0.1, sustain=0.5, release=0.1, curve=1.0, clock=nullptr)` @@ -34,56 +34,56 @@ - **Line**: Line segment with the given start/end values and duration. If loop is true, repeats indefinitely. Retriggers on a clock signal. `(from=0.0, to=1.0, time=1.0, loop=0, clock=nullptr)` - **RectangularEnvelope**: Rectangular envelope with the given sustain duration. `(sustain_duration=1.0, clock=nullptr)` -## fft +## Fft -- **FFTContinuousPhaseVocoder**: FFTContinuousPhaseVocoder `(input=nullptr, rate=1.0)` -- **FFTConvolve**: FFTConvolve `(input=nullptr, buffer=nullptr)` -- **FFT**: FFT `(input=0.0, fft_size=SIGNALFLOW_DEFAULT_FFT_SIZE, hop_size=SIGNALFLOW_DEFAULT_FFT_HOP_SIZE, window_size=0, do_window=true)` +- **FFTContinuousPhaseVocoder**: Continuous phase vocoder. Requires an FFT* input. `(input=nullptr, rate=1.0)` +- **FFTConvolve**: Frequency-domain convolution, using overlap-add. Useful for convolution reverb, with the input buffer containing an impulse response. Requires an FFT* input. `(input=nullptr, buffer=nullptr)` +- **FFT**: Fast Fourier Transform. Takes a time-domain input, and generates a frequency-domain (FFT) output. `(input=0.0, fft_size=SIGNALFLOW_DEFAULT_FFT_SIZE, hop_size=SIGNALFLOW_DEFAULT_FFT_HOP_SIZE, window_size=0, do_window=true)` - **FFTNode**: FFTNode `(fft_size=None, hop_size=None, window_size=None, do_window=None)` - **FFTOpNode**: FFTOpNode `(input=nullptr)` -- **FFTFindPeaks**: FFTFindPeaks `(input=0, prominence=1, threshold=0.000001, count=SIGNALFLOW_MAX_CHANNELS, interpolate=true)` -- **IFFT**: IFFT `(input=nullptr, do_window=false)` -- **FFTLPF**: FFTLPF `(input=0, frequency=2000)` -- **FFTNoiseGate**: FFTNoiseGate `(input=0, threshold=0.5)` -- **FFTPhaseVocoder**: FFTPhaseVocoder `(input=nullptr)` -- **FFTTonality**: FFTTonality `(input=0, level=0.5, smoothing=0.9)` -- **FFTZeroPhase**: FFTZeroPhase `(input=0)` - -## operators - -- **Add**: Add `(a=0, b=0)` -- **AmplitudeToDecibels**: AmplitudeToDecibels `(a=0)` +- **FFTFindPeaks**: Find peaks in the FFT magnitude spectrum. Requires an FFT* input. `(input=0, prominence=1, threshold=0.000001, count=SIGNALFLOW_MAX_CHANNELS, interpolate=true)` +- **IFFT**: Inverse Fast Fourier Transform. Requires an FFT* input, generates a time-domain output. `(input=nullptr, do_window=false)` +- **FFTLPF**: FFT-based brick wall low pass filter. Requires an FFT* input. `(input=0, frequency=2000)` +- **FFTNoiseGate**: FFT-based noise gate. Requires an FFT* input. `(input=0, threshold=0.5)` +- **FFTPhaseVocoder**: Phase vocoder. Requires an FFT* input. `(input=nullptr)` +- **FFTTonality**: Tonality filter. Requires an FFT* input. `(input=0, level=0.5, smoothing=0.9)` +- **FFTZeroPhase**: Remove phase information from a frequency-domain input. Requires an FFT* input. `(input=0)` + +## Operators + +- **Add**: Add each sample of a to each sample of b. Can also be written as a + b `(a=0, b=0)` +- **AmplitudeToDecibels**: Map a linear amplitude value to decibels. `(a=0)` - **DecibelsToAmplitude**: DecibelsToAmplitude `(a=0)` - **ChannelArray**: Takes an array of inputs and spreads them across multiple channels of output. `()` - **ChannelCrossfade**: Given a multichannel input, crossfades between channels based on the given position within the virtual array, producing a single-channel output. `(input=nullptr, index=nullptr, num_output_channels=1)` - **ChannelMixer**: Downmix a multichannel input to a lower-channel output. If num_channels is greater than one, spreads the input channels across the field. If amplitude_compensation is enabled, scale down the amplitude based on the ratio of input to output channels. `(num_channels=1, input=0, amplitude_compensation=true)` - **ChannelSelect**: Select a subset of channels from a multichannel input, starting at offset, up to a maximum of maximum, with the given step. `(input=nullptr, offset=0, maximum=0, step=1)` -- **Equal**: Equal `(a=0, b=0)` -- **NotEqual**: NotEqual `(a=0, b=0)` -- **GreaterThan**: GreaterThan `(a=0, b=0)` -- **GreaterThanOrEqual**: GreaterThanOrEqual `(a=0, b=0)` -- **LessThan**: LessThan `(a=0, b=0)` -- **LessThanOrEqual**: LessThanOrEqual `(a=0, b=0)` -- **Modulo**: Modulo `(a=0, b=0)` -- **Abs**: Abs `(a=0)` -- **If**: If `(a=0, value_if_true=0, value_if_false=0)` -- **Divide**: Divide `(a=1, b=1)` +- **Equal**: Compares the output of a to the output of b. Outputs 1 when equal, 0 otherwise. Can also be written as a == b `(a=0, b=0)` +- **NotEqual**: Compares the output of a to the output of b. Outputs 0 when equal, 1 otherwise. Can also be written as a != b `(a=0, b=0)` +- **GreaterThan**: Compares the output of a to the output of b. Outputs 1 when a > b, 0 otherwise. Can also be written as a > b `(a=0, b=0)` +- **GreaterThanOrEqual**: Compares the output of a to the output of b. Outputs 1 when a >= b, 0 otherwise. Can also be written as a >= b `(a=0, b=0)` +- **LessThan**: Compares the output of a to the output of b. Outputs 1 when a < b, 0 otherwise. Can also be written as a < b `(a=0, b=0)` +- **LessThanOrEqual**: Compares the output of a to the output of b. Outputs 1 when a <= b, 0 otherwise. Can also be written as a <= b `(a=0, b=0)` +- **Modulo**: Outputs the value of a modulo b, per sample. Supports fractional values. Can also be written as a % b `(a=0, b=0)` +- **Abs**: Outputs the absolute value of a, per sample. Can also be written as abs(a) `(a=0)` +- **If**: Outputs value_if_true for each non-zero value of a, value_if_false for all other values. `(a=0, value_if_true=0, value_if_false=0)` +- **Divide**: Divide each sample of a by each sample of b. Can also be written as a / b `(a=1, b=1)` - **FrequencyToMidiNote**: Map a frequency to a MIDI note (where 440Hz = A4 = 69), with floating-point output. `(a=0)` - **MidiNoteToFrequency**: Map a MIDI note to a frequency (where 440Hz = A4 = 69), supporting floating-point input. `(a=0)` -- **Multiply**: Multiply `(a=1.0, b=1.0)` -- **Pow**: Pow `(a=0, b=0)` +- **Multiply**: Multiply each sample of a by each sample of b. Can also be written as a * b `(a=1.0, b=1.0)` +- **Pow**: Outputs a to the power of b, per sample. Can also be written as a ** b `(a=0, b=0)` - **RoundToScale**: Given a frequency input, generates a frequency output that is rounded to the nearest MIDI note. (TODO: Not very well named) `(a=0)` - **Round**: Round the input to the nearest integer value. `(a=0)` - **ScaleLinExp**: Scales the input from a linear range (between a and b) to an exponential range (between c and d). `(input=0, a=0, b=1, c=1, d=10)` - **ScaleLinLin**: Scales the input from a linear range (between a and b) to a linear range (between c and d). `(input=0, a=0, b=1, c=1, d=10)` -- **Subtract**: Subtract `(a=0, b=0)` -- **Sum**: Sum `()` -- **Sin**: Sin `(a=0)` -- **Cos**: Cos `(a=0)` -- **Tan**: Tan `(a=0)` -- **Tanh**: Tanh `(a=0)` +- **Subtract**: Subtract each sample of b from each sample of a. Can also be written as a - b `(a=0, b=0)` +- **Sum**: Sums the output of all of the input nodes, by sample. `()` +- **Sin**: Outputs sin(a), per sample. `(a=0)` +- **Cos**: Outputs cos(a), per sample. `(a=0)` +- **Tan**: Outputs tan(a), per sample. `(a=0)` +- **Tanh**: Outputs tanh(a), per sample. Can be used as a soft clipper. `(a=0)` -## oscillators +## Oscillators - **Constant**: Produces a constant value. `(value=0)` - **Impulse**: Produces a value of 1 at the given frequency, with output of 0 at all other times. If frequency is 0, produces a single impulse. `(frequency=1.0)` @@ -99,7 +99,7 @@ - **Wavetable**: Plays the wavetable stored in buffer at the given frequency. `(buffer=nullptr, frequency=440, phase=0, sync=0, phase_map=nullptr)` - **Wavetable2D**: Wavetable2D `(buffer=nullptr, frequency=440, crossfade=0.0, phase=0.0, sync=0)` -## processors +## Processors - **Clip**: Clip the input to min/max. `(input=nullptr, min=-1.0, max=1.0)` - **Fold**: Fold the input beyond min/max, reflecting the excess back. `(input=nullptr, min=-1.0, max=1.0)` @@ -107,35 +107,35 @@ - **WetDry**: Takes wet and dry inputs, and outputs a mix determined by wetness. `(dry_input=nullptr, wet_input=nullptr, wetness=0.0)` - **Wrap**: Wrap the input beyond min/max. `(input=nullptr, min=-1.0, max=1.0)` -## processors/delays +## Processors: Delays - **AllpassDelay**: All-pass delay, with feedback between 0 and 1. delay_time must be less than or equal to max_delay_time. `(input=0.0, delay_time=0.1, feedback=0.5, max_delay_time=0.5)` - **CombDelay**: Comb delay, with feedback between 0 and 1. delay_time must be less than or equal to max_delay_time. `(input=0.0, delay_time=0.1, feedback=0.5, max_delay_time=0.5)` - **OneTapDelay**: Single-tap delay line. delay_time must be less than or equal to max_delay_time. `(input=0.0, delay_time=0.1, max_delay_time=0.5)` - **Stutter**: Stutters the input whenever a signal is received on clock. Generates stutter_count repeats, with duration stutter_time. `(input=0.0, stutter_time=0.1, stutter_count=1, clock=nullptr, max_stutter_time=1.0)` -## processors/distortion +## Processors: Distortion - **Resample**: Resampler and bit crusher. sample_rate is in Hz, bit_rate is an integer between 0 and 16. `(input=0, sample_rate=44100, bit_rate=16)` - **SampleAndHold**: Samples and holds the input each time a clock signal is received. `(input=nullptr, clock=nullptr)` - **Squiz**: Implementation of Dan Stowell's Squiz algorithm, a kind of downsampler. `(input=0.0, rate=2.0, chunk_size=1)` - **WaveShaper**: Applies wave-shaping as described in buffer. `(input=0.0, buffer=nullptr)` -## processors/dynamics +## Processors: Dynamics - **Compressor**: Dynamic range compression, with optional sidechain input. `(input=0.0, threshold=0.1, ratio=2, attack_time=0.01, release_time=0.1, sidechain=nullptr)` - **Gate**: Outputs the input value when it is above the given threshold, otherwise zero. `(input=0.0, threshold=0.1)` - **Maximiser**: Gain maximiser. `(input=0.0, ceiling=0.5, attack_time=1.0, release_time=1.0)` - **RMS**: Outputs the root-mean-squared value of the input, in buffers equal to the graph's current buffer size. `(input=0.0)` -## processors/filters +## Processors: Filters - **BiquadFilter**: Biquad filter. filter_type can be 'low_pass', 'band_pass', 'high_pass', 'notch', 'peak', 'low_shelf', 'high_shelf'. Not recommended for real-time modulation; for this, use SVFilter. `(input=0.0, filter_type=SIGNALFLOW_FILTER_TYPE_LOW_PASS, cutoff=440, resonance=0.0, peak_gain=0.0)` - **EQ**: Three-band EQ. `(input=0.0, low_gain=1.0, mid_gain=1.0, high_gain=1.0, low_freq=500, high_freq=5000)` - **MoogVCF**: Moog ladder low-pass filter. `(input=0.0, cutoff=200.0, resonance=0.0)` - **SVFilter**: State variable filter. filter_type can be 'low_pass', 'band_pass', 'high_pass', 'notch', 'peak', 'low_shelf', 'high_shelf'. `(input=0.0, filter_type=SIGNALFLOW_FILTER_TYPE_LOW_PASS, cutoff=440, resonance=0.0)` -## processors/panning +## Processors: Panning - **AzimuthPanner**: Pan input around an equally-spaced ring of num_channels speakers. pan is the pan position from -1..+1, where 0 = centre front. width is the source's width, where 1.0 spans exactly between an adjacent pair of channels. `(num_channels=2, input=0, pan=0.0, width=1.0)` - **ChannelPanner**: Pan the input between a linear series of channels, where pan 0 = channel 0, 1 = channel 1, etc. No wrapping is applied. `(num_channels=2, input=0, pan=0.0, width=1.0)` @@ -144,7 +144,7 @@ - **StereoPanner**: Pans a mono input to a stereo output. Pans from -1 (hard left) to +1 (hard right), with 0 = centre. `(input=0, pan=0.0)` - **StereoWidth**: Reduces the width of a stereo signal. When width = 1, input is unchanged. When width = 0, outputs a pair of identical channels both containing L+R. `(input=0, width=1)` -## sequencing +## Sequencing - **ClockDivider**: When given a clock input (e.g., an Impulse), divides the clock by the given factor. factor must be an integer greater than or equal to 1. `(clock=0, factor=1)` - **Counter**: Count upwards from min, driven by clock. `(clock=0, min=0, max=2147483647)` @@ -155,7 +155,7 @@ - **Latch**: Initially outputs 0. When a trigger is received at set, outputs 1. When a trigger is subsequently received at reset, outputs 0, until the next set. `(set=0, reset=0)` - **Sequence**: Outputs the elements in sequence, incrementing position on each clock. `(sequence=std::vector ( ), clock=nullptr)` -## stochastic +## Stochastic - **Logistic**: Logistic noise. `(chaos=3.7, frequency=0.0)` - **PinkNoise**: Pink noise, with specified low/high cutoffs. `(low_cutoff=20.0, high_cutoff=20000.0, reset=nullptr)` diff --git a/mkdocs.yml b/mkdocs.yml index dc9309f6..c8b9cf0c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -50,9 +50,11 @@ nav: - Installation: - Installation: installation/index.md - Installing on macOS: + - Installing on macOS: installation/macos/index.md - Easy install: installation/macos/easy.md - Command-line installation: installation/macos/command-line.md - Installing on Linux: + - Installing on Linux: installation/linux/index.md - Command-line installation: installation/linux/command-line.md - Examples: examples.md - License: license.md