Lab

usePianoRollPlayback

Plays a piano roll's notes, which you own: play, pause and stop, a loop or once through, a metronome, seeking, and the notes the roll auditions, with a playhead for the timeline and a clock for the transport.

pnpm dlx shadcn@latest add @audio-ui/use-piano-roll-playback
import { DEFAULT_NOTE_ENVELOPE, meterOf, usePianoRollPlayback } from "@/hooks/use-piano-roll-playback";
Fileshooks/use-piano-roll-playback.ts
Depends ondrum-synthesis, use-transport-clock, use-synth-voices, envelope-ramp, web-audio
npm@audio-ui/core, @audio-ui/engine, @audio-ui/react
Used byblock-piano-roll, piano-roll-playback-demo
ExportKindWhat it is
DEFAULT_NOTE_ENVELOPEconstADSR-shaped, matching the Envelope primitive's own default.
MeterinterfaceWhere bars and beats fall, in steps, for a signature: 6/8 has beats of two steps and bars of twelve.
meterOffunctionThe meter of a clip bars long in tempo's signature.
UsePianoRollPlaybackOptionsinterface
usePianoRollPlaybackfunctionPlays a piano roll's notes: play on from where it stopped, stop back at the start, the loop or once through, and the metronome.

Usage

Audio UI

usePianoRollPlayback plays the notes you hold through a synth: play, pause and stop, a loop or once through, a metronome, and the notes the roll auditions.

import { useMasterBus } from "@/hooks/use-master-bus";
import { meterOf, usePianoRollPlayback } from "@/hooks/use-piano-roll-playback";
import { useSynthVoices } from "@/hooks/use-synth-voices";

const tempo = { beatsPerBar: 4, beatUnit: 4, bpm: 120 };
const meter = meterOf(tempo, 4); // four bars, in steps

const output = useMasterBus(0.7);
const voices = useSynthVoices({ destination: output });
const playback = usePianoRollPlayback({ meter, notes, tempo, voices });

<Timeline
  duration={meter.duration}
  onPositionChange={playback.seek}
  snapStep={1}
  source={playback.position}
>
  <PianoRoll
    activePitches={playback.playingPitches}
    onNoteOff={playback.onNoteOff}
    onNoteOn={playback.onNoteOn}
    onValueChange={setNotes}
    value={notes}
  >
    {/* … */}
  </PianoRoll>
</Timeline>;

The voices and the bus come from Synthesis and Output, and the clock under the hook from Clock and time. The hook plays the notes; it does not hold them, so an undo, a file import or an agent can change them and the next pass plays the change. Time is in steps, four to a quarter note whatever the signature; meterOf(tempo, bars) gives a clip's length in steps and where its bars and beats fall. Notes are scheduled ahead on the audio clock, so they keep time when the page is busy, and each plays as the roll draws it: a deactivated note stays silent, a chance plays that often, a deviation strays its velocity each time round.

It returns play (on from the playhead), pause (stops where it is), stop (back to the start of the loop), seek, isPlaying, playingPitches, onNoteOn and onNoteOff for the roll, position (in steps) for the timeline's source, and clock (in seconds) for a Transport's source. It also takes looping (on by default), a loopRegion of steps, whose commitLoopRegion makes a moved loop heard at once, metronome with the destination it clicks into, and the envelope the notes play with.

Built by Ouest Labs, the source code is available on GitHub.
RSS