// js/main.js import { appState, addTrackToState, removeLastTrackFromState, } from "./state.js"; import { togglePlayback, stopPlayback, rewindPlayback, initializeAudioContext, updateMasterVolume, updateMasterPan, startAudioEditorPlayback, stopAudioEditorPlayback, } from "./audio.js"; import { handleFileLoad, generateMmpFile } from "./file.js"; import { renderApp, redrawSequencer, loadAndRenderSampleBrowser, showOpenProjectModal, closeOpenProjectModal, handleSampleUpload, // Importa handleSampleUpload, embora não seja mais usado diretamente } from "./ui.js"; import { adjustValue, enforceNumericInput } from "./utils.js"; import { DEFAULT_PAN, DEFAULT_VOLUME } from "./config.js"; document.addEventListener("DOMContentLoaded", () => { const newProjectBtn = document.getElementById("new-project-btn"); const openMmpBtn = document.getElementById("open-mmp-btn"); const saveMmpBtn = document.getElementById("save-mmp-btn"); const uploadSampleBtn = document.getElementById("upload-sample-btn"); const addInstrumentBtn = document.getElementById("add-instrument-btn"); const removeInstrumentBtn = document.getElementById("remove-instrument-btn"); const playBtn = document.getElementById("play-btn"); const stopBtn = document.getElementById("stop-btn"); const audioEditorPlayBtn = document.getElementById("audio-editor-play-btn"); const audioEditorStopBtn = document.getElementById("audio-editor-stop-btn"); const rewindBtn = document.getElementById("rewind-btn"); const metronomeBtn = document.getElementById("metronome-btn"); const mmpFileInput = document.getElementById("mmp-file-input"); const sampleFileInput = document.getElementById("sample-file-input"); const openProjectModal = document.getElementById("open-project-modal"); const openModalCloseBtn = document.getElementById("open-modal-close-btn"); const loadFromComputerBtn = document.getElementById("load-from-computer-btn"); const sidebarToggle = document.getElementById("sidebar-toggle"); const addBarBtn = document.getElementById("add-bar-btn"); const masterVolumeKnob = document.getElementById("master-volume-knob"); const masterPanKnob = document.getElementById("master-pan-knob"); newProjectBtn.addEventListener("click", () => { if ( appState.tracks.length > 0 && !confirm("Você tem certeza? Alterações não salvas serão perdidas.") ) return; Object.assign(appState, { tracks: [], activeTrackId: null, isPlaying: false, playbackIntervalId: null, currentStep: 0, metronomeEnabled: false, originalXmlDoc: null, currentBeatBasslineName: 'Novo Projeto', masterVolume: DEFAULT_VOLUME, masterPan: DEFAULT_PAN }); document.getElementById('bpm-input').value = 140; document.getElementById('bars-input').value = 1; document.getElementById('compasso-a-input').value = 4; document.getElementById('compasso-b-input').value = 4; const titleElement = document.getElementById('beat-bassline-title'); if(titleElement) titleElement.textContent = 'Novo Projeto'; renderApp(); setupMasterKnobs(); }); addBarBtn.addEventListener("click", () => { const barsInput = document.getElementById("bars-input"); if (barsInput) adjustValue(barsInput, 1); }); function setupMasterKnobs() { function updateMasterKnobVisual(knobElement, controlType) { const indicator = knobElement.querySelector(".knob-indicator"); if (!indicator) return; const minAngle = -135; const maxAngle = 135; let percentage = 0.5; let title = ""; if (controlType === "volume") { const value = appState.masterVolume; percentage = value / 1.5; title = `Volume Master: ${Math.round(value * 100)}%`; } else { const value = appState.masterPan; percentage = (value + 1) / 2; const panDisplay = Math.round(value * 100); title = `Pan Master: ${ panDisplay === 0 ? "Centro" : panDisplay < 0 ? `${-panDisplay} L` : `${panDisplay} R` }`; } const angle = minAngle + percentage * (maxAngle - minAngle); indicator.style.transform = `translateX(-50%) rotate(${angle}deg)`; knobElement.title = title; } function addMasterKnobInteraction(knobElement, controlType) { knobElement.addEventListener("wheel", (e) => { e.preventDefault(); const step = 0.05; const direction = e.deltaY < 0 ? 1 : -1; if (controlType === "volume") { const newValue = appState.masterVolume + direction * step; appState.masterVolume = Math.max(0, Math.min(1.5, newValue)); updateMasterVolume(appState.masterVolume); } else { const newValue = appState.masterPan + direction * step; appState.masterPan = Math.max(-1, Math.min(1, newValue)); updateMasterPan(appState.masterPan); } updateMasterKnobVisual(knobElement, controlType); }); knobElement.addEventListener("mousedown", (e) => { if (e.button !== 0) return; e.preventDefault(); const startY = e.clientY; const startValue = controlType === "volume" ? appState.masterVolume : appState.masterPan; document.body.classList.add("knob-dragging"); function onMouseMove(moveEvent) { const deltaY = startY - moveEvent.clientY; const sensitivity = controlType === "volume" ? 150 : 200; const newValue = startValue + deltaY / sensitivity; if (controlType === "volume") { appState.masterVolume = Math.max(0, Math.min(1.5, newValue)); updateMasterVolume(appState.masterVolume); } else { appState.masterPan = Math.max(-1, Math.min(1, newValue)); updateMasterPan(appState.masterPan); } updateMasterKnobVisual(knobElement, controlType); } function onMouseUp() { document.body.classList.remove("knob-dragging"); document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); } document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }); } addMasterKnobInteraction(masterVolumeKnob, "volume"); updateMasterKnobVisual(masterVolumeKnob, "volume"); addMasterKnobInteraction(masterPanKnob, "pan"); updateMasterKnobVisual(masterPanKnob, "pan"); } openMmpBtn.addEventListener("click", showOpenProjectModal); loadFromComputerBtn.addEventListener("click", () => mmpFileInput.click()); mmpFileInput.addEventListener("change", async (event) => { const file = event.target.files[0]; if (file) { await handleFileLoad(file); closeOpenProjectModal(); } }); uploadSampleBtn.addEventListener("click", () => sampleFileInput.click()); // --- INÍCIO DA CORREÇÃO --- // Lógica de upload de sample para o servidor Flask sampleFileInput.addEventListener("change", async (event) => { const file = event.target.files[0]; if (!file) return; const formData = new FormData(); formData.append("sampleFile", file); try { // ATENÇÃO: Verifique se a URL e a porta estão corretas para o seu servidor Flask const response = await fetch('http://localhost:5000/upload-sample', { method: 'POST', body: formData, }); const result = await response.json(); if (response.ok) { alert("Sample enviado com sucesso!"); // Recarrega a lista de samples para exibir o novo arquivo await loadAndRenderSampleBrowser(); } else { throw new Error(result.error || "Erro desconhecido no servidor."); } } catch (error) { console.error("Erro ao enviar o sample:", error); alert(`Falha no upload: ${error.message}`); } event.target.value = null; // Limpa o input para permitir o mesmo arquivo de novo }); // --- FIM DA CORREÇÃO --- saveMmpBtn.addEventListener("click", generateMmpFile); addInstrumentBtn.addEventListener("click", addTrackToState); removeInstrumentBtn.addEventListener("click", removeLastTrackFromState); playBtn.addEventListener("click", togglePlayback); stopBtn.addEventListener("click", stopPlayback); rewindBtn.addEventListener("click", rewindPlayback); metronomeBtn.addEventListener("click", () => { initializeAudioContext(); appState.metronomeEnabled = !appState.metronomeEnabled; metronomeBtn.classList.toggle("active", appState.metronomeEnabled); }); openModalCloseBtn.addEventListener("click", closeOpenProjectModal); openProjectModal.addEventListener("click", (e) => { if (e.target === openProjectModal) closeOpenProjectModal(); }); sidebarToggle.addEventListener("click", () => { document.body.classList.toggle("sidebar-hidden"); const icon = sidebarToggle.querySelector("i"); icon.className = document.body.classList.contains("sidebar-hidden") ? "fa-solid fa-caret-right" : "fa-solid fa-caret-left"; }); const inputs = document.querySelectorAll(".value-input"); inputs.forEach((input) => { input.addEventListener("input", (event) => { enforceNumericInput(event); if (appState.isPlaying && (event.target.id.startsWith("compasso-") || event.target.id === 'bars-input')) { stopPlayback(); } if (event.target.id.startsWith("compasso-") || event.target.id === 'bars-input') { redrawSequencer(); } }); input.addEventListener("wheel", (event) => { event.preventDefault(); const step = event.deltaY < 0 ? 1 : -1; adjustValue(event.target, step); }); }); const buttons = document.querySelectorAll(".adjust-btn"); buttons.forEach((button) => { button.addEventListener("click", () => { const targetId = button.dataset.target + "-input"; const targetInput = document.getElementById(targetId); const step = parseInt(button.dataset.step, 10) || 1; if (targetInput) { adjustValue(targetInput, step); } }); }); // Listeners para os controles do editor de áudio audioEditorPlayBtn.addEventListener("click", () => { if (appState.isAudioEditorPlaying) { stopAudioEditorPlayback(); } else { startAudioEditorPlayback(); } }); audioEditorStopBtn.addEventListener("click", stopAudioEditorPlayback); loadAndRenderSampleBrowser(); renderApp(); setupMasterKnobs(); });