// 1. Sélection des éléments du DOM const vinyl = document.getElementById('vinyl'); const audioForward = document.getElementById('audio'); const progress = document.getElementById('progress'); const currentTimeEl = document.getElementById('currentTime'); const durationEl = document.getElementById('duration'); const backwardBtn = document.getElementById('backward'); const forwardBtn = document.getElementById('forward'); const loopBtn = document.getElementById('loop'); // 2. Variables utiles let isPlaying = false; let isLooping = false; // Création de l'élément audio pour le son en reverse const audioReverse = new Audio('muddy_files_reverse.mp3'); audioReverse.preload = 'auto'; // Durée et mise à jour progressive let duration = 0; audioForward.addEventListener('loadedmetadata', () => { duration = audioForward.duration; durationEl.textContent = formatTime(duration); }); audioReverse.addEventListener('loadedmetadata', () => { // en principe même durée que audioForward }); // socket.io & arduino serial info const socket = io(); // Timer d'inactivité pour arrêt du son let inactivityTimeout = null; const INACTIVITY_DELAY = 1500; // ms // 3. Fonctions function formatTime(time) { const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; } function updateProgress() { let currentTime = isPlaying ? (audioForward.paused ? audioReverse.currentTime : audioForward.currentTime) : 0; const progressPercent = (currentTime / duration) * 100; progress.value = progressPercent; currentTimeEl.textContent = formatTime(currentTime); } function setProgress(e) { const width = progress.clientWidth; const clickX = e.offsetX; const newTime = (clickX / width) * duration; audioForward.currentTime = newTime; audioReverse.currentTime = newTime; } function togglePlay() { if (isPlaying) { stopAudio(); } else { audioForward.play(); vinyl.style.animationPlayState = 'running'; isPlaying = true; } } function stopAudio() { audioForward.pause(); audioReverse.pause(); vinyl.style.animationPlayState = 'paused'; isPlaying = false; } // Met à jour l'animation du vinyle selon l'état function updateVinylAnimation(running) { vinyl.style.animationPlayState = running ? 'running' : 'paused'; } // Fonction principale pour contrôler la lecture selon la vitesse function controlPlayback(speedRaw) { const threshold = 0.02; // Applique un filtre de sensibilité (ex: puissance 1.5 pour réduire les petites valeurs) let speed = Math.sign(speedRaw) * Math.pow(Math.abs(speedRaw), 1.5); if (Math.abs(speed) > threshold) { // Reset du timer d'inactivité if (inactivityTimeout) clearTimeout(inactivityTimeout); if (!isPlaying) isPlaying = true; if (speed > 0) { // Forward if (!audioForward.paused) { // synchronise la position reverse avec forward audioReverse.pause(); } else { audioReverse.pause(); audioForward.currentTime = audioReverse.currentTime; // même position audioForward.play(); } audioForward.playbackRate = Math.min(2, Math.max(0.5, speed)); updateVinylAnimation(true); } else { // Reverse if (!audioReverse.paused) { audioForward.pause(); } else { audioForward.pause(); audioReverse.currentTime = audioForward.currentTime; // même position audioReverse.play(); } audioReverse.playbackRate = Math.min(2, Math.max(0.5, Math.abs(speed))); updateVinylAnimation(true); } // Lance un timer pour stopper la lecture si aucun mouvement détecté inactivityTimeout = setTimeout(() => { stopAudio(); }, INACTIVITY_DELAY); } else { // Si la vitesse est trop faible, on stoppe la lecture stopAudio(); } } // socket.io écoute la position et contrôle la vitesse socket.on('position', (position) => { // Ici tu adaptes le mapping de position -> vitesse entre 0.5 et 2 par exemple // Exemple simple, adapte selon ta plage réelle : // position brut supposé entre -1000 et 1000 const minPos = -1000; const maxPos = 1000; // Mappe la position entre -2 et 2 let mappedSpeed = ((position - minPos) / (maxPos - minPos)) * 4 - 2; // Par exemple pour réduire encore la sensibilité mappedSpeed = Math.max(-2, Math.min(2, mappedSpeed)); controlPlayback(mappedSpeed); // Mise à jour de la progress bar updateProgress(); }); // 4. Événements vinyl.addEventListener('click', togglePlay); audioForward.addEventListener('timeupdate', updateProgress); audioReverse.addEventListener('timeupdate', updateProgress); progress.addEventListener('click', setProgress); backwardBtn.addEventListener('click', () => { let newTime = Math.max(0, audioForward.currentTime - 10); audioForward.currentTime = newTime; audioReverse.currentTime = newTime; }); forwardBtn.addEventListener('click', () => { let newTime = Math.min(duration, audioForward.currentTime + 10); audioForward.currentTime = newTime; audioReverse.currentTime = newTime; }); loopBtn.addEventListener('click', () => { isLooping = !isLooping; loopBtn.style.backgroundColor = isLooping ? 'white' : 'transparent'; loopBtn.style.color = isLooping ? '#18344b' : 'white'; }); audioForward.addEventListener('ended', () => { if (isLooping) { audioForward.currentTime = 0; audioForward.play(); } else { updateVinylAnimation(false); isPlaying = false; } }); audioReverse.addEventListener('ended', () => { if (isLooping) { audioReverse.currentTime = 0; audioReverse.play(); } else { updateVinylAnimation(false); isPlaying = false; } });