samples de áudio não reiniciavam ao fim do loop
Deploy / Deploy (push) Successful in 2m2s
Details
Deploy / Deploy (push) Successful in 2m2s
Details
This commit is contained in:
parent
d23c3aee4d
commit
39fa026e92
|
|
@ -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() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue