154 lines
5.2 KiB
JavaScript
154 lines
5.2 KiB
JavaScript
// 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();
|
|
}
|
|
} |