pnpm add @audio-ui/core
import { createPianoRollController, DRAG_THRESHOLD, cellStart, changeLevel, changeVelocity, /* … */ } from "@audio-ui/core/piano-roll";| Adapters | @audio-ui/react/piano-roll, @audio-ui/elements/piano-roll |
|---|---|
| Used by | arrangement, dawproject, drum-steps, recording, use-arrangement, use-piano-roll-playback |
| Export | Kind | What it is |
|---|---|---|
createPianoRollController | function | A piano roll's behavior with no view attached: its notes, held or controlled, its selection and the pitches sounding, and every gesture and key that edits them. |
DRAG_THRESHOLD | const | How far a pointer moves before a press on the grid or a note becomes a drag. |
PianoRollBand | interface | A selection rectangle, in px from the grid's top left corner. |
PianoRollChangeEventDetails | type | What onValueChange receives with each change: the edit it made (one for every change of a gesture), the reason, the event behind it, and cancel() to refuse the change. |
PianoRollChangeEventReason | type | Why the notes changed: "draw", "drag", "keyboard", "double-click" or "input-change". |
PianoRollCommitEventDetails | type | What onValueCommitted receives once a gesture ends: the edit it made (one for every change of a gesture), the reason and the event behind it. |
PianoRollCommitEventReason | type | Why the notes were committed: the same reasons as a change. |
PianoRollController | interface | What createPianoRollController returns: the snapshot to draw from, and a call for each gesture, key and command that edits the notes or the selection. |
PianoRollEdit | type | What a change did to the notes. |
PianoRollKeyDetails | type | A key pressed on the keyboard beside the roll, before it sounds. |
PianoRollNoteHandle | type | Which pointer handle a note gesture takes: its body, or one of its edges. |
PianoRollNoteOffEventDetails | type | What onNoteOff receives when a pitch stops: the reason and the event. |
PianoRollNoteOnEventDetails | type | Why a pitch sounds: a key pressed, glided onto or played from the keyboard, or a note drawn, picked up or moved. |
PianoRollOptions | interface | The options of createPianoRollController: the notes and the selection, each held or controlled, the timeline, the pitch range and shortest note, the mode, the draw style, the scale and folding, and the callbacks. |
PianoRollSelectionEventDetails | type | What onSelectionChange receives: the reason ("press", "marquee", "draw", "drag", "keyboard" or "double-click"), the event, and cancel() to refuse it. |
PianoRollSnapshot | interface | What a view draws the roll from. |
PianoRollTimeline | interface | The timeline the roll sits in: its length, grid, zoom and playback position. |
cellStart | function | The start of the grid cell value falls in: where a note drawn there begins. |
changeLevel | function | Changes level of every note in ids by delta, each kept within 0…1, so the notes keep their differences: the velocity, or the chance to play. |
changeVelocity | function | Changes the velocity of every note in ids by delta, each kept within 0…1: a phrase keeps its dynamics, where setting them all to one value would flatten it. |
chopNotes | function | Chops each note in ids into parts notes of the same length, as many as fit at minLength or longer: a held note becomes a repeated one. |
deleteTime | function | Takes the time from start to end out of the clip: the notes inside it go, those after it move back by its length, and one crossing it loses the part inside. |
duplicateAfter | function | Copies of the notes in ids placed right after them, one selection's length on, so a phrase repeats itself the way it is played. |
duplicateNotes | function | Copies of the notes in ids, under new ids from nextId, left where the originals are. |
duplicateTime | function | Repeats the time from start to end right after itself: silence opens at end and the notes in the range, cut to it, are copied into it under new ids. |
findNotes | function | The ids of the notes that match query, in the order they play: to select every note of a pitch, a range of velocities or lengths, out of a scale, or every other one. |
fitNotesToScale | function | Moves each note in ids that is out of scale to the nearest pitch in it, the lower of two as near. |
humanizeNotes | function | Loosens the notes in ids the way a player would: each start moves by up to timing either way and each velocity by up to velocity, from random (0…1, Math.random by default). |
insertTime | function | Opens length of silence at at: the notes from there on move later by it, one crossing at is split there with its second part moved along, and what then runs past the clip's duration is cut off. |
invertNotes | function | Turns the notes in ids upside down: the highest becomes the lowest, around the middle of their range. |
joinNotes | function | Joins the notes in ids that share a pitch into one, from the earliest start to the latest end: the earliest keeps its id and velocity. |
keyboardEdit | function | What an arrow key does to the notes in ids: Left and Right move them by step, or with Shift change their length; Up and Down move them by a semitone, or with Shift an octave. |
legatoNotes | function | Lengthens or shortens each note in ids to end where the next of them starts, so a phrase plays without gaps. |
moveNotes | function | Moves the notes in ids by the same amount, in time and in pitch, so a chord keeps its spacing. |
Note | interface | A note on the roll: its pitch, start and length, how hard and how likely it plays, how far its velocity may stray, and whether it is deactivated. |
NoteId | type | A note's id: unique within its clip. |
NoteLevel | type | A note's value a lane draws as a bar, 0…1: how hard it plays, or how likely it is to. |
NoteQuery | interface | What findNotes looks for; every condition given must hold. |
notesWithin | function | The ids of the notes overlapping a stretch of time and a band of rows. |
pasteNotes | function | Copies of clipboard under new ids, the earliest starting at at: a phrase keeps its spacing wherever it lands, and stops where the roll ends. |
quantizeNotes | function | The notes in ids with their start on the nearest grid line: their length, and the rest, stay. |
RollBounds | interface | What every edit keeps a note within: the timeline's length, the pitch range and the shortest length. |
rampVelocity | function | Sets the velocity of the notes in ids along a line, from from on the earliest to to on the latest: a crescendo, or a fade. |
resizeNotes | function | Moves one edge of every note in ids by delta: the end makes them longer or shorter, the start trims them from the front while their end stays put. |
reverseNotes | function | Plays the notes in ids backwards: each lands where it would end, mirrored across their span. |
sameNotes | function | Whether two lists hold the same notes, in the same order. |
sanitizeNote | function | Keeps a note on the roll: inside the timeline, in the pitch range, at least minLength long. |
selectionSpan | function | The time the notes in ids cover, from the earliest start to the latest end, or null with none. |
setChance | function | Sets how likely the notes in ids are to play each time round, 0…1: 1 always, 0.5 every other time on average. |
setDeviation | function | Sets how far the velocity of the notes in ids may stray each time they play, kept within -1…1. |
snapEdgeDelta | function | How far the edge of note moves when dragged by delta, on the grid unless grid.step is 0. |
spatialOrder | function | Notes in the order the eye reads them: left to right, then top to bottom. |
splitNotes | function | Splits the notes in ids at at, or at each time in it, wherever a note holds that time: the first piece keeps the note's id, the others take new ones. |
stretchNotes | function | Makes the notes in ids factor times as long, around anchor (where the earliest starts, by default): 2 plays them at half the speed, 0.5 at twice. |
toggleMuted | function | Deactivates the notes in ids, or activates them again when every one of them already is: a deactivated note stays on the roll and does not play. |
RollScale | interface | A scale as a piano roll reads it: the pitch classes it keeps and the one it starts on, each from 0 (C) to 11 (B). |
Rows | interface | The rows a piano roll draws, highest pitch first, and the conversions between a pitch and its row. |
rollRows | function | The rows for pitches min to max: every one, or folded to just those the notes use, and with "scale" those in scale as well. |
Usage
A note is a pitch, a start and a length in the timeline's units, with a velocity, a chance, a deviation and a mute. The controller holds the notes, the selection and the pitches sounding, and every gesture and key that edits them; it follows the contract every controller shares:
import { createPianoRollController } from "@audio-ui/core/piano-roll";
const roll = createPianoRollController({
timeline: { duration: 16, step: 0.25, grid: { offset: 0, step: 0.25 }, zoom: null, position: () => clock.position },
defaultValue: [{ id: "n1", pitch: 60, start: 0, length: 1 }],
minPitch: 36,
maxPitch: 84,
onValueCommitted: (notes) => save(notes),
onNoteOn: (pitch) => synth.noteOn(pitch),
onNoteOff: (pitch) => synth.noteOff(pitch),
});| Option | Meaning |
|---|---|
value, defaultValue | The notes, controlled, or the uncontrolled start |
selection, defaultSelection, onSelectionChange | The selected notes' ids |
timeline | Its length, the grid edits snap to, the zoom and the playback position |
minPitch, maxPitch, minLength | The bounds every edit keeps a note within: 48 to 72, and the grid step, unless given |
mode | What a press on empty grid does: "select" (the default) starts a selection rectangle, "draw" draws a note |
drawStyle | In draw mode, "stretch" (the default) makes the drawn note as long as the drag; "paint" draws a note in each grid cell crossed |
scale, folded | The scale the rows shade, as its pitch classes and root (a RollScale), and folding the rows to the pitches used or to the scale |
activePitches | Keys lit from elsewhere, such as the notes playing |
The input
pressGrid(event, grid)starts a selection rectangle, or draws a note in"draw"mode;addAtPlaybackadds a note at the playback position from the keyboard.pressNote(id, handle, event, element, grid)moves a note by its body, or resizes it by an edge; the selected notes move with it, a chord keeping its spacing.pressStretch(edge, …)stretches the selection in time from its other edge.pressVelocityandpaintLevelsdraw the velocity, the deviation or the chance in a lane under the roll.keyNote,keyGridandshortcuttake the keyboard: on a note, the arrows move the selection (Shift changes its length), Enter or Space selects it and Delete removes it; Escape clears the selection, Cmd or Ctrl with A selects every note, with C, X, V and D copies, cuts, pastes at the playhead and duplicates, and Q alone quantizes the selection, or every note with none selected. Each returnstruewhen it handled the key.keyOnandkeyOffsound the keyboard beside the roll.
Edits
Every edit is also a plain function on a list of notes, for a menu, a command or an engine that keeps the notes itself. Each takes the notes, the ids to change and the bounds to keep them in, and returns new notes:
import { findNotes, fitNotesToScale, moveNotes, quantizeNotes } from "@audio-ui/core/piano-roll";
const bounds = { duration: 16, minLength: 0.25, minPitch: 0, maxPitch: 127 };
const all = new Set(notes.map((note) => note.id));
moveNotes(notes, all, 1, 12, bounds); // a beat later, an octave up
quantizeNotes(notes, all, (time) => Math.round(time), bounds); // starts on the beat
fitNotesToScale(notes, all, { pitchClasses: [0, 2, 4, 5, 7, 9, 11], root: 9 }, bounds); // into A minor
findNotes(notes, { pitchClasses: [0] }); // the ids of every CThe others edit time and shape: splitNotes, joinNotes, chopNotes, stretchNotes, legatoNotes, reverseNotes, invertNotes, duplicateNotes, duplicateAfter, pasteNotes, insertTime, deleteTime, duplicateTime and toggleMuted; and the dynamics: changeVelocity, rampVelocity, humanizeNotes, setChance and setDeviation. findNotes selects by pitch class, scale, velocity, length, chance or mute, every nth of them, or the inverse.
Rows
rollRows(notes, min, max, fold, scale) gives the rows a roll draws, highest pitch first, with rowOf and pitchAt to go between a pitch and its row: every pitch, only those the notes use, or only those in a scale.
A scale is a RollScale: the pitch classes it keeps and its root, each from 0 (C) to 11 (B). The roll knows no scale by name; rollScale gives a named one in that shape.
The Piano roll draws this controller in React.