116 lines
3.4 KiB
JavaScript
116 lines
3.4 KiB
JavaScript
// js/state.js
|
|
import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js";
|
|
import {
|
|
initializeAudioContext,
|
|
getAudioContext,
|
|
getMainGainNode,
|
|
} from "./audio.js";
|
|
import { renderApp } from "./ui.js";
|
|
|
|
// O "cérebro" da aplicação
|
|
export let appState = {
|
|
tracks: [],
|
|
isPlaying: false,
|
|
playbackIntervalId: null,
|
|
currentStep: 0,
|
|
metronomeEnabled: false,
|
|
originalXmlDoc: null,
|
|
};
|
|
|
|
// ESSA É A FUNÇÃO QUE ESTÁ FALTANDO NO SEU ARQUIVO ATUAL
|
|
// Função auxiliar para carregar o buffer de áudio para uma track específica
|
|
export async function loadAudioForTrack(track) {
|
|
if (!track.samplePath) {
|
|
console.warn("Track sem samplePath, pulando o carregamento de áudio.");
|
|
return track;
|
|
}
|
|
try {
|
|
const audioContext = getAudioContext();
|
|
if (!audioContext) initializeAudioContext();
|
|
|
|
const response = await fetch(track.samplePath);
|
|
if (!response.ok) throw new Error(`Erro ao buscar o sample: ${response.statusText}`);
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
track.audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
console.log(`Áudio carregado para a trilha: ${track.name}`);
|
|
} catch (error) {
|
|
console.error(`Falha ao carregar áudio para a trilha ${track.name}:`, error);
|
|
track.audioBuffer = null; // Marca como falha para não tentar tocar
|
|
}
|
|
return track;
|
|
}
|
|
|
|
export function addTrackToState() {
|
|
initializeAudioContext();
|
|
const audioContext = getAudioContext();
|
|
const mainGainNode = getMainGainNode();
|
|
|
|
const newTrack = {
|
|
id: Date.now(),
|
|
name: "novo instrumento",
|
|
samplePath: null,
|
|
audioBuffer: null,
|
|
steps: [],
|
|
volume: DEFAULT_VOLUME,
|
|
pan: DEFAULT_PAN,
|
|
gainNode: audioContext.createGain(),
|
|
pannerNode: audioContext.createStereoPanner(),
|
|
};
|
|
newTrack.gainNode.connect(newTrack.pannerNode);
|
|
newTrack.pannerNode.connect(mainGainNode);
|
|
newTrack.gainNode.gain.value = newTrack.volume;
|
|
newTrack.pannerNode.pan.value = newTrack.pan;
|
|
|
|
appState.tracks.push(newTrack);
|
|
renderApp();
|
|
}
|
|
|
|
export function removeLastTrackFromState() {
|
|
appState.tracks.pop();
|
|
renderApp();
|
|
}
|
|
|
|
export async function updateTrackSample(trackId, samplePath) {
|
|
const track = appState.tracks.find((t) => t.id == trackId);
|
|
if (track) {
|
|
track.samplePath = samplePath;
|
|
track.name = samplePath.split("/").pop();
|
|
track.audioBuffer = null;
|
|
renderApp();
|
|
await loadAudioForTrack(track); // Reutiliza a nova função aqui
|
|
}
|
|
}
|
|
|
|
export function toggleStepState(trackId, stepIndex) {
|
|
const track = appState.tracks.find((t) => t.id == trackId);
|
|
if (track) {
|
|
track.steps[stepIndex] = !track.steps[stepIndex];
|
|
}
|
|
}
|
|
|
|
export function updateTrackVolume(trackId, volume) {
|
|
const track = appState.tracks.find((t) => t.id == trackId);
|
|
const audioContext = getAudioContext();
|
|
if (track) {
|
|
const clampedVolume = Math.max(0, Math.min(1.5, volume));
|
|
track.volume = clampedVolume;
|
|
if (track.gainNode) {
|
|
track.gainNode.gain.setValueAtTime(
|
|
clampedVolume,
|
|
audioContext.currentTime
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function updateTrackPan(trackId, pan) {
|
|
const track = appState.tracks.find((t) => t.id == trackId);
|
|
const audioContext = getAudioContext();
|
|
if (track) {
|
|
const clampedPan = Math.max(-1, Math.min(1, pan));
|
|
track.pan = clampedPan;
|
|
if (track.pannerNode) {
|
|
track.pannerNode.pan.setValueAtTime(clampedPan, audioContext.currentTime);
|
|
}
|
|
}
|
|
} |