samples de áudio não reiniciavam ao fim do loop
Deploy / Deploy (push) Successful in 2m2s Details

This commit is contained in:
JotaChina 2025-12-27 11:35:25 -03:00
parent d23c3aee4d
commit 39fa026e92
1 changed files with 27 additions and 39 deletions

View File

@ -107,11 +107,10 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) {
const player = new Tone.Player(toneBuf).sync().connect(gain); const player = new Tone.Player(toneBuf).sync().connect(gain);
const rate = const rate = clip.pitch && clip.pitch !== 0 ? Math.pow(2, clip.pitch / 12) : 1;
clip.pitch && clip.pitch !== 0 ? Math.pow(2, clip.pitch / 12) : 1;
player.playbackRate = rate; player.playbackRate = rate;
// --- tempo no Transport (em segundos) --- // tempo no Transport (em segundos)
const occurrenceInTransportSec = const occurrenceInTransportSec =
absolutePlayTime - startTime + (appState.audio.audioEditorSeekTime || 0); absolutePlayTime - startTime + (appState.audio.audioEditorSeekTime || 0);
@ -123,14 +122,11 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) {
const safeOffset = Math.max(0, offset); const safeOffset = Math.max(0, offset);
const safeDur = dur == null ? undefined : Math.max(0, dur); const safeDur = dur == null ? undefined : Math.max(0, dur);
// ✅ blindagem: nunca agenda no passado (especialmente após “virada” do loop) // ✅ USE O POSICIONAMENTO ATUAL (já atualizado pelo seu código)
let transportNow = const transportNow = Tone.Transport.seconds;
Tone.Transport.getSecondsAtTime
? Tone.Transport.getSecondsAtTime(Tone.now())
: Tone.Transport.seconds;
// pequena folga pra não “perder” o start por alguns ms // folga pequena pra não “perder” start
const EPS = 0.003; const EPS = 0.005;
const startAt = Math.max(safeOccurrence, transportNow + EPS); const startAt = Math.max(safeOccurrence, transportNow + EPS);
player.start(startAt, safeOffset, safeDur); player.start(startAt, safeOffset, safeDur);
@ -147,7 +143,6 @@ function _scheduleClip(clip, absolutePlayTime, durationSec, overrideOffsetSec) {
}; };
} }
function _handleClipEnd(eventId, clipId) { function _handleClipEnd(eventId, clipId) {
scheduledNodes.delete(eventId); scheduledNodes.delete(eventId);
runtimeClipState.delete(clipId); runtimeClipState.delete(clipId);
@ -311,30 +306,14 @@ function _animationLoop() {
if (isLoopActive) { if (isLoopActive) {
if (newLogicalTime >= loopEndTimeSec) { if (newLogicalTime >= loopEndTimeSec) {
const loopDuration = loopEndTimeSec - loopStartTimeSec; // ✅ volta exatamente pro início do loop (sem remainder/jitter)
if (loopDuration > 0) { newLogicalTime = loopStartTimeSec;
newLogicalTime =
loopStartTimeSec +
((newLogicalTime - loopStartTimeSec) % loopDuration);
} else {
newLogicalTime = loopStartTimeSec;
}
// realinha relógio interno // realinha relógio interno do seu scheduler
startTime = now; startTime = now;
appState.audio.audioEditorSeekTime = newLogicalTime; appState.audio.audioEditorSeekTime = newLogicalTime;
// ✅ força o Transport “pular” junto na virada do loop // limpa players/estado pra permitir reagendamento limpo
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
runtimeClipState.clear(); runtimeClipState.clear();
scheduledNodes.forEach(({ player }) => { scheduledNodes.forEach(({ player }) => {
try { player.unsync(); } catch {} try { player.unsync(); } catch {}
@ -343,16 +322,27 @@ function _animationLoop() {
}); });
scheduledNodes.clear(); scheduledNodes.clear();
// ✅ reinicia patterns do song (seu scheduler da playlist) // ✅ reinicia patterns e Transport de forma confiável
try { stopSongPatternPlaybackOnTransport(); } catch {}
try { try {
stopSongPatternPlaybackOnTransport(); const bpm = parseFloat(document.getElementById("bpm-input")?.value) || 120;
startSongPatternPlaybackOnTransport();
Tone.Transport.stop();
Tone.Transport.bpm.value = bpm;
// posiciona exatamente no loopStart
Tone.Transport.seconds = newLogicalTime;
Tone.Transport.start();
} catch {} } catch {}
// ✅ IMPORTANTÍSSIMO: recomeça clips que atravessam o loopStart try { startSongPatternPlaybackOnTransport(); } catch {}
_scheduleOverlappingClipsAtTime(newLogicalTime);
// 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(); _schedulerTick();
} }
} }
@ -383,8 +373,6 @@ function _animationLoop() {
animationFrameId = requestAnimationFrame(_animationLoop); animationFrameId = requestAnimationFrame(_animationLoop);
} }
// --- API Pública --- // --- API Pública ---
export function updateTransportLoop() { export function updateTransportLoop() {