This commit is contained in:
El-yazide MOHAMED 2025-06-07 08:32:34 +02:00
parent 61f68ca41f
commit e957de4d86

View File

@ -1,7 +1,6 @@
// 1. Sélection des éléments du DOM // 1. Sélection des éléments du DOM
const vinyl = document.getElementById('vinyl'); const vinyl = document.getElementById('vinyl');
const audioForward = document.getElementById('audio-forward'); const audioForward = document.getElementById('audio');
const audioReverse = document.getElementById('audio-reverse');
const progress = document.getElementById('progress'); const progress = document.getElementById('progress');
const currentTimeEl = document.getElementById('currentTime'); const currentTimeEl = document.getElementById('currentTime');
const durationEl = document.getElementById('duration'); const durationEl = document.getElementById('duration');
@ -12,184 +11,175 @@ const loopBtn = document.getElementById('loop');
// 2. Variables utiles // 2. Variables utiles
let isPlaying = false; let isPlaying = false;
let isLooping = false; let isLooping = false;
let lastPosition = 0;
let stopTimeout = null; // 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 // socket.io & arduino serial info
const socket = io(); const socket = io();
const MIN_SPEED = 0.1; // seuil minimal pour commencer la lecture // Timer d'inactivité pour arrêt du son
const MAX_SPEED = 2; // vitesse max let inactivityTimeout = null;
const SENSITIVITY = 0.01; // ajuste la sensibilité du mapping const INACTIVITY_DELAY = 1500; // ms
// 3. Fonctions
// Fonction pour formater le temps (min:sec)
function formatTime(time) { function formatTime(time) {
const minutes = Math.floor(time / 60); const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60); const seconds = Math.floor(time % 60);
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
} }
// Mise à jour de la barre de progression à partir de la piste active
function updateProgress() { function updateProgress() {
let activeAudio = isPlaying ? (audioForward.paused ? audioReverse : audioForward) : null; let currentTime = isPlaying
if (activeAudio && activeAudio.duration) { ? (audioForward.paused ? audioReverse.currentTime : audioForward.currentTime)
const progressPercent = (activeAudio.currentTime / activeAudio.duration) * 100; : 0;
progress.value = progressPercent;
currentTimeEl.textContent = formatTime(activeAudio.currentTime); const progressPercent = (currentTime / duration) * 100;
durationEl.textContent = formatTime(activeAudio.duration); progress.value = progressPercent;
} currentTimeEl.textContent = formatTime(currentTime);
} }
// Met à jour lanimation du vinyle selon lecture en cours function setProgress(e) {
function updateVinylAnimation() { const width = progress.clientWidth;
if (!audioForward.paused || !audioReverse.paused) { const clickX = e.offsetX;
vinyl.style.animationPlayState = 'running'; const newTime = (clickX / width) * duration;
} else {
vinyl.style.animationPlayState = 'paused'; audioForward.currentTime = newTime;
} audioReverse.currentTime = newTime;
} }
function togglePlay() {
// Fonction pour contrôler la lecture en fonction de la vitesse (peut être négative)
function controlPlayback(speed) {
// Nettoyer lancien timeout darrêt
if (stopTimeout) clearTimeout(stopTimeout);
// Si vitesse proche de 0, on arrête la lecture après 1.5s dinactivité
if (Math.abs(speed) < MIN_SPEED) {
// Lance le timeout d'arrêt si plus de mouvement
stopTimeout = setTimeout(() => {
stopAudio();
}, 1500);
return;
}
// Ici, on a une vitesse suffisante => on arrête les deux audios d'abord
audioForward.pause();
audioReverse.pause();
// Calcule la vitesse normalisée (sensibilité appliquée)
let normalizedSpeed = Math.min(MAX_SPEED, Math.max(0.5, Math.abs(speed) * SENSITIVITY));
// On lit dans le bon sens
if (speed > 0) {
// Passage en lecture forward
// Si on était en reverse, on récupère la position dans reverse et on la convertit en forward
if (!audioForward.paused) {
// on continue normalement
} else {
// on synchronise la position forward avec la position inverse actuelle
audioForward.currentTime = audioReverse.duration - audioReverse.currentTime;
audioReverse.pause();
}
audioForward.playbackRate = normalizedSpeed;
audioForward.play();
isPlaying = true;
} else {
// Passage en lecture reverse
if (!audioReverse.paused) {
// on continue normalement
} else {
// on synchronise la position reverse avec la position forward actuelle
audioReverse.currentTime = audioForward.duration - audioForward.currentTime;
audioForward.pause();
}
audioReverse.playbackRate = normalizedSpeed;
audioReverse.play();
isPlaying = true;
}
updateVinylAnimation();
}
// Fonction pour arrêter les deux audios
function stopAudio() {
audioForward.pause();
audioReverse.pause();
isPlaying = false;
updateVinylAnimation();
}
// socket.io reçoit la position de lencodeur
socket.on('position', (position) => {
// Calcule la vitesse par rapport au dernier mouvement
let speed = position - lastPosition;
lastPosition = position;
controlPlayback(speed);
});
// 3. Événements généraux
// Clique sur le vinyle pour basculer lecture/pauses sur les deux audios
vinyl.addEventListener('click', () => {
if (isPlaying) { if (isPlaying) {
stopAudio(); stopAudio();
} else { } else {
audioForward.playbackRate = 1;
audioForward.play(); audioForward.play();
vinyl.style.animationPlayState = 'running';
isPlaying = true; isPlaying = true;
updateVinylAnimation();
} }
}
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();
}); });
// Mise à jour de la barre de progression à chaque changement de temps (sur les deux audios) // 4. Événements
vinyl.addEventListener('click', togglePlay);
audioForward.addEventListener('timeupdate', updateProgress); audioForward.addEventListener('timeupdate', updateProgress);
audioReverse.addEventListener('timeupdate', updateProgress); audioReverse.addEventListener('timeupdate', updateProgress);
progress.addEventListener('click', setProgress);
// Mise à jour de la durée du titre dès que l'audio est chargé (sur les deux)
audioForward.addEventListener('loadedmetadata', () => {
durationEl.textContent = formatTime(audioForward.duration);
});
audioReverse.addEventListener('loadedmetadata', () => {
durationEl.textContent = formatTime(audioReverse.duration);
});
// Permet de cliquer sur la barre de progression (pour avancer dans la piste forward)
progress.addEventListener('click', (e) => {
const width = progress.clientWidth;
const clickX = e.offsetX;
const duration = audioForward.duration;
audioForward.currentTime = (clickX / width) * duration;
// En cas d'avancer, on arrête le reverse
audioReverse.pause();
updateProgress();
});
// Bouton reculer de 10s (sur piste forward)
backwardBtn.addEventListener('click', () => { backwardBtn.addEventListener('click', () => {
audioForward.currentTime = Math.max(0, audioForward.currentTime - 10); let newTime = Math.max(0, audioForward.currentTime - 10);
audioReverse.pause(); audioForward.currentTime = newTime;
updateProgress(); audioReverse.currentTime = newTime;
}); });
// Bouton avancer de 10s (sur piste forward)
forwardBtn.addEventListener('click', () => { forwardBtn.addEventListener('click', () => {
audioForward.currentTime = Math.min(audioForward.duration, audioForward.currentTime + 10); let newTime = Math.min(duration, audioForward.currentTime + 10);
audioReverse.pause(); audioForward.currentTime = newTime;
updateProgress(); audioReverse.currentTime = newTime;
}); });
// Bouton pour activer/désactiver la boucle (LOOP)
loopBtn.addEventListener('click', () => { loopBtn.addEventListener('click', () => {
isLooping = !isLooping; isLooping = !isLooping;
loopBtn.style.backgroundColor = isLooping ? 'white' : 'transparent'; loopBtn.style.backgroundColor = isLooping ? 'white' : 'transparent';
loopBtn.style.color = isLooping ? '#18344b' : 'white'; loopBtn.style.color = isLooping ? '#18344b' : 'white';
audioForward.loop = isLooping;
audioReverse.loop = isLooping;
}); });
// Lorsque l'audio est terminé, on répète si la boucle est activée
audioForward.addEventListener('ended', () => { audioForward.addEventListener('ended', () => {
if (isLooping) { if (isLooping) {
audioForward.currentTime = 0; audioForward.currentTime = 0;
audioForward.play(); audioForward.play();
} else { } else {
vinyl.style.animationPlayState = 'paused'; updateVinylAnimation(false);
isPlaying = false; isPlaying = false;
} }
}); });
@ -198,7 +188,7 @@ audioReverse.addEventListener('ended', () => {
audioReverse.currentTime = 0; audioReverse.currentTime = 0;
audioReverse.play(); audioReverse.play();
} else { } else {
vinyl.style.animationPlayState = 'paused'; updateVinylAnimation(false);
isPlaying = false; isPlaying = false;
} }
}); });