108 lines
3.9 KiB
JavaScript
108 lines
3.9 KiB
JavaScript
// 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}`);
|
|
}
|
|
} |