// Récupération des éléments const audioForward = document.getElementById('audio'); const reverseSrc = audioForward.getAttribute('data-reverse-src'); const audioReverse = new Audio(reverseSrc); const progress = document.getElementById('progress'); const currentTimeElem = document.getElementById('currentTime'); const durationElem = document.getElementById('duration'); const vinyl = document.getElementById('vinyl'); const btnForward = document.getElementById('forward'); const btnBackward = document.getElementById('backward'); const btnLoop = document.getElementById('loop'); let lastPosition = 0; let isPlaying = false; let currentDirection = 1; // 1 = forward, -1 = reverse // Variables pour arrêter le son si pas de mouvement let stopTimeout = null; const STOP_DELAY = 1000; // ms sans mouvement avant arrêt // Une fois les métadonnées chargées, on met la durée audioForward.addEventListener('loadedmetadata', () => { durationElem.textContent = formatTime(audioForward.duration); progress.max = 100; }); // Formatage temps en mm:ss function formatTime(seconds) { const m = Math.floor(seconds / 60); const s = Math.floor(seconds % 60); return `${m}:${s < 10 ? '0' + s : s}`; } // Fonction pour arrêter les audios function stopAudio() { audioForward.pause(); audioReverse.pause(); isPlaying = false; vinyl.style.animationPlayState = 'paused'; } // Met à jour la barre de progression et le timer courant function updateProgress(currentTime, duration) { const percent = (currentTime / duration) * 100; progress.value = percent; currentTimeElem.textContent = formatTime(currentTime); } // Fonction pour synchroniser la position de reverse sur la base de forward function syncReversePosition() { audioReverse.currentTime = audioForward.duration - audioForward.currentTime; } // Fonction pour synchroniser la position de forward sur la base de reverse function syncForwardPosition() { audioForward.currentTime = audioReverse.duration - audioReverse.currentTime; } // Gestion du slider pour contrôle manuel progress.addEventListener('input', () => { const seekTime = (progress.value / 100) * audioForward.duration; audioForward.currentTime = seekTime; audioReverse.currentTime = audioForward.duration - seekTime; // Si pause, on reste en pause if (!isPlaying) { updateProgress(seekTime, audioForward.duration); } }); // Fonction principale pour gérer la lecture en fonction de la position de l'encodeur function handlePosition(position) { const speedFactor = 0.1; // ajuster la vitesse de lecture ici const delta = position - lastPosition; // Si pas de mouvement détecté (delta petit), on stoppe après un délai if (Math.abs(delta) < 0.01) { if (stopTimeout === null) { stopTimeout = setTimeout(() => { stopAudio(); stopTimeout = null; }, STOP_DELAY); } return; } else { // Mouvement détecté => clear timeout arrêt if (stopTimeout !== null) { clearTimeout(stopTimeout); stopTimeout = null; } } // Calcul vitesse lecture let playbackRate = Math.min(Math.abs(delta) * speedFactor, 2); // max 2x vitesse if (delta > 0) { // Lecture forward if (currentDirection !== 1) { // Switch direction: garder la position audioForward.currentTime = audioReverse.duration - audioReverse.currentTime; audioReverse.pause(); currentDirection = 1; } if (audioForward.paused) audioForward.play(); audioForward.playbackRate = playbackRate; audioReverse.pause(); // Avance le temps audioForward.currentTime += delta * speedFactor; // Clamp currentTime if (audioForward.currentTime > audioForward.duration) { audioForward.currentTime = audioForward.duration; stopAudio(); } updateProgress(audioForward.currentTime, audioForward.duration); } else if (delta < 0) { // Lecture reverse if (currentDirection !== -1) { // Switch direction: garder la position audioReverse.currentTime = audioForward.duration - audioForward.currentTime; audioForward.pause(); currentDirection = -1; } if (audioReverse.paused) audioReverse.play(); audioReverse.playbackRate = playbackRate; audioForward.pause(); // Recul temps (en tenant compte que currentTime augmente dans les deux) audioReverse.currentTime += -delta * speedFactor; // Clamp currentTime if (audioReverse.currentTime > audioReverse.duration) { audioReverse.currentTime = audioReverse.duration; stopAudio(); } // Mise à jour progress en miroir const forwardTime = audioReverse.duration - audioReverse.currentTime; updateProgress(forwardTime, audioReverse.duration); } // Animation vinyle if (!isPlaying) { vinyl.style.animationPlayState = 'running'; isPlaying = true; } lastPosition = position; } // Gestion boutons +10s, -10s btnForward.addEventListener('click', () => { if (currentDirection === 1) { audioForward.currentTime = Math.min(audioForward.duration, audioForward.currentTime + 10); updateProgress(audioForward.currentTime, audioForward.duration); syncReversePosition(); } else { audioReverse.currentTime = Math.min(audioReverse.duration, audioReverse.currentTime + 10); updateProgress(audioReverse.duration - audioReverse.currentTime, audioReverse.duration); syncForwardPosition(); } if (!isPlaying) { if (currentDirection === 1) audioForward.play(); else audioReverse.play(); vinyl.style.animationPlayState = 'running'; isPlaying = true; } }); btnBackward.addEventListener('click', () => { if (currentDirection === 1) { audioForward.currentTime = Math.max(0, audioForward.currentTime - 10); updateProgress(audioForward.currentTime, audioForward.duration); syncReversePosition(); } else { audioReverse.currentTime = Math.max(0, audioReverse.currentTime - 10); updateProgress(audioReverse.duration - audioReverse.currentTime, audioReverse.duration); syncForwardPosition(); } if (!isPlaying) { if (currentDirection === 1) audioForward.play(); else audioReverse.play(); vinyl.style.animationPlayState = 'running'; isPlaying = true; } }); // Toggle loop btnLoop.addEventListener('click', () => { const newLoop = !audioForward.loop; audioForward.loop = newLoop; audioReverse.loop = newLoop; btnLoop.textContent = newLoop ? 'LOOP ON' : 'LOOP'; }); // Exemple : simulateur de position venant de l'encodeur (juste pour test) // Remplace cette fonction par ta vraie récupération de la position de l'encodeur let testPosition = 0; setInterval(() => { // Simule un mouvement avec des valeurs entre -20 et 20 // Ici, rien ne bouge (position stable) pour test arrêt handlePosition(testPosition); }, 100); // Tu peux appeler handlePosition(positionEncoder) quand tu as la position réelle