Skip to content

Samplers

A sampler plays back recorded audio — a kick drum, a piano note, a vocal chop. Resonon ships with a built-in sample engine and two flavours of sampler. A Sampler is a drum machine: each name in your pattern triggers a different one-shot sample. A SamplerMelodic is a pitched instrument: it takes a recorded note and transposes it across the keyboard so you can play melodies.

Both live in the std/instruments module, alongside Kit, which loads a folder of samples:

use "std/instruments" { Sampler, SamplerMelodic, Kit };

Here’s the drum sampler from the previous chapter, start to finish:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
PLAY;

Kit("CR-78") names the built-in CR-78 kit and Sampler(...) describes an instrument built from it; instrument is what puts it on the track and makes the handle live. The pattern triggers samples by name. Reach into a sampler after that line, never before — see A constructor is a description.

The names in [bd sd bd sd] aren’t keywords — they’re the sample slots of the loaded kit. The CR-78 names follow its original drum machine: bd is the bass drum, sd the snare, hh the hi-hat, cy the cymbal, cp the clap, rs the rimshot, cb the cowbell. There are more — bongos, claves, maracas, a tambourine — enough to write a full groove from one kit:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [bd cp sd cp];
PLAY;

Not sure what a kit contains? show() prints its full sample table to the console:

use "std/instruments" { Sampler, Kit };
show(Kit("CR-78"));

Three kits come bundled: CR-78 and TR-808 for drums, and keys for melodic sampling. You can also load your own — a folder of .wav files under kits/, optionally with a kit.toml describing the sample-to-note mapping.

Every slot is mapped to a MIDI note, so you can trigger samples by number instead of name — handy when you think in note numbers or drive a GM-style kit. In the CR-78, 36 is the bass drum, 38 the snare, 42 the hi-hat. Names and numbers mix freely in one pattern:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [36 42 38 42]; // kick, hat, snare, hat
drums << [bd 42 sd 42]; // mixed names and numbers
PLAY;

A comma stacks patterns into parallel layers that play at once — kicks, a snare backbeat, and a running hi-hat, all in one bar:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [bd _ bd _, _ sd _ sd, hh*8];
PLAY;

The _ is a rest. Nesting [...] inside a step subdivides it, so you can pack a drum roll into a single beat: [[bd bd] sd bd sd]. These are all mini-notation features — the Patterns chapters go through them properly.

Individual hits can be shaped right in the pattern. Attach a method to a sample name and only that hit changes:

  • .vel(n) sets velocity — how hard the hit lands, 0 to 1. 1.0 is full; lower values are softer, good for ghost notes.
  • .pitch(semitones) transposes the sample up or down.
  • .pan(position) places the hit in the stereo field, -1 left to 1 right.
  • .gain(n) scales the hit’s level, 1.0 unchanged. This is where you go past full — velocity stops at 1.
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [bd.vel(1.0) sd.vel(0.4) bd.vel(1.0) sd.vel(0.4)]; // dynamics
drums << [bd bd.pitch(5) bd bd.pitch(-5)]; // pitch moves
drums << [hh.pan(-0.3) hh.pan(0.3) hh.pan(-0.3) hh.pan(0.3)]; // stereo hats
PLAY;

Each one shapes only the voice that hit starts, so the pad keeps its own settings for every other hit.

Chain them to combine — a loud, slightly detuned kick panned left:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.instrument(Sampler(Kit("CR-78")));
drums << [bd.gain(1.4).pitch(-2).pan(-0.5) sd.vel(0.5).pitch(2)];
PLAY;

For pitched material — bass, keys, pads — reach for SamplerMelodic. It maps a recorded sample across the keyboard, so note names in a pattern come out as a melody. The bundled keys kit is a good place to start:

use "std/instruments" { SamplerMelodic, Kit };
let lead = AudioTrack("lead");
lead.instrument(SamplerMelodic(Kit("keys")));
lead << [C4 E4 G4 C5];
PLAY;

SamplerMelodic(Kit(...)) is a ready-to-play preset: it loads the kit’s default_sample before the sampler can be placed on a track. A kit containing only one sample may omit that field because the choice is unambiguous. Use SamplerMelodic() when you deliberately want an empty device, then choose content later with .load_kit() or .load_sample().

Loading a different sample is explicit and replaces the preset selection:

use "std/instruments" { SamplerMelodic, Kit };
let keys = SamplerMelodic(Kit("keys"));
keys.load_sample("sound2");

Missing media, a missing root note, or an ambiguous multi-sample kit is an error at load time rather than a sampler that accepts notes but stays silent.

The same per-hit methods work on melodic notes. On a melodic sampler, .pitch(n) is a relative offset from the note you played: C4.pitch(12) sounds at C5, while E4.pitch(-12) sounds at E3. It composes with the sampler’s root-note tracking rather than replacing it. Gain and pan likewise shape only that note’s voice:

use "std/instruments" { SamplerMelodic, Kit };
let lead = AudioTrack("lead");
lead.instrument(SamplerMelodic(Kit("keys")));
lead << [C4 C4.pitch(12) C4.gain(0.5) C4.pan(-0.5)];
PLAY;

Note names, octaves, chords, and rests all behave as they do for drums — a comma still stacks voices, so [C4 E4 G4, D4 F4 A4] plays two chords in sequence.

A melodic sampler has an amplitude envelope — attack, decay, sustain, and release — that decides how each note swells and fades. Set it with .envelope, with all three time values in milliseconds:

use "std/instruments" { SamplerMelodic, Kit };
let lead = AudioTrack("lead");
let keys = SamplerMelodic(Kit("keys"));
lead.instrument(keys);
keys.envelope(10, 100, 1.0, 200); // attack, decay, sustain, release
lead << [C3 E3 G3 C4];
PLAY;

keys is the handle to the sampler on that track, and lead.instrument(keys) comes first for a reason: that line is what builds it, and .envelope(...) shapes what it built.

A quick attack and short release give a plucky, percussive note; a slow attack and long release turn the same sample into a swelling pad.

Pattern methods like .vel() shape one hit at a time. To change a sample for every hit — its overall gain, pan, envelope, slice points or reverse — reach into the sampler’s slot and set a parameter. Those belong to the pad, not to a single hit, and a per-hit .pan() or .gain() sits on top of whatever the slot says:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
let kit = Sampler(Kit("CR-78"));
drums.instrument(kit);
kit.slot("bd").param("gain") << 0.8; // every kick, a touch quieter
drums << [bd sd bd sd];
PLAY;

Same order again: .instrument() first, then reach into a slot. kit.slot("bd") names something inside a live sampler, so the sampler has to be on the track before you can name it.

Because that’s the same << you use for track volume, a slot parameter can take a moving signal too — auto-panning a hi-hat, pulsing a kick — which the Signals & Automation chapter covers. The sampler can also remap samples to new notes and assign choke groups (so an open hi-hat cuts off a closed one); when you need them, those live on the same Sampler value.

You can load kits, write grooves, sample melodies, and shape individual hits. Next, run a track through effects.