mmpSearch/assets/js/creations/audio/audio_audio.js

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();
}
}