127 lines
4.2 KiB
TypeScript
127 lines
4.2 KiB
TypeScript
/* eslint-disable no-mixed-spaces-and-tabs */
|
|
// Inspired from OSMD example project
|
|
// https://github.com/opensheetmusicdisplay/react-opensheetmusicdisplay/blob/master/src/lib/OpenSheetMusicDisplay.jsx
|
|
import React, { useEffect } from 'react';
|
|
import {
|
|
CursorType,
|
|
Fraction,
|
|
OpenSheetMusicDisplay as OSMD,
|
|
IOSMDOptions,
|
|
Note,
|
|
} from 'opensheetmusicdisplay';
|
|
import useColorScheme from '../hooks/colorScheme';
|
|
import { PianoCursorPosition } from './PartitionVisualizer/PhaserCanvas';
|
|
|
|
type PartitionViewProps = {
|
|
// The Buffer of the MusicXML file retreived from the API
|
|
file: string;
|
|
onPartitionReady: (base64data: string, cursorInfos: PianoCursorPosition[]) => void;
|
|
onEndReached: () => void;
|
|
// Timestamp of the play session, in milisecond
|
|
timestamp: number;
|
|
};
|
|
|
|
const PartitionView = (props: PartitionViewProps) => {
|
|
const colorScheme = useColorScheme();
|
|
const OSMD_DIV_ID = 'osmd-div';
|
|
const options: IOSMDOptions = {
|
|
darkMode: colorScheme == 'dark',
|
|
backend: 'canvas',
|
|
drawComposer: false,
|
|
drawCredits: false,
|
|
drawLyrics: false,
|
|
drawPartNames: false,
|
|
followCursor: false,
|
|
renderSingleHorizontalStaffline: true,
|
|
cursorsOptions: [{ type: CursorType.Standard, color: 'green', alpha: 0.5, follow: false }],
|
|
autoResize: false,
|
|
};
|
|
// Turns note.Length or timestamp in ms
|
|
const timestampToMs = (timestamp: Fraction, wholeNoteLength: number) => {
|
|
return timestamp.RealValue * wholeNoteLength;
|
|
};
|
|
const getActualNoteLength = (note: Note, wholeNoteLength: number) => {
|
|
let duration = timestampToMs(note.Length, wholeNoteLength);
|
|
if (note.NoteTie) {
|
|
const firstNote = note.NoteTie.Notes.at(1);
|
|
if (Object.is(note.NoteTie.StartNote, note) && firstNote) {
|
|
duration += timestampToMs(firstNote.Length, wholeNoteLength);
|
|
} else {
|
|
duration = 0;
|
|
}
|
|
}
|
|
return duration;
|
|
};
|
|
|
|
useEffect(() => {
|
|
const _osmd = new OSMD(OSMD_DIV_ID, options);
|
|
Promise.all([
|
|
// SoundFont.instrument(audioContext as unknown as AudioContext, 'electric_piano_1'),
|
|
_osmd.load(props.file),
|
|
]).then(([player]) => {
|
|
// setSoundPlayer(player);
|
|
_osmd.render();
|
|
_osmd.cursor.show();
|
|
// get the current cursor position
|
|
const bpm = _osmd.Sheet.HasBPMInfo ? _osmd.Sheet.getExpressionsStartTempoInBPM() : 60;
|
|
// setWholeNoteLength(Math.round((60 / bpm) * 4000));
|
|
const wholeNoteLength = Math.round((60 / bpm) * 4000);
|
|
const curPos = [];
|
|
while (!_osmd.cursor.iterator.EndReached) {
|
|
const notesToPlay = _osmd.cursor
|
|
.NotesUnderCursor()
|
|
.filter((note) => {
|
|
return note.isRest() == false && note.Pitch;
|
|
})
|
|
.map((note) => {
|
|
return {
|
|
note: note,
|
|
duration: getActualNoteLength(note, wholeNoteLength),
|
|
};
|
|
});
|
|
const shortestNotes = _osmd!.cursor
|
|
.NotesUnderCursor()
|
|
.sort((n1, n2) => n1.Length.CompareTo(n2.Length))
|
|
.at(0);
|
|
const ts = timestampToMs(shortestNotes?.getAbsoluteTimestamp() ?? new Fraction(-1), wholeNoteLength);
|
|
const sNL = timestampToMs(shortestNotes?.Length ?? new Fraction(-1), wholeNoteLength);
|
|
curPos.push({
|
|
offset: _osmd.cursor.cursorElement.offsetLeft,
|
|
notes: notesToPlay,
|
|
shortedNotes: shortestNotes,
|
|
sNinfos: {
|
|
ts,
|
|
sNL,
|
|
isRest: shortestNotes?.isRest(),
|
|
}
|
|
});
|
|
_osmd.cursor.next();
|
|
}
|
|
// console.log('curPos', curPos);
|
|
_osmd.cursor.reset();
|
|
_osmd.cursor.hide();
|
|
// console.log('timestamp cursor', _osmd.cursor.iterator.CurrentSourceTimestamp);
|
|
// console.log('timestamp cursor', _osmd.cursor.iterator.CurrentVoiceEntries);
|
|
// console.log('current measure index', _osmd.cursor.iterator.CurrentMeasureIndex);
|
|
const osmdCanvas = document.querySelector('#' + OSMD_DIV_ID + ' canvas');
|
|
// Ty https://github.com/jimutt/osmd-audio-player/blob/ec205a6e46ee50002c1fa8f5999389447bba7bbf/src/PlaybackEngine.ts#LL77C12-L77C63
|
|
props.onPartitionReady(
|
|
osmdCanvas.toDataURL(),
|
|
curPos.map((pos) => {
|
|
return {
|
|
x: pos.offset,
|
|
timing: pos.sNinfos.sNL,
|
|
timestamp: pos.sNinfos.ts,
|
|
notes: pos.notes,
|
|
};
|
|
})
|
|
);
|
|
// Do not show cursor before actuall start
|
|
});
|
|
}, []);
|
|
|
|
return <div id={OSMD_DIV_ID} style={{ width: '100%', overflow: 'hidden' }} />;
|
|
};
|
|
|
|
export default PartitionView;
|