Instruments & outputs
Every track has exactly one output: the thing that turns its notes into sound.
It’s pure data — a tagged union, discriminated by kind — so the runtime
maps it to the actual voice. Five kinds: synth, sampler, soundfont,
midi, and external for hardware with an audio return.
import type { Song } from "@barline/runtime";synth — the built-ins
Section titled “synth — the built-ins”output: { kind: "synth", synth: { id: "kick", params: { volume: -12 } } }synth.id names a built-in voice; synth.params is an optional
Record<string, number | string> of per-instrument knobs (e.g.
{ volume: -12, note: 86 }). The nine drum/synth primitives:
id | Role |
|---|---|
kick | The four-on-the-floor anchor |
hat | Closed hi-hat tick |
clap | Off-beat backbeat clap |
metal | Metallic perc / ride hit |
bass | Sub / rumble bass |
lead | Mono lead line |
kick909 | Punchier 909-style kick |
acid303 | Squelchy 303-style mono |
stab | Chord/organ stab |
FAUST polyphonic voices
Section titled “FAUST polyphonic voices”Nine voices are FAUST-compiled and 16-voice polyphonic — referenced by the
same synth.id. The runtime lazily loads only the devices named by the song.
If an artifact is unavailable it falls back to a Tone PolySynth, so playback
never breaks:
id | Role |
|---|---|
subtractive | Classic subtractive poly |
supersaw | Detuned supersaw stack |
fm | FM operator voice |
pluck | Karplus-style pluck |
acid | 303 diode-ladder mono |
hardkick | Distorted hard-techno kick |
prism | Dual-engine flagship synth: independent shapes/filters, cross-mod, routing, unison, drive, and space |
drumForge | Unified kick/snare/clap/hat/percussion synthesis family |
resonator | Playable modal physical model with mallet and bow excitation |
const lead = song.track({ name: "lead", output: { kind: "synth", synth: { id: "supersaw", params: { volume: -8 } } },});Per-note expression
Section titled “Per-note expression”Every FAUST voice supports deterministic pitch bend, pressure, timbre, glide,
and legato. Attach those values to pattern events with express():
import { express, notes, beats } from "@barline/core";
const phrase = express( (_event, index) => ({ pressure: index % 3 === 0 ? 0.8 : 0.3, timbre: index / 8, pitchBend: index % 2 === 0 ? 0 : 0.08, glide: 0.04, legato: index > 0, }), notes(["F3", "Ab3", "C4", "Eb4"], beats(0.5)),);Expression is pure event data. The generated FAUST processor queues complete timestamped note/expression packets on the audio thread, so live and offline rendering use the same timing model.
sampler — a multisample map
Section titled “sampler — a multisample map”output: { kind: "sampler", samples: { "C3": "/api/uploads/<id>", "C4": "/api/uploads/<id>" },}samples is a note → URL map. The runtime pitches each zone to fill the gaps,
so a few well-placed root notes cover a whole range. URLs are uploaded assets at
/api/uploads/<id> — see the Library for how to upload them.
soundfont — a GM program
Section titled “soundfont — a GM program”output: { kind: "soundfont", font: "<id>", program: 0 }font is an uploaded .sf2/.sf3 (an /api/uploads/<id>); program is a
General MIDI melodic program number, 0..127 (0 = acoustic grand piano).
Upload soundfonts the same way as samples — both live in the unified
Library.
midi — drive external gear
Section titled “midi — drive external gear”output: { kind: "midi", channel: 1, port: "IAC Driver Bus 1" }Sends note events out over Web MIDI on channel (1..16). port is optional —
omit it to use the default output port.
When an explicitly named port disappears, Barline reports it offline and sends
nothing; it never silently reroutes the performance to another connected device.
Audio Setup can also run MIDI Clock as a leader or follower. The Sync Hub supports Start/Continue/Stop and Song Position Pointer at 24 PPQN and shows measured BPM, jitter, and drift. Arming follower mode confirms audio ownership before external transport can start playback. Start resets to zero; Continue resumes the retained Song Position Pointer, including sub-bar positions. A leader starting after a seek sends SPP plus Continue. Ableton Link is not part of the current contract.
external — MIDI out plus a calibrated audio return
Section titled “external — MIDI out plus a calibrated audio return”output: { kind: "external", channel: 1, port: "USB MIDI Interface", returnRoute: "external-1", hardwareLatencyMs: 1.4,}An external output sends the same timestamped note stream as midi, then binds
the track to a logical return name. In Audio Setup, map external-1 to the
physical input/output pair and run loopback calibration. Physical device ids and
measured offsets stay machine-local; the song remains portable.
Changing either physical side revokes the old calibration and print approval
until the new route is measured.
The measured route plus optional fixed converter/device delay enters the normal sample latency ledger and graph delay compensation. The return is monitored through the track’s device/effect/mixer path.
Offline render cannot summon hardware. Choose External return print in the
Record panel to capture an exact-frame managed take first. The printed clip can
then be edited, exported, or source-frozen like any internal instrument.
Because a hardware return is shared, per-play .through() chains are ignored
on midi and external tracks; use the track chain or process the printed
audio instead.
Putting it together
Section titled “Putting it together”A song file is export const config plus a default function the runtime calls
with a live song. Here are several tracks across kinds:
import type { Song } from "@barline/runtime";import { fx, notes, bars, beats, r } from "@barline/core";
export const config = { bpm: 132, key: "A minor" };
export default function (song: Song) { const kick = song.track({ name: "kick", output: { kind: "synth", synth: { id: "kick909" } }, effects: [fx.distortion({ drive: 0.3, wet: 0.5 })], });
const keys = song.track({ name: "keys", output: { kind: "soundfont", font: "<id>", program: 4 }, // electric piano });
const drums = song.track({ name: "drums", output: { kind: "sampler", samples: { C2: "/api/uploads/<id>", D2: "/api/uploads/<id>" }, }, });
song.section("intro", bars(4), () => { kick.play(r`x . . . x . . . x . . . x . . .`); drums.play(r`. . x . . . x .`); keys.play(notes(["C3", "Eb3", "G3"], beats(1))); });
song.arrange([{ section: "intro" }]);}Once an instrument is making sound, shape it with the track’s effects chain.