From 39fa026e92d28b4ef712a88519e30198a40e7994 Mon Sep 17 00:00:00 2001 From: JotaChina Date: Sat, 27 Dec 2025 11:35:25 -0300 Subject: [PATCH] =?UTF-8?q?samples=20de=20=C3=A1udio=20n=C3=A3o=20reinicia?= =?UTF-8?q?vam=20ao=20fim=20do=20loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/creations/audio/audio_audio.js | 66 ++++++++++-------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/assets/js/creations/audio/audio_audio.js b/assets/js/creations/audio/audio_audio.js index b53b8c0a..7e1c88de 100755 --- a/assets/js/creations/audio/audio_audio.js +++ b/assets/js/creations/audio/audio_audio.js @@ -107,11 +107,10 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) { const player = new Tone.Player(toneBuf).sync().connect(gain); - const rate = - clip.pitch && clip.pitch !== 0 ? Math.pow(2, clip.pitch / 12) : 1; + const rate = clip.pitch && clip.pitch !== 0 ? Math.pow(2, clip.pitch / 12) : 1; player.playbackRate = rate; - // --- tempo no Transport (em segundos) --- + // tempo no Transport (em segundos) const occurrenceInTransportSec = absolutePlayTime - startTime + (appState.audio.audioEditorSeekTime || 0); @@ -123,14 +122,11 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) { const safeOffset = Math.max(0, offset); const safeDur = dur == null ? undefined : Math.max(0, dur); - // ✅ blindagem: nunca agenda no passado (especialmente após “virada” do loop) - let transportNow = - Tone.Transport.getSecondsAtTime - ? Tone.Transport.getSecondsAtTime(Tone.now()) - : Tone.Transport.seconds; + // ✅ USE O POSICIONAMENTO ATUAL (já atualizado pelo seu código) + const transportNow = Tone.Transport.seconds; - // pequena folga pra não “perder” o start por alguns ms - const EPS = 0.003; + // folga pequena pra não “perder” start + const EPS = 0.005; const startAt = Math.max(safeOccurrence, transportNow + EPS); player.start(startAt, safeOffset, safeDur); @@ -147,7 +143,6 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) { }; } - function _handleClipEnd(eventId, clipId) { scheduledNodes.delete(eventId); runtimeClipState.delete(clipId); @@ -311,30 +306,14 @@ function _animationLoop() { if (isLoopActive) { if (newLogicalTime >= loopEndTimeSec) { - const loopDuration = loopEndTimeSec - loopStartTimeSec; - if (loopDuration > 0) { - newLogicalTime = - loopStartTimeSec + - ((newLogicalTime - loopStartTimeSec) % loopDuration); - } else { - newLogicalTime = loopStartTimeSec; - } + // ✅ volta exatamente pro início do loop (sem remainder/jitter) + newLogicalTime = loopStartTimeSec; - // realinha relógio interno + // realinha relógio interno do seu scheduler startTime = now; appState.audio.audioEditorSeekTime = newLogicalTime; - // ✅ força o Transport “pular” junto na virada do loop - try { - // (desativa loop do Transport aqui pra não brigar com a sua lógica de loop da playlist) - Tone.Transport.loop = false; - } catch {} - - try { - Tone.Transport.seconds = newLogicalTime; - } catch {} - - // ✅ limpa players/estado pra permitir reagendamento limpo + // limpa players/estado pra permitir reagendamento limpo runtimeClipState.clear(); scheduledNodes.forEach(({ player }) => { try { player.unsync(); } catch {} @@ -343,16 +322,27 @@ function _animationLoop() { }); scheduledNodes.clear(); - // ✅ reinicia patterns do song (seu scheduler da playlist) + // ✅ reinicia patterns e Transport de forma confiável + try { stopSongPatternPlaybackOnTransport(); } catch {} + try { - stopSongPatternPlaybackOnTransport(); - startSongPatternPlaybackOnTransport(); + const bpm = parseFloat(document.getElementById("bpm-input")?.value) || 120; + + Tone.Transport.stop(); + Tone.Transport.bpm.value = bpm; + + // posiciona exatamente no loopStart + Tone.Transport.seconds = newLogicalTime; + + Tone.Transport.start(); } catch {} - // ✅ IMPORTANTÍSSIMO: recomeça clips que atravessam o loopStart - _scheduleOverlappingClipsAtTime(newLogicalTime); + try { startSongPatternPlaybackOnTransport(); } catch {} - // e já agenda os próximos inícios sem esperar o próximo interval tick + // ✅ recomeça clips que atravessam o loopStart (mais preciso que AtTime) + _scheduleOverlappingClipsAtLoopStart(loopStartTimeSec, loopEndTimeSec); + + // agenda os próximos inícios sem esperar o próximo interval tick _schedulerTick(); } } @@ -383,8 +373,6 @@ function _animationLoop() { animationFrameId = requestAnimationFrame(_animationLoop); } - - // --- API Pública --- export function updateTransportLoop() {