Recording & Input
Everything so far has made sound inside Resonon. But a track can also listen to the outside world — a microphone, a guitar, a synth, anything plugged into your audio interface. You route an input to a track, arm it like a channel in a DAW, monitor it through the track’s effects, and capture takes to disk. Live input and recorded patterns coexist on the same tracks, so you can sing over a beat you’re coding and bounce the result.
Here’s the whole loop — route an input, arm it, record, and grab the take:
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone"); // listen to a devicemic = mic.arm(); // ready to recordmic = mic.monitor("auto"); // hear it while armed
RECORD; // start capturing on every armed track// ... play or sing ...PAUSE; // stop capturing
let take = mic.take(); // pull the recording off the trackif take != NUL { take.save("vocals.wav");}Each piece of that — finding devices, arming, monitoring, the transport, and retrieving takes — is worth a closer look.
Discovering Devices
Section titled “Discovering Devices”audio_devices() returns a list of every audio device on your system. Each entry
is a dict carrying the device name, its input_channels and output_channels
counts, the default sample_rate in hertz, and an is_default flag. To find
something you can record from, keep the ones with at least one input channel:
let devices = audio_devices();for d in devices { if d["input_channels"] > 0 { PRINT d["name"] + " (" + d["input_channels"] + " inputs)"; }}Naming Inputs in Your Config
Section titled “Naming Inputs in Your Config”If you always record through the same interface, naming your inputs once in your
config file (~/.resonon/config.toml) beats typing channel numbers every session:
[audio.input]device = "Focusrite Scarlett 2i2"
[audio.input.channels]mic = [0]vocals = [0, 1]guitar = [2]A single-element array like [0] is a mono input from that channel; a
two-element array like [0, 1] is a stereo pair. Once they’re named, you pass the
channel name to .input() instead of the device name, and Resonon resolves it
to the right channels on the right interface.
Routing an Input to a Track
Section titled “Routing an Input to a Track”.input(name) points a track at an input source. The argument is either a device
name or one of your configured channel names:
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone");You don’t need a config file for basic use — if no input stream is running yet,
.input() starts one for the named device automatically. Like the other track
methods, it returns the track so calls chain.
Arming and Monitoring
Section titled “Arming and Monitoring”A track won’t capture anything until it’s armed, and arming requires an input
source — calling .arm() on a track with no .input() is an error. Only armed
tracks record when you trigger the transport. .disarm() undoes it:
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone");mic = mic.arm();// ... record ...mic = mic.disarm();Monitoring is whether you hear the live input through the output, set with
.monitor(mode). "in" always passes the input through; "auto" passes it only
while the track is armed or recording; "off" keeps it silent.
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone");mic = mic.monitor("in"); // always hear the micmic = mic.monitor("auto"); // hear it only when armed/recordingmic = mic.monitor("off"); // silentRecording
Section titled “Recording”The transport drives recording. RECORD starts playback and begins capturing on
all armed tracks at once:
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone").arm().monitor("auto");
RECORD;// ... perform ...PAUSE;PAUSE and STOP both halt the transport, but they finalize differently. PAUSE
freezes playback while the recording engine keeps the captured audio intact — call
.take() or STOP to finalize it. STOP finalizes the take, resets the
transport to the start, and mutes the master output, leaving the track armed so
you can RECORD another pass without re-arming.
| Command | Stops transport | Finalizes take | Keeps armed | Mutes master |
|---|---|---|---|---|
PAUSE | Yes | No | Yes | No |
STOP | Yes (+ reset) | Yes | Yes | Yes |
Multi-Track Recording
Section titled “Multi-Track Recording”Because RECORD captures every armed track simultaneously, tracking a band is
just a matter of arming more than one. Each track keeps its own independent
recording:
let vocals = AudioTrack("vocals");vocals = vocals.input("vocals").arm().monitor("auto");
let guitar = AudioTrack("guitar");guitar = guitar.input("guitar").arm().monitor("auto");
RECORD;// ... perform ...PAUSE;
let vocal_take = vocals.take();let guitar_take = guitar.take();Punch Recording
Section titled “Punch Recording”Punch recording arms the transport to a cycle range rather than the whole
performance — perfect for overdubbing one section or fixing a single phrase
without re-recording around it. .punch(in, out) sets both ends; recording goes
live at the in cycle and stops at the out cycle:
let rec = AudioTrack("rec");rec.input("MacBook Pro Microphone").arm();rec.punch(4, 12); // capture only cycles 4–11
RECORD; // playback rolls; recording arms itself at cycle 4Set one end only with .punch_in(cycle) (open-ended — starts there, no auto-stop)
or .punch_out(cycle) (starts immediately, stops there). .clear_punch() removes
the points, and .is_punched() tells you whether any are set:
let rec = AudioTrack("rec");rec.input("MacBook Pro Microphone").arm();rec.punch_in(8); // open-ended from cycle 8show(rec.is_punched()); // truerec.clear_punch();show(rec.is_punched()); // falseA few things worth knowing: punch transitions land on cycle boundaries, so
they stay in musical time; punch points persist across RECORD/STOP, so you
can punch the same window over several takes; and PAUSE inside a punch window
finalizes whatever partial take you’ve captured.
Getting Your Take
Section titled “Getting Your Take”There are two ways to pull a recording off a track, and both consume the buffer — once you’ve taken a recording, calling either again returns nothing until you record afresh. Pick one per take.
.take() returns a RecordedAudio object you can inspect before saving, or NUL
if nothing was captured. It exposes .duration() (seconds), .samples() (total
sample count), .sample_rate() (hertz), and a chainable .save(path):
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone").arm();RECORD;PAUSE;
let take = mic.take();if take != NUL { show(take.duration()); show(take.sample_rate()); take.save("take1.wav");}.save(path) on the track itself is the shortcut — it writes the recording
straight to a WAV and returns the track so it chains:
let mic = AudioTrack("mic");mic = mic.input("MacBook Pro Microphone").arm();RECORD;PAUSE;mic.save("takes/vocals_01.wav");Live Processing
Section titled “Live Processing”A monitored input flows through the track’s effect chain before it reaches the output, so the same effects you’d put on a sampler shape a live signal too. Load them before you arm and monitor, so the chain is ready when audio starts moving:
use "std/effects" { Reverb, Highpass };
let vox = AudioTrack("vocals");vox = vox.input("MacBook Pro Microphone");vox = vox.load_effect(Highpass(120));vox = vox.load_effect(Reverb(0.3, 0.5));vox = vox.monitor("in");// now you hear your voice, filtered and reverbed, in real timeRouting works on a live input as well. Send processed input to a shared bus with
.send_to(), exactly as you would a sampler track:
use "std/effects" { Reverb };
let reverb_bus = AudioTrack("reverb_bus");reverb_bus = reverb_bus.load_effect(Reverb(0.5, 0.7));
let vox = AudioTrack("vocals");vox = vox.input("MacBook Pro Microphone").monitor("in");vox = vox.send_to(reverb_bus, 0.4); // 40% to the reverb busLatency Compensation
Section titled “Latency Compensation”When you record while playing back, the captured audio arrives a little late by
the round-trip latency of your interface (input plus output). recording_offset()
trims that off the front of every recording. Read it with no argument, set it with
a millisecond value:
recording_offset(10); // trim 10 ms — match your interfaceshow(recording_offset()); // read the current offsetThe right value depends on your hardware; start with your interface’s advertised
round-trip latency and fine-tune by recording a click. To see what Resonon
currently measures, audio_latency() returns a dict with buffer_ms (audio
buffer latency), pdc_ms (plugin-delay-compensation path latency), sample_rate,
and the underlying buffer_frames and pdc_samples:
let lat = audio_latency();show(lat["buffer_ms"]);show(lat["pdc_ms"]);show(lat["sample_rate"]);You can persist the offset in your config so it’s set every session:
[audio]recording_offset_ms = 10.0For outboard gear — running a track out through a hardware effect or a guitar amp
and back — use .delay(ms) to compensate for that device’s own round-trip delay.
It shifts the track’s output later in time, and the PDC system nudges every other
track to keep the whole mix aligned (recordings on the delayed track shift to
match too):
let guitar = AudioTrack("guitar");guitar.delay(8); // ~8 ms round trip through an ampshow(guitar.delay()); // read it backNext Steps
Section titled “Next Steps”Once you can capture and shape audio, give your piece a structure.
- Arrangements — place patterns along a fixed-length timeline
- Effects — the effect chain your live input flows through
- Signals & Automation — automate track and effect parameters