Basics

Previous: Get started

Nodes and modules

SNDC files are composed of nodes. A node is an instance of a particular module. A node has a name, a list of inputs and a list of outputs.

Let’s go back to our test.sndc file:

o: osc {
    function: "sin";
    freq: 440;
    duration: 1;
}

Here, we instantiated a node named o, instance of the module osc. The osc module defines several inputs among which function, freq and duration are required. In the test file above, we have set the values of those inputs as follows:

Let’s experiment a bit and add another node after o:

o: osc {
    function: "sin";
    freq: 440;
    duration: 1;
}

o2: osc {
    function: "sin";
    freq: 220;
    duration: 1;
}

And play it:

$ sndc_play test.sndc

We can hear a single tone again, but this time at frequency 220Hz. The 440Hz tone has disappeared despite still being instantiated in the file, and processed by sndc.

This is because sndc only plays the output of the last node of the file, ignoring all other nodes above. So, how can we play both sounds at once?

Modules' help

To play both sounds at once, we are going to need to mix them, with the mix module.

First, let’s ask sndc what the inputs and outputs of the mix module are:

$ sndc -h mix
[effect] mix - Mixer for up to 8 input buffers
Inputs:
    input0 [BUFFER] [REQUIRED]
        input #0
    input1 [BUFFER] [OPTIONAL]
        input #1
    input2 [BUFFER] [OPTIONAL]
        input #2
    input3 [BUFFER] [OPTIONAL]
        input #3
    input4 [BUFFER] [OPTIONAL]
        input #4
    input5 [BUFFER] [OPTIONAL]
        input #5
    input6 [BUFFER] [OPTIONAL]
        input #6
    input7 [BUFFER] [OPTIONAL]
        input #7
    gain0 [BUFFER | FLOAT] [OPTIONAL]
        gain #0, def 1
    gain1 [BUFFER | FLOAT] [OPTIONAL]
        gain #1, def 1
    gain2 [BUFFER | FLOAT] [OPTIONAL]
        gain #2, def 1
    gain3 [BUFFER | FLOAT] [OPTIONAL]
        gain #3, def 1
    gain4 [BUFFER | FLOAT] [OPTIONAL]
        gain #4, def 1
    gain5 [BUFFER | FLOAT] [OPTIONAL]
        gain #5, def 1
    gain6 [BUFFER | FLOAT] [OPTIONAL]
        gain #6, def 1
    gain7 [BUFFER | FLOAT] [OPTIONAL]
        gain #7, def 1
Outputs:
    out [BUFFER] output buffer w/ all inputs mixed

sndc -h <module> can be used to query the specification of all built-in modules. In the mix help above, we can see a list of inputs with their names, types, whether they are optional or required, and a short description.

We can also see a single output named out.

Let’s try and print the help for the osc module that we have used so far:

$ sndc -h osc
[generator] osc - A generator for sine, saw and square waves
Inputs:
    function [STRING] [REQUIRED]
        waveform: 'sin', 'square', 'saw' or 'input'
    waveform [BUFFER] [OPTIONAL]
        buffer containing waveform, used when 'input' is specified in 'function'
    freq [BUFFER | FLOAT] [REQUIRED]
        frequency in Hz
    amplitude [BUFFER | FLOAT] [OPTIONAL]
        amplitude in unit
    p_offset [FLOAT] [OPTIONAL]
        period offset, in period (1. = full period)
    a_offset [FLOAT] [OPTIONAL]
        amplitude offset, a constant that gets added to the resulting signal
    duration [FLOAT] [REQUIRED]
        duration of resulting signal in seconds
    sampling [FLOAT] [OPTIONAL]
        sampling rate, def 44100Hz
    interp [STRING] [OPTIONAL]
        interpolation of the resulting buffer, 'step', 'linear' or 'sine'
    param0 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 0
    param1 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 1
    param2 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 2
    param3 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 3
    param4 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 4
    param5 [BUFFER | FLOAT] [OPTIONAL]
        wave parameter 5
Outputs:
    out [BUFFER] output signal

There is quite a lot going on here, but we can see only three REQUIRED inputs: function, freq and duration, exactly the ones we have used in our test file. While there is no surprise regarding the type of function and duration, we can see that freq has BUFFER | FLOAT as a type: it can accept either, as we will see later.

Connecting nodes

Now that we understand better the inputs and output of osc and mix modules, we can try and add a mix node in our test file, and mix both tones together.

o: osc {
    function: "sin";
    freq: 440;
    duration: 1;
}

o2: osc {
    function: "sin";
    freq: 220;
    duration: 1;
}

m: mix {
    input0: o.out;
    input1: o2.out;
}

We’ve added a new node named m at the end of the file, instance of module mix. We’ve set two of its inputs: input0 and input1, but the value we’ve given them is not a number or a string. It’s a reference to the output of previous nodes.

If you look at the help for the osc module above, you will see one output: out, of type BUFFER. In the mix help, you will see that input0 and input1 both take in BUFFER, so this connection is valid: the types match. We refer to nodes' output with nodeName.outputName.

Let’s play the file now:

$ sndc_play test.sndc

We can hear both tones together.

Listing available modules

At that point, the obvious question probably is, “how do I see the list of available modules?”. Fortunately, there is a simple command for this, sndc -l:

$ sndc -l
Available modules:

  - debug:
    print - Prints input buffer to specified file for plotting/analysis
    satwarn - Outputs warning in stderr if input buffer is saturating

  - effect:
    echo - Produces a series of exponentially decaying echoes
    envelop - Apply envelop to input signal
    mix - Mixer for up to 8 input buffers
    reverb - Implementation of the Freeverb algorithm
    slider - Mix 2 input buffers according to a specified gain profile

  - filter:
    biquad - Bi-quadratic filter, two poles and two zeroes
    fft_filter - Generic FFT-based lowpass / highpass filter
    filter - Generic lowpass/bandpass/highpass filter
    simplelp - Low pass filter using simple and fast diff equation

  - generator:
    noise - Noise generator
    osc - A generator for sine, saw and square waves

  - math:
    binop - Binary operation between two buffers or numbers
    convol - Convolution of an input buffer with a kernel
    fft - Compute the fast Fourier transform of input buffer
    func - A generator for mathematical functions
    ifft - Compute the inverse Fourier transform of input buffers

  - sequencer:
    drumbox - Simple drum machine that sequences its input samples
    keyboard - Melodic sequencer for a given intrument node
    layout - A generic layout helper to mix layers together
    monoseq - Takes a simple single-voice pattern and generates corresponding buffers of frequency and amplitude to feed into
a mono synthesizer
    nodeseq - Executes a node and appends outputs at given times

  - util:
    note - A simple note to frequency converter
    resize - Extend or crop input buffer
    reverse - Reverse the input buffer
    sampler - External sample buffer loader
    var - Variable, copies its input value to its output value

This command returns a list of all available, built-in modules. Each of those module has an associated help that can be displayed with sndc -h <module> as we saw above. The modules are loosely sorted by category: there are generators which produce a signal, filters which apply different transformations on the frequencies of the input signal, effects with the familiar reverb, echo and so on.

Next: A simple police siren