in src/hooks/use-magenta-player.ts [50:183]
export default function useMagentaPlayer({
load = true,
url = "https://storage.googleapis.com/magentadata/js/soundfonts/sgm_plus",
notes,
loop = true,
bpm = defaultBpm,
}: Partial<Props> = {}): Player {
const [player, setPlayer] = useState<mm.SoundFontPlayer>();
const [state, setState] = useState(State.Stopped);
const [atEnd, setAtEnd] = useState(false);
const [currentPlayTime, setCurrentPlayTime] = useState(0);
const [activeNote, setActiveNote] = useState<mm.NoteSequence.INote>();
const playNotes = useCallback(() => {
if (!player || !notes) return;
setState(State.Started);
setAtEnd(false);
player.start(notes, 120, currentPlayTime).then(() => {
setAtEnd(true);
setCurrentPlayTime(0);
setState(State.Stopped);
});
}, [currentPlayTime, notes, player]);
const start = useCallback(() => {
if (!player) return;
if (state === State.Paused) {
player.resume();
setState(State.Started);
} else if (state === State.Stopped) {
playNotes();
}
}, [playNotes, player, state]);
const pause = useCallback(() => {
if (state === State.Started) {
player?.pause();
setState(State.Paused);
}
}, [player, state]);
const stop = useCallback(() => {
setCurrentPlayTime(0);
player?.stop();
setState(State.Stopped);
}, [player]);
const playNoteUp = useCallback(
(note: mm.NoteSequence.INote) => {
player?.playNoteUp(note);
},
[player]
);
const playNoteDown = useCallback(
(note: mm.NoteSequence.INote) => {
player?.playNoteDown(note);
},
[player]
);
useEffect(() => {
if (!load) return;
const promise = import("@magenta/music/esm/core/player").then(
async (mmp) => {
const player = new mmp.SoundFontPlayer(
url,
undefined,
undefined,
undefined,
{
run: (n) => {
if (n.startTime) setCurrentPlayTime(n.startTime);
setActiveNote(n);
},
stop: () => {
if (!player) return;
},
}
);
const notes: mm.NoteSequence.INote[] = [];
for (let i = lowestMidiNote; i <= highestMidiNote; i++) {
notes.push({ pitch: i });
}
await player.loadSamples({ notes });
setPlayer(player);
return player;
}
);
return () => {
promise.then((p) => {
p.stop();
setState(State.Stopped);
});
};
}, [load, url]);
useEffect(() => {
if (atEnd && state === State.Started) {
if (loop) {
playNotes();
} else {
setState(State.Stopped);
}
}
}, [loop, playNotes, atEnd, state]);
useEffect(() => {
// The tempo control of the player is kinda broken so let's tweak the
// underlying Tone.js...
// We do this at every rendering because it's pretty lightweight and
// changing it right after player.start() doesn't work.
if (bpm) Tone.Transport.bpm.value = bpm;
});
useEffect(() => {
player?.stop();
setState(State.Stopped);
}, [notes, player]);
return {
start,
pause,
stop,
activeNote,
state,
atEnd,
playNoteDown,
playNoteUp,
ready: player !== undefined,
};
}