Skip to content

The Audio Graph

Every sound you make in resonon flows through an audio graph: a network of mixer channels that all feed one master output. If you’ve used a DAW, you already know its shape — tracks with an instrument and an effect chain, a fader, aux sends, and a master bus at the end. This page is the model behind that graph, so that routing, gain and modulation all hang on the same picture.

Here is a complete session with two channels: a drum track, and a reverb track the drums feed.

use "std/instruments" { Sampler, Kit };
use "std/effects" { Reverb };
let drums = AudioTrack("drums");
drums.set_instrument(Sampler(Kit("CR-78")));
let verb = AudioTrack("reverb");
verb.set_effects(#[Reverb("reverb", 0.8, 0.3)]);
drums << [bd sd bd sd];
drums.set_send(verb, 0.3);
master.volume << -2;
PLAY;

Two tracks, three routes. The drum track plays dry to master and sends a copy of itself to the reverb track at 30%. The reverb track plays its wet output to master as well. master sums everything and turns it down 2 dB before it reaches your speakers.

AudioTrack("drums") makes the channel the first time it runs and hands back the same channel every time after — same fader, same instrument, same effect chain. The name is not a label on a track; it is the track, the way a channel in Live is the channel whatever you did to it.

That is what makes re-running code safe while the music plays: a line you execute again talks to the track that is already there instead of stacking a second one. The flip side is that two AudioTrack("bass") calls mean one track. Two tracks need two names.

A track lives as long as a variable holds it. When nothing holds it any more — the last variable rebound, its block ended, or it was never bound — it is deleted at the end of that statement, exactly as .delete() deletes it: tracks that output to it now output to nowhere, and its name is free for a new track. A deleted track gives back its place in the track limit, so a loop that makes a track per iteration and keeps none can run as long as it likes.

A single track is a fixed chain. Audio enters at the top and leaves at the bottom:

input (hardware channels, when the track has one)
│
instrument (the sound source — Sampler, plugin, or DSP)
│
effect chain (effects applied in order: filter, delay, …)
│
fader (volume + pan)
│
sends ───────────▶ other tracks / master (post-fader copies)
│
▼
output (one: master, another track, or hardware channels)

The order is what matters:

  • Input is mixed into the track before anything else, so a live signal runs through the same effects and fader an instrument would.
  • The instrument generates the sound. A track holds exactly one.
  • The effect chain processes it in order, index 0 first. A filter-then-delay is not a delay-then-filter, so reordering the chain changes the sound.
  • The fader applies volume and pan after the effects.
  • Sends branch post-fader. Turn the track’s fader down and its sends quieten with it.

A track’s volume is in decibels; 0 is unity. Its pan runs from -1 (hard left) to 1 (hard right) with a constant-power law: a source keeps a steady perceived loudness as it moves across the stereo field. Centre is unity, and each side rises to +3 dB at the hard-panned extreme, so the total acoustic power stays constant — the behaviour of a DAW pan pot. The consequence is that a near-full-scale source can clip when hard-panned; trim its volume if it does.

master is the last gain stage. Every track reaches it at unity unless you route it elsewhere, and master.volume trims the whole mix, exactly like a master fader.

Every track has exactly one output. It starts as master at unity gain, which is why sound reaches your speakers without any wiring. >> sets that output — drums >> bus; — and setting it replaces it: there is nothing to add to, because there is only one. This is Live’s Audio To, and like Live’s chooser it is set on its own, apart from the track’s definition.

A send is different: it is an additional, post-fader copy to another destination, at a level of its own. The output keeps playing at unity, so a send layers on top of the dry signal — the classic reverb or delay return.

Sending to the track’s own output destination is the one case that doesn’t add a copy: it sets the level of the output edge instead. That is what turns two ordinary sends into a dry/wet crossfade — drums.set_send(rev, 0.6); drums.set_send(master, 0.4); routes the track through a bus without doubling its level. It also explains an ordering rule: >> sets the output edge back to unity, so setting the route after a crossfade undoes the crossfade.

The output can also be the audio interface itself — Output(2, 3), Live’s Ext. Out. That is still the one output, so it replaces the route to master; the track’s sends are unaffected, and it stays in the same latency compensation as the master, so it stays in time with the mix. master has an output too, and it can only be hardware; it starts on the first two channels.

The output can also be nowhere — Live’s Sends Only. drums >> nowhere; takes the track out of the mix, and its sends keep feeding their buses: a fully wet reverb feed, or a parallel bus with no dry signal. A track that reaches nothing at all is not heard, so its plugins’ latency delays nothing else. Channel numbers belong in the piece because every rig has a channel 0; the device they belong to is a fact about your machine, set in preferences (see The Machine and the Piece). Switching to an interface with fewer channels says which outputs it cannot play as routed: a stereo pair it has only one channel of plays folded to mono on that channel, and an output it has none of is silent.

Any track can act as a bus: route several tracks into it and its effect chain treats them as one. Because tracks feed tracks, the graph can be several layers deep (track → bus → master). Resonon processes tracks in dependency order — every track that feeds another is rendered first — and rejects routing cycles, so a bus always has its inputs ready before it runs.

A sidechain input feeds a track’s audio into an effect’s detector rather than into the mix. It creates a dependency, so it is ordered like any other edge and cannot form a cycle, but the source is not heard through it. A mono sidechain port folds a stereo source down to (L + R) / 2.

Faders, pans and send levels aren’t just static numbers. The << operator that sends a pattern to a track also binds a signal to a parameter:

use "std/signals" { Sine };
drums.volume << Sine(0.5).range(-12, 0);

The audio engine evaluates the signal as the graph runs, so the fader glides instead of stepping. Signals & Time explains how a signal keeps time.