karaoke
This commit is contained in:
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
>
|
||||
<CreateLyricsHeader
|
||||
// disableBlur={Platform.OS !== "ios"}
|
||||
// containerStyle={{ backgroundColor: Palette.ultraLightBlack }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
}}
|
||||
>
|
||||
L'enregistrement de ta vidéo commencera lorsque tu lanceras ta
|
||||
musique, et s'arrêtera à la fin du morceau.
|
||||
</Text>
|
||||
<CreateLyricsHeader>
|
||||
{(isRecording || showProgress) && alignedWords?.length > 0 ? (
|
||||
<KaraokeLine words={alignedWords} currentIdx={currentIdx} />
|
||||
) : (
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
}}
|
||||
>
|
||||
L'enregistrement de ta vidéo commencera lorsque tu lanceras ta
|
||||
musique, et s'arrêtera à la fin du morceau.
|
||||
</Text>
|
||||
)}
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
</View>
|
||||
@@ -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 (
|
||||
<Text
|
||||
style={{
|
||||
color: Palette.white,
|
||||
fontSize: 18,
|
||||
lineHeight: 24,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{slice.map((w, i) => {
|
||||
const idx = start + i;
|
||||
const text = String(w.word || "").replace(/\s+/g, " ");
|
||||
const isCurrent = idx === currentIdx;
|
||||
return (
|
||||
<Text
|
||||
key={`${idx}-${text}`}
|
||||
style={{
|
||||
color: isCurrent ? Palette.white : "#FFFFFFAA",
|
||||
fontFamily: isCurrent
|
||||
? FONT_FAMILY.InterSemiBold
|
||||
: FONT_FAMILY.InterMedium,
|
||||
}}
|
||||
>
|
||||
{text + " "}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user