FunDSP

repository·master·Indexed 22 days ago

https://github.com/samiperttu/fundsp

A high-performance audio DSP library for Rust featuring an inline graph notation to describe audio processing networks as types. It supports both static (zero-cost) and dynamic audio graph construction, providing two parallel component systems: AudioNode for stack-allocated, compile-time fixed networks and AudioUnit for heap-allocated, runtime-flexible networks. The library includes features for block processing with SIMD acceleration, a Sequencer for sample-accurate event mixing, and a Net component for dynamic connectivity.

Tokens
49.1K
Snippets
104
Records
133
Agent score
78%

What's inside fundsp

  1. Understand Component Opcodes and type-level integers

    master
    FunDSP uses type-level integers to parameterize component opcodes. These integers are represented by the symbols U0, U1, U2, etc., which correspond to type-level values 0, 1, 2, and so on. These parameters are used within the prelude32 and prelude64 preludes to define the properties or configurations of specific audio components.
  2. Introduce real-time control using Atomic Variables

    master

    You can communicate data from external contexts to an audio graph using shared atomic variables.

    1. Declare: Create a shared variable with an initial value using shared(value).
    2. Send to Graph: Use the var or var_fn opcodes to instantiate the shared variable as an output in your graph.
    3. Update: Use the .set_value(new_value) method on the shared variable from any thread.

    Smoothing: To prevent abrupt parameter changes, pipe a shared variable through a follow(time_in_seconds) filter.

    Timer: The timer opcode maintains stream time in a shared variable. It has no inputs or outputs and can be joined to any node by stacking.

    use fundsp::prelude64::*;
    
    // 1. Declare
    let amp = shared(1.0);
    
    // 2. Use in graph
    let amp_controlled = noise() * var(&amp);
    
    // 3. Update from anywhere
    amp.set_value(0.5);
    
    // Pattern: Smoothing with follow
    let amp_smoothed = noise() * (var(&amp) >> follow(0.1));
  3. Compare AudioNode and AudioUnit component systems

    master

    FunDSP provides two parallel component systems for building audio graphs. All components use 32-bit floating point samples (f32).

    FeatureAudioNodeAudioUnit
    Dispatchstatic, inlineddynamic, object safe
    Allocationstackheap
    ConnectivityFixed at compile timeFixed after construction
    ArityFixed at compile timeDetermined at runtime

    Use AudioNode for high-performance, fixed-structure networks. Use AudioUnit when you need to defer decisions about input/output arities or contents to runtime.

  4. How to use graph notation for node composition

    master

    FunDSP uses a graph-based notation to compose audio nodes. You can connect nodes using several operators:

    • Pipe (>> or pipe(x, y)): Chains node x into node y. x >> y is identical to pipe(x, y).
    • Stack/Sum (| or + or stack(x, y) or sum(x, y)): Combines signals. x | y is identical to stack(x, y) and sum(x, y).
    • Product (* or product(x, y)): Multiplies signals. x * y is identical to product(x, y).
    • Not/Thru (! or thru(x)): Passes through missing outputs. !x is identical to thru(x).
    // Example of piping a sine wave into a lowpass filter
    sine_hz(440.0) >> lowpass_hz(1000.0, 0.7)
    
    // Example of summing two oscillators
    sine_hz(440.0) | sine_hz(880.0)
    
    // Example of multiplying a signal by a constant
    sine_hz(440.0) * 0.5
  5. How graph notation and expressions work

    master

    FunDSP uses a graph notation where expressions define signal processing graphs. For example, A >> (B ^ C ^ D) defines a graph with the inputs of A and outputs B, C, and D in parallel.

    Key characteristics:

    • Static Optimization: The structure is packed, monomorphized, and inlined. Connectivity is checked at compile time.
    • Tree Structure: Graph combinators consume their arguments, which prevents cycles and ensures a tree-shaped computation graph.
    • Reuse: To reuse components, define them as functions, closures, or use .clone().
    • Connectivity Errors: Mismatched connectivity results in compilation errors involving typenum types.
    // Example of graph notation
    A >> (B ^ C ^ D)
  6. Analyze signal flow, latency, and frequency response

    master

    FunDSP features a signal flow system that can analytically calculate the frequency response of any linear network. Linear networks are constructed from linear filters, delays, and operations like mixing, chaining, and constant scaling.

    Additionally, the system analyzes signal latencies from input to output, which allows for the automatic removal of pre-delay from effects chains. Note that 'latency' refers to involuntary causal delay; voluntarily placed delay elements (like the tick opcode) do not count as latency in this analysis.

    use fundsp::prelude64::*;
    // Example: Verifying a 2-point averaging filter has zero gain at Nyquist
    assert!((pass() & tick()).response(0, 22050.0).unwrap().norm() < 1.0e-9);
  7. Understand broadcasting in FunDSP

    master

    Arithmetic operators are applied to outputs channelwise.

    • Component Arithmetic: Arithmetic between two components (e.g., A * B) never broadcasts channels; the channel arities must match exactly.
    • Constant Arithmetic: Direct arithmetic with f32 values (e.g., A * 2.0) broadcasts the constant to an arbitrary number of channels.
    • Negation: The negation operator -A broadcasts, behaving like (0.0 - A).
  8. Use custom operators for audio component combination

    master

    FunDSP provides custom operators to combine audio components inline. These operators follow specific precedence rules and connectivity requirements.

    Precedence (Highest to Lowest):

    1. -A (Negate)
    2. !A (Thru)
    3. A * B (Multiply/Ring Modulation)
    4. A * constant (Broadcast Multiply)
    5. A + B (Sum/Mix)
    6. A + constant (Broadcast Add)
    7. A - B (Difference)
    8. A - constant (Broadcast Subtract)
    9. A >> B (Pipe/Chain)
    10. A & B (Bus)
    11. A ^ B (Branch)
    12. A | B (Stack)

    Note: constant refers to an f32 value. All operators are associative except for the left-associative -.

    | Expression | Meaning | Inputs | Outputs | Notes |
    | -------------- | ----------------------------- |:-------:|:-------:| ------------------------------------------- |
    | `-A` | negate `A` | `a` | `a` | Negates any number of outputs, even zero. |
    | `!A` | thru `A` | `a` | same as inputs | Passes through extra inputs. |
    | `A * B` | multiply `A` with `B` | `a` `+` `b` | `a` `=` `b` | Aka amplification, or ring modulation when both are audio signals. Number of outputs in `A` and `B` must match. |
    | `A * constant` | multiply `A` | `a` | `a` | Broadcasts constant. Same applies to `constant * A`. |
    | `A + B` | sum `A` and `B` | `a` `+` `b` | `a` `=` `b` | Aka mixing. Number of outputs in `A` and `B` must match. |
    | `A + constant` | add to `A` | `a` | `a` | Broadcasts constant. Same applies to `constant + A`. |
    | `A - B` | difference of `A` and `B` | `a` `+` `b` | `a` `=` `b` | Number of outputs in `A` and `B` must match. |
    | `A - constant` | subtract from `A` | `a` | `a` | Broadcasts constant. Same applies to `constant - A`. |
    | `A >> B` | pipe `A` to `B` | `a` | `b` | Aka chaining. Number of outputs in `A` must match number of inputs in `B`. |
    | `A & B` | bus `A` and `B` | `a` `=` `b` | `a` `=` `b` | Sum `A` and `B`. `A` and `B` must have identical connectivity. |
    | `A ^ B` | branch input to `A` and `B` in parallel | `a` `=` `b` | `a` `+` `b` | Number of inputs in `A` and `B` must match. |
    | `A | B` | stack `A` and `B` in parallel | `a` `+` `b` | `a` `+` `b` | Concatenates `A` and `B` inputs and outputs. |
  9. Understand FunDSP Graph Notation

    master

    FunDSP uses an algebraic graph notation to describe audio processing networks. This notation allows you to express complex algorithms (like FM synthesis) using standard Rust operators, which are compiled into stack-allocated, inlined forms. Connectivity errors are caught at compile time. The notation is generic over channel arities, meaning a mono graph can often be used as a stereo graph by simply swapping mono components for stereo ones.

    // Example: FM oscillator
    sine_hz(f) * f * m + f >> sine()
  10. Understand AudioNode Arity and Type Encoding

    master

    FunDSP represents audio network structure at the type level using the AudioNode component system.

    Arity and Types

    Input and output arities are encoded using types U0, U1, ..., UN (from the typenum crate). These are accessible via the associated types AudioNode::Inputs and AudioNode::Outputs.

    The An<X> Wrapper

    Preludes use the An<X: AudioNode> wrapper type. This wrapper provides operator overloading (like ^, |, >>, &) and implements the AudioUnit trait.

    Type Signatures

    When defining functions that return a graph, you can use impl AudioNode for brevity, but the type will be opaque. To allow the node to be used in further combinations, you should declare the full arity in the signature:

    use fundsp::prelude64::*;
    fn split_quad() -> An<impl AudioNode<Inputs = U1, Outputs = U4>> {
        pass() ^ pass() ^ pass() ^ pass()
    }

    Debugging Opaque Types

    Because the compiler reports these types in an opaque form (e.g., An<Bus<Noise, Pipe<Constant<...>, Sine>>>), you can use the provided example utility to unscramble them:

    cargo run --example type -- "fundsp::combinator::An<Bus<Noise, Pipe<Constant<typenum::uint::UInt<typenum::uint::UTerm,typenum::bit::B1>>, Sine>>>"
  11. Choose a FunDSP Prelude environment

    master

    FunDSP provides three compatible prelude environments to balance precision and performance:

    • fundsp::prelude: The default generic interface.
    • fundsp::prelude64: Uses 64-bit internal state for components to maximize audio quality (ideal for audio hacking).
    • fundsp::prelude32: Uses 32-bit internal state for components to maximize processing speed.
  12. Use free functions for audio processing

    master
    FunDSP provides a set of free functions available in the environment that can be used directly for audio processing and synthesis tasks. These functions act as building blocks for constructing audio graphs.