Cursor is controlled by partition timestamps provided by playview and can thus be paused and onEndReached is now called
This commit is contained in:
@@ -31,7 +31,9 @@ const PartitionCoord = ({
|
|||||||
setPartitionData([base64data, a]);
|
setPartitionData([base64data, a]);
|
||||||
onPartitionReady();
|
onPartitionReady();
|
||||||
}}
|
}}
|
||||||
onEndReached={onEndReached}
|
onEndReached={() => {
|
||||||
|
console.log('osmd end reached');
|
||||||
|
}}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -39,6 +41,9 @@ const PartitionCoord = ({
|
|||||||
<PhaserCanvas
|
<PhaserCanvas
|
||||||
partitionB64={partitionData?.[0]}
|
partitionB64={partitionData?.[0]}
|
||||||
cursorPositions={partitionData?.[1]}
|
cursorPositions={partitionData?.[1]}
|
||||||
|
onEndReached={() => {
|
||||||
|
onEndReached();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const PartitionView = (props: PartitionViewProps) => {
|
|||||||
const [osmd, setOsmd] = useState<OSMD>();
|
const [osmd, setOsmd] = useState<OSMD>();
|
||||||
const [soundPlayer, setSoundPlayer] = useState<SoundFont.Player>();
|
const [soundPlayer, setSoundPlayer] = useState<SoundFont.Player>();
|
||||||
const audioContext = new SAC.AudioContext();
|
const audioContext = new SAC.AudioContext();
|
||||||
const [wholeNoteLength, setWholeNoteLength] = useState(0); // Length of Whole note, in ms (?)
|
// const [wholeNoteLength, setWholeNoteLength] = useState(0); // Length of Whole note, in ms (?)
|
||||||
const colorScheme = useColorScheme();
|
const colorScheme = useColorScheme();
|
||||||
const dimensions = useWindowDimensions();
|
const dimensions = useWindowDimensions();
|
||||||
const OSMD_DIV_ID = 'osmd-div';
|
const OSMD_DIV_ID = 'osmd-div';
|
||||||
@@ -45,15 +45,15 @@ const PartitionView = (props: PartitionViewProps) => {
|
|||||||
autoResize: false,
|
autoResize: false,
|
||||||
};
|
};
|
||||||
// Turns note.Length or timestamp in ms
|
// Turns note.Length or timestamp in ms
|
||||||
const timestampToMs = (timestamp: Fraction) => {
|
const timestampToMs = (timestamp: Fraction, wholeNoteLength: number) => {
|
||||||
return timestamp.RealValue * wholeNoteLength;
|
return timestamp.RealValue * wholeNoteLength;
|
||||||
};
|
};
|
||||||
const getActualNoteLength = (note: Note) => {
|
const getActualNoteLength = (note: Note, wholeNoteLength: number) => {
|
||||||
let duration = timestampToMs(note.Length);
|
let duration = timestampToMs(note.Length, wholeNoteLength);
|
||||||
if (note.NoteTie) {
|
if (note.NoteTie) {
|
||||||
const firstNote = note.NoteTie.Notes.at(1);
|
const firstNote = note.NoteTie.Notes.at(1);
|
||||||
if (Object.is(note.NoteTie.StartNote, note) && firstNote) {
|
if (Object.is(note.NoteTie.StartNote, note) && firstNote) {
|
||||||
duration += timestampToMs(firstNote.Length);
|
duration += timestampToMs(firstNote.Length, wholeNoteLength);
|
||||||
} else {
|
} else {
|
||||||
duration = 0;
|
duration = 0;
|
||||||
}
|
}
|
||||||
@@ -97,9 +97,38 @@ const PartitionView = (props: PartitionViewProps) => {
|
|||||||
_osmd.render();
|
_osmd.render();
|
||||||
_osmd.cursor.show();
|
_osmd.cursor.show();
|
||||||
// get the current cursor position
|
// 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 = [];
|
const curPos = [];
|
||||||
while (!_osmd.cursor.iterator.EndReached) {
|
while (!_osmd.cursor.iterator.EndReached) {
|
||||||
curPos.push(_osmd.cursor.cursorElement.offsetLeft);
|
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();
|
_osmd.cursor.next();
|
||||||
}
|
}
|
||||||
console.log('curPos', curPos);
|
console.log('curPos', curPos);
|
||||||
@@ -110,14 +139,14 @@ const PartitionView = (props: PartitionViewProps) => {
|
|||||||
console.log('current measure index', _osmd.cursor.iterator.CurrentMeasureIndex);
|
console.log('current measure index', _osmd.cursor.iterator.CurrentMeasureIndex);
|
||||||
const osmdCanvas = document.querySelector('#' + OSMD_DIV_ID + ' canvas');
|
const osmdCanvas = document.querySelector('#' + OSMD_DIV_ID + ' canvas');
|
||||||
// Ty https://github.com/jimutt/osmd-audio-player/blob/ec205a6e46ee50002c1fa8f5999389447bba7bbf/src/PlaybackEngine.ts#LL77C12-L77C63
|
// Ty https://github.com/jimutt/osmd-audio-player/blob/ec205a6e46ee50002c1fa8f5999389447bba7bbf/src/PlaybackEngine.ts#LL77C12-L77C63
|
||||||
const bpm = _osmd.Sheet.HasBPMInfo ? _osmd.Sheet.getExpressionsStartTempoInBPM() : 60;
|
|
||||||
setWholeNoteLength(Math.round((60 / bpm) * 4000));
|
|
||||||
props.onPartitionReady(
|
props.onPartitionReady(
|
||||||
osmdCanvas.toDataURL(),
|
osmdCanvas.toDataURL(),
|
||||||
curPos.map((pos) => {
|
curPos.map((pos) => {
|
||||||
return {
|
return {
|
||||||
x: pos,
|
x: pos.offset,
|
||||||
timing: Math.floor(Math.random() * 600) + 100,
|
timing: pos.sNinfos.sNL,
|
||||||
|
timestamp: pos.sNinfos.ts,
|
||||||
|
notes: pos.notes,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+17
-51
@@ -21,7 +21,6 @@ import { transformQuery, useQuery } from '../Queries';
|
|||||||
import API from '../API';
|
import API from '../API';
|
||||||
import LoadingComponent, { LoadingView } from '../components/Loading';
|
import LoadingComponent, { LoadingView } from '../components/Loading';
|
||||||
import Constants from 'expo-constants';
|
import Constants from 'expo-constants';
|
||||||
import VirtualPiano from '../components/VirtualPiano/VirtualPiano';
|
|
||||||
import { strToKey, keyToStr, Note } from '../models/Piano';
|
import { strToKey, keyToStr, Note } from '../models/Piano';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { RootState } from '../state/Store';
|
import { RootState } from '../state/Store';
|
||||||
@@ -70,6 +69,14 @@ function parseMidiMessage(message: MIDIMessageEvent) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PartitionContext = React.createContext<{
|
||||||
|
// Timestamp of the play session, in milisecond
|
||||||
|
timestamp: number;
|
||||||
|
}>({
|
||||||
|
timestamp: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
||||||
const accessToken = useSelector((state: RootState) => state.user.accessToken);
|
const accessToken = useSelector((state: RootState) => state.user.accessToken);
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
@@ -79,7 +86,6 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
const webSocket = useRef<WebSocket>();
|
const webSocket = useRef<WebSocket>();
|
||||||
const [paused, setPause] = useState<boolean>(true);
|
const [paused, setPause] = useState<boolean>(true);
|
||||||
const stopwatch = useStopwatch();
|
const stopwatch = useStopwatch();
|
||||||
const [isVirtualPianoVisible, setVirtualPianoVisible] = useState<boolean>(false);
|
|
||||||
const [time, setTime] = useState(0);
|
const [time, setTime] = useState(0);
|
||||||
const [partitionRendered, setPartitionRendered] = useState(false); // Used to know when partitionview can render
|
const [partitionRendered, setPartitionRendered] = useState(false); // Used to know when partitionview can render
|
||||||
const [score, setScore] = useState(0); // Between 0 and 100
|
const [score, setScore] = useState(0); // Between 0 and 100
|
||||||
@@ -218,7 +224,7 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE).catch(() => {});
|
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE).catch(() => {});
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setTime(() => getElapsedTime()); // Countdown
|
setTime(() =>getElapsedTime()); // Countdown
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -253,6 +259,11 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flexGrow: 1, flexDirection: 'column' }}>
|
<SafeAreaView style={{ flexGrow: 1, flexDirection: 'column' }}>
|
||||||
|
<PartitionContext.Provider
|
||||||
|
value={{
|
||||||
|
timestamp: time,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<HStack
|
<HStack
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
@@ -279,7 +290,8 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
/> */}
|
/> */}
|
||||||
<PartitionCoord
|
<PartitionCoord
|
||||||
file={musixml.data}
|
file={musixml.data}
|
||||||
timestamp={Math.max(0, time)}
|
// timestamp={Math.max(0, time)}
|
||||||
|
timestamp={0}
|
||||||
onEndReached={() => {
|
onEndReached={() => {
|
||||||
onEnd();
|
onEnd();
|
||||||
}}
|
}}
|
||||||
@@ -288,39 +300,6 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
{!partitionRendered && <LoadingComponent />}
|
{!partitionRendered && <LoadingComponent />}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{isVirtualPianoVisible && (
|
|
||||||
<Column
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '20%',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VirtualPiano
|
|
||||||
onNoteDown={(note) => {
|
|
||||||
console.log('On note down', keyToStr(note));
|
|
||||||
}}
|
|
||||||
onNoteUp={(note) => {
|
|
||||||
console.log('On note up', keyToStr(note));
|
|
||||||
}}
|
|
||||||
showOctaveNumbers={true}
|
|
||||||
startNote={Note.C}
|
|
||||||
endNote={Note.B}
|
|
||||||
startOctave={2}
|
|
||||||
endOctave={5}
|
|
||||||
style={{
|
|
||||||
width: '80%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
highlightedNotes={[
|
|
||||||
{ key: strToKey('D3') },
|
|
||||||
{ key: strToKey('A#'), bgColor: '#00FF00' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Column>
|
|
||||||
)}
|
|
||||||
<Box
|
<Box
|
||||||
shadow={4}
|
shadow={4}
|
||||||
style={{
|
style={{
|
||||||
@@ -364,20 +343,6 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
|
||||||
size="sm"
|
|
||||||
colorScheme="coolGray"
|
|
||||||
variant="solid"
|
|
||||||
icon={
|
|
||||||
<Icon
|
|
||||||
as={MaterialCommunityIcons}
|
|
||||||
name={isVirtualPianoVisible ? 'piano-off' : 'piano'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
setVirtualPianoVisible(!isVirtualPianoVisible);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text>
|
<Text>
|
||||||
{time < 0
|
{time < 0
|
||||||
? paused
|
? paused
|
||||||
@@ -406,6 +371,7 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
|
|||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
</Box>
|
</Box>
|
||||||
|
</PartitionContext.Provider>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user