Your First Mix
On this page you build a small mix from nothing: three drum tracks, a filter, a shared reverb, and the master fader. At the end, you put the whole rig into one function that you can edit while it plays.
Work in a project, as in Your First Sound.
Write each step into src/main.non, select it, and press Cmd+Enter. Every step adds to what the
previous one built.
Before you start
Section titled “Before you start”Finish Language for a Reusable Piece so that
fn, return and tuple unpacking are familiar. Open your project’s src/main.non, evaluate
RESET; on its own, then remove that line. Check that all earlier tracks are silent.
This page builds a new mix with bundled kits; evaluate the blocks in order.
Make a track
Section titled “Make a track”A track is a channel on a mixer. It holds one instrument, has a fader, and sends its sound to the master.
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");drums.set_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
PLAY;You hear a kick and a snare, looping.
AudioTrack("drums")makes a track calleddrums. Run the line again and you get the same track, not a second one.set_instrument(...)puts an instrument on it — here aSamplerwith the built-in CR-78 drum kit.<<sends a pattern to the track. The track plays it every cycle until you send another one.
Set the volume and pan
Section titled “Set the volume and pan”Every track has a volume in decibels and a pan from -1 (left) to 1 (right). Set them with
<<:
drums.volume << -6;drums.pan << -0.5;The drums are quieter, and to the left.
Add more tracks
Section titled “Add more tracks”Give every track its own name. Replace the combined beat with three separate parts.
First send silence to the earlier drums track so it does not double the new kick and snare:
drums << [_];
let kick = AudioTrack("kick");let snare = AudioTrack("snare");let hats = AudioTrack("hats");
kick.set_instrument(Sampler(Kit("CR-78")));snare.set_instrument(Sampler(Kit("CR-78")));hats.set_instrument(Sampler(Kit("CR-78")));
kick.volume << -9;snare.volume << -9;hats.volume << -15;
kick << [bd*4];snare << [_ sd _ sd];hats << [hh*8];
PLAY;Three tracks play together. You can change each one without touching the others.
Add an effect chain
Section titled “Add an effect chain”Effects process a track’s sound after the instrument. set_effects sets a track’s effect chain, as a
list. The sound goes through the list in order:
use "std/effects" { Highpass, Delay };
hats.set_effects(#[Highpass("highpass", 2000), Delay("delay", 0.125, 0.3)]);The hats are thinner, and they echo. The first argument of each effect is its name in the chain.
Now change one setting. Keep a handle to the effect, put it on the track, and set a parameter through the handle:
let echo = Delay("delay", 0.125, 0.3);hats.set_effects(#[Highpass("highpass", 2000), echo]);
echo.parameter("Feedback") << 0.6;The echoes last longer. The chain was not rebuilt: the delay called "delay" was already there, so
it kept playing.
Share a reverb
Section titled “Share a reverb”A reverb for every track would be wasteful. Make one reverb track, and send a copy of the snare to it:
use "std/effects" { Reverb };
let verb = AudioTrack("verb");verb.set_effects(#[Reverb("room", 0.8, 0.3)]);
snare.set_send(verb, 0.3);The snare plays dry as before, and 30% of it also goes through the reverb. The verb track has no
instrument — it only processes what other tracks send to it.
Ride the master
Section titled “Ride the master”Every track reaches master, the last fader before your speakers. You never made it; it is always
there. Turn the whole mix down:
master.volume << -2;Write a track as one chain
Section titled “Write a track as one chain”Every setter returns the track, so a whole channel strip can be one expression. Read it from top to bottom: what makes the sound, what processes it, how loud.
use "std/effects" { Lowpass };
let bass = AudioTrack("bass") .set_instrument(Sampler(Kit("CR-78"))) .set_effects(#[Lowpass("lowpass", 400)]) .set_volume(-4);
bass << [bd _ _ bd _ _ bd _];.set_volume(-4) is the same as bass.volume << -4. Where the track goes is a statement of its own:
a new track goes to master, and bass >> verb; would route it through the reverb instead.
Put the rig in a function
Section titled “Put the rig in a function”A function that builds tracks and returns them is a layout. This last exercise returns to one drums track and the shared reverb. Send silence to the separate parts and the bass first; otherwise those tracks would keep playing under the new layout:
kick << [_];snare << [_];hats << [_];bass << [_];
fn stage() { let verb = AudioTrack("verb") .set_effects(#[Reverb("room", 0.8, 0.3)]);
let drums = AudioTrack("drums") .set_instrument(Sampler(Kit("CR-78"))) .set_effects(#[Lowpass("lowpass", 4000)]) .set_volume(-9) .set_pan(0) .set_send(verb, 0.3);
return (drums, verb);}
let (drums, verb) = stage();drums << [bd sd bd sd];
PLAY;The earlier tracks no longer trigger notes; their existing tails can finish. The drums fader and pan are set explicitly so the first exercise’s left pan does not carry into this layout.
While it plays, add an effect to the drums’ .set_effects(#[...]) list and run the whole block again. The
new effect is inserted, and nothing else stops: the tracks and effects with the same names are kept.
Remove the effect and run it again, and it is gone.
STOP; stops playback and goes back to the start. In VSCode, Shift+F5 does the same, and F5
toggles play and pause.
STOP;Next steps
Section titled “Next steps”- Play Samples — drum kits, melodic samples, and shaping each hit
- Group Tracks on a Bus — one fader and shared effects for a group
- Share an Effect with a Send — return tracks and send levels
- Signals and Modulation — the next tutorial: make parameters move
- Descriptions & Slots — why re-running the layout keeps what is playing