From 62cac7c45998f3b837683af734f9296549cb03e9 Mon Sep 17 00:00:00 2001 From: el-yazide mohamed Date: Sun, 8 Jun 2025 10:25:31 +0200 Subject: [PATCH] web audio API --- node-server/assets/muddy_files.html | 4 +- node-server/assets/script.js | 155 +++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 4 deletions(-) diff --git a/node-server/assets/muddy_files.html b/node-server/assets/muddy_files.html index 2c5bf7f..bc0822c 100644 --- a/node-server/assets/muddy_files.html +++ b/node-server/assets/muddy_files.html @@ -43,9 +43,7 @@ - + diff --git a/node-server/assets/script.js b/node-server/assets/script.js index bbb1ee0..e1dc4b5 100644 --- a/node-server/assets/script.js +++ b/node-server/assets/script.js @@ -1,4 +1,4 @@ -// 1. Sélection des éléments DOM +/* // 1. Sélection des éléments DOM const vinyl = document.getElementById('vinyl'); const audio = document.getElementById('audio'); const progress = document.getElementById('progress'); @@ -148,4 +148,157 @@ audio.addEventListener('ended', () => { } else { isPlaying = false; } +}); */ + +// === Variables DOM === +const vinyl = document.getElementById('vinyl'); +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'); + +// === Web Audio === +let audioCtx; +let buffer; +let source; +let startTime = 0; +let offset = 0; +let isPlaying = false; +let playbackRate = 1; +let isLooping = false; + +// === Socket + Vitesse === +let idleTimeout = null; +let lastPosition = null; +let lastTime = null; + +// === Initialisation Audio (doit être déclenchée par une interaction utilisateur) === +async function initAudio() { + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + const response = await fetch('sounds/muddy_files.mp3'); // adapte ici + const arrayBuffer = await response.arrayBuffer(); + buffer = await audioCtx.decodeAudioData(arrayBuffer); + durationEl.textContent = formatTime(buffer.duration); +} + +// === Lecture === +function startPlayback() { + if (isPlaying || !buffer) return; + source = audioCtx.createBufferSource(); + source.buffer = buffer; + source.playbackRate.value = playbackRate; + source.loop = isLooping; + source.connect(audioCtx.destination); + source.start(0, offset); + startTime = audioCtx.currentTime; + isPlaying = true; + + source.onended = () => { + isPlaying = false; + if (isLooping) { + offset = 0; + startPlayback(); + } + }; +} + +function stopPlayback() { + if (!isPlaying) return; + source.stop(); + offset += (audioCtx.currentTime - startTime) * playbackRate; + isPlaying = false; +} + +function updateSpeed(speed) { + const MIN_SPEED = 1; + const MAX_SPEED = 100; + + if (Math.abs(speed) < MIN_SPEED) { + stopPlayback(); + return; + } + + let normSpeed = Math.min(Math.max(speed, MIN_SPEED), MAX_SPEED); + playbackRate = 0.5 + ((normSpeed - MIN_SPEED) / (MAX_SPEED - MIN_SPEED)) * 0.5; + + if (isPlaying) { + stopPlayback(); + startPlayback(); + } +} + +// === Socket.io === +const socket = io(); + +socket.on('position', (position) => { + const now = Date.now(); + + if (lastPosition !== null && lastTime !== null) { + const deltaPos = position - lastPosition; + const deltaTime = (now - lastTime) / 1000; + const speed = deltaPos / deltaTime; + + clearTimeout(idleTimeout); + + if (Math.abs(speed) < 1) { + stopPlayback(); + return; + } + + if (speed < 0) { + stopPlayback(); + offset = Math.max(0, offset + speed * 0.01); // ajuste sensibilité + } else { + updateSpeed(speed); + } + } + + lastPosition = position; + lastTime = now; + + idleTimeout = setTimeout(() => { + stopPlayback(); + }, 300); }); + +// === Contrôles UI === +vinyl.addEventListener('click', () => { + if (!audioCtx) initAudio().then(startPlayback); + else if (isPlaying) stopPlayback(); + else startPlayback(); +}); + +backwardBtn.addEventListener('click', () => { + offset = Math.max(0, offset - 10); +}); + +forwardBtn.addEventListener('click', () => { + offset = Math.min(buffer.duration, offset + 10); +}); + +loopBtn.addEventListener('click', () => { + isLooping = !isLooping; + loopBtn.style.backgroundColor = isLooping ? 'white' : 'transparent'; + loopBtn.style.color = isLooping ? '#18344b' : 'white'; +}); + +// === Affichage rotation & temps === +function formatTime(time) { + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60); + return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; +} + +function updateVinylRotation() { + if (!buffer) return; + currentRotation = (offset / buffer.duration) * 360 * 3; + vinyl.style.transform = `rotate(${currentRotation}deg)`; +} + +setInterval(() => { + if (isPlaying) updateVinylRotation(); + currentTimeEl.textContent = formatTime(offset + (audioCtx.currentTime - startTime) * playbackRate); +}, 100); +