Skip to content

Library

The Library panel uploads raw audio into your song, browses what you’ve already uploaded, and hands you the id to reference it from code. Unlike the Registry — which distributes code packages — the Library holds bytes: one per-user, session-gated store for both audio samples and whole-font SoundFonts (ADR-032).

  • An upload dropzone accepting samples (.wav, .mp3, .ogg, .flac, .aiff) and SoundFonts (.sf2, .sf3 — prefer .sf3, ~5x smaller).
  • A library list, filterable by type, showing each upload’s name, kind, and its /api/uploads/<id> url with a copy action.

The list is per-user: you see only what you’ve uploaded, and uploading requires a session. Once an upload exists, anyone can fetch its bytes — the serve route is public.

A drop posts the file to POST /api/uploads?name=<file> (auth). The response is the upload record you reference from code:

{ "id": "<userId>/<hash>.wav", "url": "/api/uploads/<id>", "type": "sample", "name": "kick.wav" }

Keys are content-addressed — stored as uploads/<userId>/<sha>.<ext>, so re-uploading the same bytes dedupes to the same id and a reference never breaks. Listing is type-scoped (GET /api/uploads?type=sample|soundfont, auth, per-user); the bytes serve from GET /api/uploads/* (public, immutable cache).

A sample plays as a clip or via a sampler note-map:

import type { Song } from "@barline/runtime";
import { bars, beats } from "@barline/core";
export const config = { bpm: 138, key: "A minor" };
export default function (song: Song) {
// A multi-note sampler: map notes to upload urls.
const keys = song.track({
name: "keys",
output: {
kind: "sampler",
samples: { C3: "/api/uploads/<id>", G3: "/api/uploads/<id>" },
},
});
// A one-shot clip is scheduled on a track INSIDE a section body — never a
// track option. `at`/`beats` are `Beats`, so wrap numbers with beats()/bars().
song.section("intro", bars(4), () => {
keys.clip({ url: "/api/uploads/<id>", at: beats(0), beats: bars(4) });
});
song.arrange([{ section: "intro" }]);
}

A SoundFont is a melodic instrument — reference the font id and pick a GM program (0..127):

song.track({
name: "rhodes",
output: { kind: "soundfont", font: "<id>", program: 4 },
});

The font value is the upload id (a .sf3/.sf2 you dropped here), not a separate route — always /api/uploads/<id> for bytes, never /api/soundfonts.

The two stores sit side by side but never overlap: the Library is bytes you own and serve (samples and fonts, content-addressed, public on read); the Registry is source code others install. A sample is a url in your song; a package is a <name>.ts in your files. Upload here when you have audio; publish there when you have a reusable idea.