pnpm dlx shadcn@latest add @audio-ui/use-transport-clock
import { useTransportClock } from "@/hooks/use-transport-clock";| Files | hooks/use-transport-clock.ts |
|---|---|
| Depends on | web-audio |
| npm | @audio-ui/engine |
| Used by | sequencer, use-piano-roll-playback, use-playback-clock, use-simulated-clock |
| Export | Kind | What it is |
|---|---|---|
TransportRegion | interface | |
UseTransportClockOptions | interface | |
UseTransportClockReturn | interface | |
useTransportClock | function | The region-aware {@link https://web.dev/audio-scheduling | look-ahead} transport clock, the seam between musical time (tempo, steps, loop region, seek) and Web Audio scheduling. |
Usage
A timer in JavaScript drifts and stalls when the page is busy or in the background; the audio clock does not. useTransportClock looks a little ahead on the audio clock and calls you back with the exact audio time of each step, so you schedule the sound at that time and it plays on the beat whatever the page is doing.
import { useTransportClock } from "@/hooks/use-transport-clock";
const clock = useTransportClock({
bpm: 120,
stepsPerBeat: 4, // a step is a sixteenth note
totalSteps: 64, // four bars
region: loop, // { start, end } in steps, or null for all of it
onStep: (step, time) => {
for (const note of notesAt(step)) {
voices.triggerNote(note.pitch, envelope, note.velocity, time);
}
},
onReset: () => voices.releaseAll(),
});
<Button onClick={clock.isPlaying ? clock.stop : clock.start}>Play</Button>;The clock knows when, never what: onStep decides what plays. onReset runs on every start, stop, seek and loop change, to drop what was already scheduled ahead. For a playhead, read getPosition() in an animation frame and push it to a source: it gives fractional steps, so the playhead glides between them.
The piano roll plays its notes with this clock through usePianoRollPlayback, which is a good example to copy.
Options · 6
time.Returns · 5
step now (the start of the region when left out): seeking, or hearing a moved loop at once. No-op while stopped.The scheduling itself is plain functions in @audio-ui/engine/time (advance, anchoredCursor, positionAt, SCHEDULE_AHEAD_SECONDS): the hook only connects them to React and the audio context, so an engine outside React uses them directly.