// js/pattern/pattern_bounce.js import { appState } from '../state.js'; import { getSecondsPerStep } from '../utils.js'; import { addAudioClipToTimeline, addAudioTrackLane } from '../audio/audio_state.js'; import { getAudioContext } from '../audio.js'; import { renderAudioEditor } from '../audio/audio_ui.js'; /** * Renderiza (bounce) o pattern de beat atualmente ativo para uma nova pista de áudio. */ export async function bounceActivePatternToAudio() { console.log("Iniciando 'bounce' do pattern..."); // 1. Encontrar o pattern ativo const activeTrackId = appState.pattern.activeTrackId; // --- DEBUG --- console.log(`[DEBUG bounce] activeTrackId lido do estado: ${activeTrackId}`); const activeTrack = appState.pattern.tracks.find(t => t.id === activeTrackId); // --- DEBUG --- if (activeTrack) { console.log(`[DEBUG bounce] Pista ativa encontrada:`, activeTrack.name); console.log(`[DEBUG bounce] Verificando track.buffer:`, activeTrack.buffer); } else { console.error(`[DEBUG bounce] NENHUMA PISTA ATIVA ENCONTRADA. activeTrackId é nulo ou inválido.`); console.log(`[DEBUG bounce] Pistas disponíveis no estado:`, appState.pattern.tracks.map(t => ({id: t.id, name: t.name}))); } // --- FIM DEBUG --- if (!activeTrack) { alert('Nenhuma pista de pattern selecionada para renderizar.'); return; } const activePattern = activeTrack.patterns[activeTrack.activePatternIndex]; if (!activePattern) { alert('Nenhum pattern ativo encontrado na pista.'); return; } const trackBuffer = activeTrack.buffer; if (!trackBuffer) { alert('O áudio (sample) desta pista ainda não foi carregado.'); return; } // 2. Calcular a duração do pattern const steps = activePattern.steps; const totalSteps = steps.length; const secondsPerStep = getSecondsPerStep(); // const duration = totalSteps * secondsPerStep; if (duration <= 0) { alert("Pattern está vazio ou com duração zero."); return; } try { // 3. Usar Tone.Offline para renderizar o áudio const audioBuffer = await Tone.Offline(async (offlineCtx) => { const gainNode = new Tone.Gain(Tone.gainToDb(activeTrack.volume)).connect(offlineCtx.destination); const pannerNode = new Tone.Panner(activeTrack.pan).connect(gainNode); // const now = offlineCtx.currentTime; // --- INÍCIO DA CORREÇÃO (que estava na versão anterior) --- const offlineBuffer = offlineCtx.createBuffer( trackBuffer.numberOfChannels, trackBuffer.length, trackBuffer.sampleRate ); for (let i = 0; i < trackBuffer.numberOfChannels; i++) { offlineBuffer.copyToChannel(trackBuffer.getChannelData(i), i); } // --- FIM DA CORREÇÃO --- // Agendar todas as notas (steps) steps.forEach((isActive, index) => { if (isActive) { const time = now + (index * secondsPerStep); const source = new Tone.BufferSource(offlineBuffer).connect(pannerNode); source.start(time); } }); }, duration); // 4. Áudio renderizado. Agora, adicione-o ao editor de áudio. addAudioTrackLane(); // const newTrack = appState.audio.tracks[appState.audio.tracks.length - 1]; // const newTrackId = newTrack.id; const clipName = `${activeTrack.name}_(${activePattern.name})`; addAudioClipToTimeline(null, newTrackId, 0, clipName, audioBuffer); // console.log("Pattern renderizado com sucesso!"); } catch (err) { console.error("Erro ao renderizar o pattern:", err); alert(`Erro ao renderizar pattern: ${err.message}`); } }