Record
The Record panel captures what you play. It has two kinds of take: a MIDI take (the notes you played, quantized to the section’s grid) and an audio take (a single track recorded to WAV). They commit in different directions — MIDI becomes a pattern you can edit, audio becomes a sample in your Library.
What it captures
Section titled “What it captures”- MIDI take: the notes played over the transport while it loops, kept as played-note events you can fold into a pattern.
- Audio take: one track, recorded as 16-bit PCM mono WAV. On stop it
uploads to your Library via
POST /api/uploadsand is auto-namedrecording-N—Nis the next free index, so takes never clobber each other.
Recording MIDI needs a section with bars
Section titled “Recording MIDI needs a section with bars”MIDI capture rides the bar clock, so it needs somewhere to land: a section with bars and a running transport. A stopped transport disables the control — there is no grid to quantize against. Give the take a home first:
import type { Song } from "@barline/runtime";import { bars, beats, r } from "@barline/core";
export const config = { bpm: 128, key: "A minor" };
export default function (song: Song) { const lead = song.track({ name: "lead", output: { kind: "synth", synth: { id: "lead" } }, });
song.section("verse", bars(4), () => { // play into this section while it loops — the take quantizes here lead.play(r`x . x . x . x .`); });
song.arrange([{ section: "verse", repeat: 2 }]);}Loop the section, arm the track, and play. The captured notes come back as a
played-note take you can hand-edit into a notes([...]) literal in the section,
the same write-back surface the Clip detail piano roll edits.
Commit a jam straight to the arrangement
Section titled “Commit a jam straight to the arrangement”A finished MIDI take writes a recording-N.ts pattern file you own as code. Pick
a track and hit → arrangement and the studio commits it as real structure in
one edit: it adds the import, inserts a named section that plays the take —
import { recording1 } from "./recording-1";
song.section("jam-1", bars(4), (t) => { t.tracks["bass"]?.play(recording1);});— and appends { section: "jam-1" } to song.arrange([...]). The take is now an
addressable part of the song (it appears in the Arrange and
Session views), not a loose file to hand-wire. The edit goes
through the same Yjs path as every keystroke, so the arrangement array is
regenerated cleanly (never comma-spliced) and the result always parses.
Using an audio take
Section titled “Using an audio take”An audio take is just a Library sample once it uploads, so it lives at
/api/uploads/<id> like any other upload. Wire it two ways.
As a clip — drop the take onto a track as an audio block with
track.clip({ url }), called inside a section body:
export default function (song: Song) { const recording = "/api/uploads/<id>"; // the take's url from your Library
const vox = song.track({ name: "vox", output: { kind: "synth", synth: { id: "lead" } }, });
song.section("intro", bars(8), () => { vox.clip({ url: recording, at: beats(0), beats: beats(16) }); });
song.arrange([{ section: "intro" }]);}clip() takes the same Clip options as any audio block —
at, beats, loop, gain, and stretch / semitones if you want the take
time-stretched or pitch-shifted to fit. at and beats are Beats, so wrap
bare numbers with beats() or bars().
As a sampler — map the take to a note and play it back pitched across the keyboard:
export default function (song: Song) { const chop = song.track({ name: "chop", output: { kind: "sampler", samples: { C3: "/api/uploads/<id>" }, // the recording-N take }, });
song.section("loop", bars(2), () => { chop.play(r`x . . . x . x .`); });
song.arrange([{ section: "loop", repeat: 4 }]);}Either way the take is a normal upload from there — content-addressed, served immutably, and shared with every other sample in your Library. Record captures the performance; the rest of the studio treats it like any other clip.