Added a loading dispaly for the play button and removed onProgressUpdate from expo av and reusing the timestamp to advance the cursor despite perf issues

This commit is contained in:
Clément Le Bihan
2024-01-13 18:38:15 +01:00
parent f5136ae59b
commit aae0bbea3a
2 changed files with 35 additions and 54 deletions

View File

@@ -34,6 +34,7 @@ const getCursorToPlay = (
const cursorInfo = cursorInfos[i]!; const cursorInfo = cursorInfos[i]!;
if (cursorInfo.timestamp <= timestamp) { if (cursorInfo.timestamp <= timestamp) {
onCursorMove(cursorInfo, i); onCursorMove(cursorInfo, i);
return;
} }
} }
}; };
@@ -63,7 +64,7 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
const piano = React.useRef<SplendidGrandPiano | null>(null); const piano = React.useRef<SplendidGrandPiano | null>(null);
const [isPianoLoaded, setIsPianoLoaded] = React.useState(false); const [isPianoLoaded, setIsPianoLoaded] = React.useState(false);
const [timestamp, setTimestamp] = useAtom(timestampAtom); const [timestamp, setTimestamp] = useAtom(timestampAtom);
const shouldPlay = useAtom(shouldPlayAtom)[0]; const [shouldPlay, setShouldPlay] = useAtom(shouldPlayAtom);
const [partitionState, setPartitionState] = useAtom(partitionStateAtom); const [partitionState, setPartitionState] = useAtom(partitionStateAtom);
const cursorPaddingVertical = 10; const cursorPaddingVertical = 10;
const cursorPaddingHorizontal = 3; const cursorPaddingHorizontal = 3;
@@ -76,7 +77,7 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
const cursorTop = (data?.cursors[cursorDisplayIdx]?.y ?? 0) - cursorPaddingVertical; const cursorTop = (data?.cursors[cursorDisplayIdx]?.y ?? 0) - cursorPaddingVertical;
const cursorLeft = (data?.cursors[0]?.x ?? 0) - cursorPaddingHorizontal; const cursorLeft = (data?.cursors[0]?.x ?? 0) - cursorPaddingHorizontal;
console.log('state', partitionState); console.log('state', partitionState, timestamp);
if (!endPartitionReached && currentCurIdx.current + 1 === data?.cursors.length) { if (!endPartitionReached && currentCurIdx.current + 1 === data?.cursors.length) {
// weird contraption but the mobile don't want classic functions to be called // weird contraption but the mobile don't want classic functions to be called
@@ -101,6 +102,8 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
return () => { return () => {
setPartitionState('loading'); setPartitionState('loading');
setTimestamp(0);
setShouldPlay(false);
}; };
}, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded, isPianoLoaded]); }, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded, isPianoLoaded]);
@@ -112,14 +115,9 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
setIsPianoLoaded(true); setIsPianoLoaded(true);
}); });
} else if (!melodySound.current) { } else if (!melodySound.current) {
Audio.Sound.createAsync( Audio.Sound.createAsync({
{ uri: API.getPartitionMelodyUrl(songID),
uri: API.getPartitionMelodyUrl(songID), }).then((track) => {
},
{
progressUpdateIntervalMillis: 200,
}
).then((track) => {
melodySound.current = track.sound; melodySound.current = track.sound;
}); });
} }
@@ -141,9 +139,13 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
}, [data]); }, [data]);
React.useEffect(() => { React.useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(
setTimestamp(stopwatch.getElapsedRunningTime()); () => {
}, 200); // if (partitionState !== 'playing') return;
setTimestamp(stopwatch.getElapsedRunningTime());
},
Platform.OS === 'web' ? 200 : 500
);
return () => { return () => {
clearInterval(interval); clearInterval(interval);
}; };
@@ -152,13 +154,13 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
React.useEffect(() => { React.useEffect(() => {
if (Platform.OS === 'web') { if (Platform.OS === 'web') {
if (!piano.current || !isPianoLoaded) return; if (!piano.current || !isPianoLoaded) return;
startResumePauseWatch(stopwatch, shouldPlay);
setPartitionState(shouldPlay ? 'playing' : 'paused'); setPartitionState(shouldPlay ? 'playing' : 'paused');
startResumePauseWatch(stopwatch, shouldPlay);
return; return;
} }
if (!melodySound.current || !melodySound.current._loaded) return; if (!melodySound.current || !melodySound.current._loaded) return;
startResumePauseWatch(stopwatch, shouldPlay);
setPartitionState(shouldPlay ? 'playing' : 'paused'); setPartitionState(shouldPlay ? 'playing' : 'paused');
startResumePauseWatch(stopwatch, shouldPlay);
if (shouldPlay) { if (shouldPlay) {
melodySound.current.playAsync().catch(console.error); melodySound.current.playAsync().catch(console.error);
} else { } else {
@@ -166,34 +168,9 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
} }
}, [shouldPlay]); }, [shouldPlay]);
React.useEffect(() => {
if (!melodySound.current || !melodySound.current._loaded) return;
if (!data || data?.cursors.length === 0) return;
melodySound.current.setOnPlaybackStatusUpdate((status) => {
//@ts-expect-error positionMillis is not in the type
const timestamp = status?.positionMillis ?? 0;
getCursorToPlay(
data!.cursors,
currentCurIdx.current,
timestamp + transitionDuration,
(cursor, idx) => {
currentCurIdx.current = idx;
partitionOffset.value = withTiming(
-(cursor.x - data!.cursors[0]!.x) / partitionDims[0],
{
duration: transitionDuration,
easing: Easing.inOut(Easing.ease),
}
);
}
);
});
}, [data?.cursors, melodySound.current?._loaded]);
React.useEffect(() => { React.useEffect(() => {
if (!shouldPlay) return; if (!shouldPlay) return;
if (!piano.current || !isPianoLoaded) return; if (!melodySound.current || !melodySound.current._loaded) return;
if (!data || data?.cursors.length === 0) return; if (!data || data?.cursors.length === 0) return;
getCursorToPlay( getCursorToPlay(
data!.cursors, data!.cursors,
@@ -208,6 +185,7 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
easing: Easing.inOut(Easing.ease), easing: Easing.inOut(Easing.ease),
} }
); );
if (!piano.current || !isPianoLoaded) return;
cursor.notes.forEach((note) => { cursor.notes.forEach((note) => {
piano.current?.start({ piano.current?.start({
note: note.note, note: note.note,

View File

@@ -1,6 +1,6 @@
import { View } from 'react-native'; import { View } from 'react-native';
import * as React from 'react'; import * as React from 'react';
import { Row, Image, Text, useBreakpointValue, IconButton } from 'native-base'; import { Row, Image, Text, useBreakpointValue, IconButton, Button } from 'native-base';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { MetronomeControls } from '../Metronome'; import { MetronomeControls } from '../Metronome';
import StarProgress from '../StarProgress'; import StarProgress from '../StarProgress';
@@ -26,7 +26,7 @@ const PlayViewControlBar = ({ song }: PlayViewControlBarProps) => {
const { colors } = useTheme(); const { colors } = useTheme();
const textColor = colors.text; const textColor = colors.text;
const isPlaying = partitionState === 'playing'; const isPlaying = partitionState === 'playing';
const disabled = partitionState === 'loading' || partitionState === 'error'; const isPartitionLoading = partitionState === 'loading';
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
@@ -109,17 +109,20 @@ const PlayViewControlBar = ({ song }: PlayViewControlBarProps) => {
gap: isPhone ? 10 : 25, gap: isPhone ? 10 : 25,
}} }}
> >
<IconButton {isPartitionLoading ? (
size="sm" <Button isLoading size="sm" variant="solid" color={colors.coolGray[900]} />
variant="solid" ) : (
disabled={disabled} <IconButton
_icon={{ size="sm"
as: Ionicons, variant="solid"
color: colors.coolGray[900], _icon={{
name: isPlaying ? 'pause' : 'play', as: Ionicons,
}} color: colors.coolGray[900],
onPress={() => setShouldPlay(!isPlaying)} name: isPlaying ? 'pause' : 'play',
/> }}
onPress={() => setShouldPlay(!isPlaying)}
/>
)}
<IconButton <IconButton
size="sm" size="sm"
colorScheme="coolGray" colorScheme="coolGray"