From 5f24c6e7bdabc18e67b91126a27903823e76027e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Bihan?= Date: Wed, 10 Jan 2024 18:25:04 +0100 Subject: [PATCH] using smplr player on web and mp3 on mobile --- front/components/Metronome.tsx | 4 +- front/components/Play/PartitionMagic.tsx | 76 +++++++++++++++++--- front/components/Play/PlayViewControlBar.tsx | 3 +- front/package.json | 1 + front/views/PlayView.tsx | 2 +- front/yarn.lock | 5 ++ 6 files changed, 75 insertions(+), 16 deletions(-) diff --git a/front/components/Metronome.tsx b/front/components/Metronome.tsx index 4ccf750..6d7ff33 100644 --- a/front/components/Metronome.tsx +++ b/front/components/Metronome.tsx @@ -63,7 +63,7 @@ export const MetronomeControls = ({ paused = false, bpm }: { paused?: boolean; b } onPress={() => setEnabled(!enabled)} /> - {/* - */} + ); diff --git a/front/components/Play/PartitionMagic.tsx b/front/components/Play/PartitionMagic.tsx index bf460f6..d0402f7 100644 --- a/front/components/Play/PartitionMagic.tsx +++ b/front/components/Play/PartitionMagic.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { View } from 'react-native'; +import { Platform, View } from 'react-native'; import API from '../../API'; import { useQuery } from '../../Queries'; import Animated, { useSharedValue, withTiming, Easing } from 'react-native-reanimated'; @@ -7,9 +7,10 @@ import { CursorInfoItem } from '../../models/SongCursorInfos'; import { Audio } from 'expo-av'; import { SvgContainer } from './SvgContainer'; import LoadingComponent from '../Loading'; +import { SplendidGrandPiano } from 'smplr'; -// note we are also using timestamp in a context export type ParitionMagicProps = { + timestamp: number; songID: number; shouldPlay: boolean; onEndReached: () => void; @@ -43,6 +44,7 @@ const getCursorToPlay = ( const transitionDuration = 50; const PartitionMagic = ({ + timestamp, songID, shouldPlay, onEndReached, @@ -57,6 +59,8 @@ const PartitionMagic = ({ const [isPartitionSvgLoaded, setIsPartitionSvgLoaded] = React.useState(false); const partitionOffset = useSharedValue(0); const melodySound = React.useRef(null); + const piano = React.useRef(null); + const [isPianoLoaded, setIsPianoLoaded] = React.useState(false); const cursorPaddingVertical = 10; const cursorPaddingHorizontal = 3; @@ -75,12 +79,21 @@ const PartitionMagic = ({ } React.useEffect(() => { - if (!melodySound.current) { - Audio.Sound.createAsync({ - uri: API.getPartitionMelodyUrl(songID), - }, { - progressUpdateIntervalMillis: 200, - }).then((track) => { + if (Platform.OS === 'web' && !piano.current) { + const audio = new AudioContext(); + piano.current = new SplendidGrandPiano(audio); + piano.current.load.then(() => { + setIsPianoLoaded(true); + }); + } else if (!melodySound.current) { + Audio.Sound.createAsync( + { + uri: API.getPartitionMelodyUrl(songID), + }, + { + progressUpdateIntervalMillis: 200, + } + ).then((track) => { melodySound.current = track.sound; }); } @@ -89,6 +102,11 @@ const PartitionMagic = ({ melodySound.current.pauseAsync(); melodySound.current.unloadAsync(); } + if (piano.current) { + piano.current.stop(); + piano.current.context.close(); + piano.current = null; + } }; }, []); const partitionDims = React.useMemo<[number, number]>(() => { @@ -103,12 +121,19 @@ const PartitionMagic = ({ }, [onError, isError]); React.useEffect(() => { - if (isPartitionSvgLoaded && !isLoading && melodySound.current?._loaded) { + if (isPartitionSvgLoaded && !isLoading && (melodySound.current?._loaded || isPianoLoaded)) { onReady(); } - }, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded]); + }, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded, isPianoLoaded]); React.useEffect(() => { + if (Platform.OS === 'web') { + if (!piano.current || !isPianoLoaded) { + return; + } + shouldPlay ? onPlay() : onPause(); + return; + } if (!melodySound.current || !melodySound.current._loaded) { return; } @@ -116,6 +141,7 @@ const PartitionMagic = ({ melodySound.current.getStatusAsync().then((status) => { const lastCur = data!.cursors[data!.cursors.length - 1]!; const maxTs = lastCur.timestamp + lastCur.timing; + //@ts-expect-error error in the type const newRate = status.durationMillis! / maxTs; console.log('newRate', newRate); if (newRate < 0 || newRate > 2) { @@ -140,7 +166,7 @@ const PartitionMagic = ({ React.useEffect(() => { if (!melodySound.current || !melodySound.current._loaded) return; - if (data?.cursors.length === 0) return; + if (!data || data?.cursors.length === 0) return; melodySound.current.setOnPlaybackStatusUpdate((status) => { //@ts-expect-error positionMillis is not in the type @@ -164,6 +190,34 @@ const PartitionMagic = ({ }); }, [data?.cursors, melodySound.current?._loaded]); + React.useEffect(() => { + if (!shouldPlay) return; + if (!piano.current || !isPianoLoaded) return; + if (!data || data?.cursors.length === 0) return; + getCursorToPlay( + data!.cursors, + currentCurIdx.current, + timestamp + transitionDuration, + (cursor, idx) => { + console.log('cursor', cursor); + currentCurIdx.current = idx; + partitionOffset.value = withTiming( + -(cursor.x - data!.cursors[0]!.x) / partitionDims[0], + { + duration: transitionDuration, + easing: Easing.inOut(Easing.ease), + } + ); + cursor.notes.forEach((note) => { + piano.current?.start({ + note: note.note, + duration: note.duration, + }); + }); + } + ); + }, [timestamp, data?.cursors, isPianoLoaded]); + return ( { > { setTimeout(() => { diff --git a/front/yarn.lock b/front/yarn.lock index 7f02311..a1276bb 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -10764,6 +10764,11 @@ slugify@^1.3.4: resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== +smplr@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/smplr/-/smplr-0.12.1.tgz#53895a6928c2a3093d72e639440ca4be0b480e54" + integrity sha512-GEHuuP2vCtgu7+HY2F0DEnIxB5JHfxsZt4lJd9i/zxiZz1eBHheiy9h5Th32XGkLGObm/MMCfVs8Fwz6N/BKMw== + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"