diff --git a/src/screens/Playback/Playback.js b/src/screens/Playback/Playback.js index bc305ce..75368f8 100644 --- a/src/screens/Playback/Playback.js +++ b/src/screens/Playback/Playback.js @@ -15,24 +15,11 @@ const Playback = ({ route }) => { const { project } = route.params; const projectId = project?.id; const songIndex = project?.songIndex; - console.log("sonindex", songIndex); const sunoTaskId = project?.sunoTaskId; const { setIsLoading } = useMinuit(); - // console.log("musicTimestamps avec songIndex:", { - // // Accès avec la clé dot notation - // musicTimeStamps: project?.[`musicTimestamps.${songIndex}`], - // // Toutes les clés qui commencent par musicTimestamps - // }); + const onPressRecord = useCallback(async () => { try { - console.log( - "project", - songIndex, - "project id", - projectId, - "sunoTaskId", - sunoTaskId - ); if (!projectId || songIndex === undefined || !sunoTaskId) { return; // return navigate(Routes.RecordPlayback, { project }); diff --git a/src/screens/Playback/RecordPlayback.js b/src/screens/Playback/RecordPlayback.js index 83af899..3745d99 100644 --- a/src/screens/Playback/RecordPlayback.js +++ b/src/screens/Playback/RecordPlayback.js @@ -1,7 +1,13 @@ import { useFocusEffect } from "@react-navigation/native"; import { useAudioPlayer } from "expo-audio"; import { CameraView, useCameraPermissions } from "expo-camera"; -import React, { useCallback, useEffect, useRef, useState } from "react"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { Text, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Svg, { Circle } from "react-native-svg"; @@ -14,20 +20,13 @@ import { gutters, Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader"; -/** - * RecordPlayback — refactor robuste du compteur - * - * Correction de l'auto-start : on garde un flag countdownActiveRef pour - * empêcher l'effet de se déclencher tant que le timer n'a pas réellement démarré. - * On affiche 5→1 puis on bascule (pas de 0 visible) pour éviter le "bloqué sur 1". - */ - const TIME_BEFORE_INCREMENT_MS = 20000; // 20s const RecordPlayback = ({ route }) => { const { top, bottom } = useSafeAreaInsets(); const { project } = route.params || {}; - + const projectId = project?.id; + const songIndex = project?.songIndex; // Permissions const [cameraPermission, requestCameraPermission] = useCameraPermissions(); @@ -55,6 +54,22 @@ const RecordPlayback = ({ route }) => { const songUrl = project?.songUrl || null; const player = useAudioPlayer(songUrl ? { uri: songUrl } : undefined); + const musicIndex = useMemo(() => { + const i = Number(songIndex); + return Number.isFinite(i) && i >= 0 ? i : 0; + }, [songIndex]); + + const alignedWords = useMemo(() => { + const ts = project?.[`musicTimestamps.${songIndex}`]; + null; + const arr = Array.isArray(ts?.alignedWords) ? ts.alignedWords : []; + return arr.map((w) => ({ + word: String(w?.word ?? "").replace(/\n/g, " "), + startS: Number(w?.startS ?? 0), + endS: Number(w?.endS ?? 0), + })); + }, [project?.musicTimestamps, musicIndex]); + // console.log("alignedWords:", alignedWords); useEffect(() => { listenedMsRef.current = 0; incrementDoneRef.current = false; @@ -73,6 +88,19 @@ const RecordPlayback = ({ route }) => { return () => clearInterval(id); }, [player]); + // Compute current word index from playback time + const currentTimeS = (progressInfo?.pos || 0) / 1000; + const currentIdx = useMemo(() => { + if (!alignedWords || alignedWords.length === 0) return -1; + for (let i = 0; i < alignedWords.length; i++) { + const w = alignedWords[i]; + if (currentTimeS >= w.startS && currentTimeS <= w.endS) return i; + } + if (currentTimeS > (alignedWords[alignedWords.length - 1]?.endS || 0)) + return alignedWords.length - 1; + return -1; + }, [alignedWords, currentTimeS]); + // Permissions au mount + cleanup useEffect(() => { (async () => { @@ -408,24 +436,25 @@ const RecordPlayback = ({ route }) => { position: "absolute", left: 0, right: 0, - bottom: bottom + 100, + bottom: bottom + 130, paddingHorizontal: gutters, }} > - - - L'enregistrement de ta vidéo commencera lorsque tu lanceras ta - musique, et s'arrêtera à la fin du morceau. - + + {(isRecording || showProgress) && alignedWords?.length > 0 ? ( + + ) : ( + + L'enregistrement de ta vidéo commencera lorsque tu lanceras ta + musique, et s'arrêtera à la fin du morceau. + + )} @@ -464,3 +493,43 @@ const ProgressRing = ({ size = 50, strokeWidth = 12, progress = 0 }) => { }; export default RecordPlayback; + +// Simple karaoke line renderer +const KaraokeLine = ({ words = [], currentIdx = -1 }) => { + const windowSize = 8; + const start = Math.max(0, currentIdx - 2); + const end = Math.min( + words.length, + currentIdx >= 0 ? currentIdx + windowSize : windowSize + ); + const slice = words.slice(start, end); + return ( + + {slice.map((w, i) => { + const idx = start + i; + const text = String(w.word || "").replace(/\s+/g, " "); + const isCurrent = idx === currentIdx; + return ( + + {text + " "} + + ); + })} + + ); +};