// js/audio_audio.js import { appState } from "../state.js"; import { updateAudioEditorUI, updatePlayheadVisual, resetPlayheadVisual } from "./audio_ui.js"; import { PIXELS_PER_STEP } from "../config.js"; import { initializeAudioContext } from "../audio.js"; import { getPixelsPerSecond } from "../utils.js"; function animationLoop() { if (!appState.global.isAudioEditorPlaying) return; const pixelsPerSecond = getPixelsPerSecond(); const totalElapsedTime = Tone.Transport.seconds; let maxTime = 0; appState.audio.clips.forEach(clip => { const endTime = clip.startTime + clip.duration; if (endTime > maxTime) maxTime = endTime; }); if (!appState.global.isLoopActive && totalElapsedTime >= maxTime && maxTime > 0) { stopAudioEditorPlayback(); resetPlayheadVisual(); return; } const newPositionPx = totalElapsedTime * pixelsPerSecond; updatePlayheadVisual(newPositionPx); // ##### CORREÇÃO 1 ##### // Salva o ID da animação para que o stop possa cancelá-lo appState.audio.audioEditorAnimationId = requestAnimationFrame(animationLoop); } export function updateTransportLoop() { Tone.Transport.loop = appState.global.isLoopActive; Tone.Transport.loopStart = appState.global.loopStartTime; Tone.Transport.loopEnd = appState.global.loopEndTime; } export function startAudioEditorPlayback() { if (appState.global.isAudioEditorPlaying) return; initializeAudioContext(); Tone.Transport.cancel(); // Limpa eventos agendados anteriormente updateTransportLoop(); // Isso deve definir Tone.Transport.loop = true e Tone.Transport.loopEnd // 1. Pegue a duração total do loop que a função acima definiu const loopInterval = Tone.Transport.loopEnd; // Se loopEnd não foi definido (ex: 0 ou undefined), o loop não funcionará. if (!loopInterval || loopInterval === 0) { console.error("LoopEnd não está definido no Tone.Transport! O áudio não repetirá."); // Você pode querer definir um padrão aqui, mas o ideal é // garantir que 'updateTransportLoop' esteja definindo 'loopEnd' corretamente. // ex: const loopInterval = "1m"; (se for um compasso por padrão) } appState.audio.clips.forEach(clip => { if (!clip.player || !clip.player.loaded) return; // 2. CORREÇÃO: Use scheduleRepeat no lugar de scheduleOnce Tone.Transport.scheduleRepeat((time) => { // Sua lógica de parâmetros está correta clip.gainNode.gain.value = Tone.gainToDb(clip.volume); clip.pannerNode.pan.value = clip.pan; clip.player.playbackRate = Math.pow(2, clip.pitch / 12); // Inicia o player no tempo agendado clip.player.start(time, clip.offset, clip.duration); }, loopInterval, // <--- O intervalo de repetição (ex: "4m", "8m") clip.startTime // <--- Onde o clip começa dentro da linha do tempo ); }); // 3. ADIÇÃO CRÍTICA: Inicie o transporte e atualize o estado Tone.Transport.start(); appState.global.isAudioEditorPlaying = true; // 4. (CORRIGIDO) Atualize a UI do botão de play const playBtn = document.getElementById("audio-editor-play-btn"); if (playBtn) { playBtn.classList.add("active"); // Verifica se o ícone existe antes de tentar mudá-lo const icon = playBtn.querySelector('i'); if (icon) { icon.className = 'fa-solid fa-pause'; } } // ##### CORREÇÃO 2 ##### // Inicia o loop de animação da agulha animationLoop(); } export function stopAudioEditorPlayback() { if (!appState.global.isAudioEditorPlaying) return; Tone.Transport.stop(); appState.audio.clips.forEach(clip => { if (clip.player && clip.player.state === 'started') { clip.player.stop(); } }); appState.audio.audioEditorPlaybackTime = Tone.Transport.seconds; // Esta lógica agora funcionará corretamente graças à Correção 1 if (appState.audio.audioEditorAnimationId) { cancelAnimationFrame(appState.audio.audioEditorAnimationId); appState.audio.audioEditorAnimationId = null; } // (CORRIGIDO) Atualiza a UI do botão de play const playBtn = document.getElementById("audio-editor-play-btn"); if (playBtn) { playBtn.classList.remove("active"); // Verifica se o ícone existe antes de tentar mudá-lo const icon = playBtn.querySelector('i'); if (icon) { icon.className = 'fa-solid fa-play'; // Muda de volta para "play" } } appState.global.isAudioEditorPlaying = false; updateAudioEditorUI(); } export function seekAudioEditor(newTime) { const wasPlaying = appState.global.isAudioEditorPlaying; if (wasPlaying) { stopAudioEditorPlayback(); } appState.audio.audioEditorPlaybackTime = newTime; Tone.Transport.seconds = newTime; const pixelsPerSecond = getPixelsPerSecond(); const newPositionPx = newTime * pixelsPerSecond; updatePlayheadVisual(newPositionPx); if (wasPlaying) { startAudioEditorPlayback(); } } export function restartAudioEditorIfPlaying() { if (appState.global.isAudioEditorPlaying) { appState.audio.audioEditorPlaybackTime = Tone.Transport.seconds; stopAudioEditorPlayback(); startAudioEditorPlayback(); } }