versão 2.0
This commit is contained in:
1 parent
d506332268
commit
fe735bf0a6
16 files changed
+1735
-1626
No files matched your search
+102
-198
@@ -1,31 +1,24 @@
|
||||
// js/main.js
|
||||
import {
|
||||
appState,
|
||||
addTrackToState,
|
||||
removeLastTrackFromState,
|
||||
} from "./state.js";
|
||||
import { appState, resetProjectState } from "./state.js";
|
||||
import { addTrackToState, removeLastTrackFromState } from "./pattern/pattern_state.js";
|
||||
import { addAudioTrackLane } from "./audio/audio_state.js";
|
||||
import { updateTransportLoop } from "./audio/audio_audio.js";
|
||||
import {
|
||||
togglePlayback,
|
||||
stopPlayback,
|
||||
rewindPlayback,
|
||||
initializeAudioContext,
|
||||
updateMasterVolume,
|
||||
updateMasterPan,
|
||||
} from "./pattern/pattern_audio.js";
|
||||
import {
|
||||
startAudioEditorPlayback,
|
||||
stopAudioEditorPlayback,
|
||||
restartAudioEditorIfPlaying,
|
||||
} from "./audio.js";
|
||||
} from "./audio/audio_audio.js";
|
||||
import { initializeAudioContext } from "./audio.js";
|
||||
import { handleFileLoad, generateMmpFile } from "./file.js";
|
||||
import {
|
||||
renderApp,
|
||||
redrawSequencer,
|
||||
loadAndRenderSampleBrowser,
|
||||
showOpenProjectModal,
|
||||
closeOpenProjectModal,
|
||||
handleSampleUpload,
|
||||
} from "./ui.js";
|
||||
import { renderAll, loadAndRenderSampleBrowser, showOpenProjectModal, closeOpenProjectModal } from "./ui.js";
|
||||
import { renderAudioEditor } from "./audio/audio_ui.js";
|
||||
import { adjustValue, enforceNumericInput } from "./utils.js";
|
||||
import { DEFAULT_PAN, DEFAULT_VOLUME } from "./config.js";
|
||||
import { ZOOM_LEVELS } from "./config.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const newProjectBtn = document.getElementById("new-project-btn");
|
||||
@@ -39,8 +32,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const audioEditorPlayBtn = document.getElementById("audio-editor-play-btn");
|
||||
const audioEditorStopBtn = document.getElementById("audio-editor-stop-btn");
|
||||
const audioEditorLoopBtn = document.getElementById("audio-editor-loop-btn");
|
||||
const addAudioTrackBtn = document.getElementById("add-audio-track-btn");
|
||||
const rewindBtn = document.getElementById("rewind-btn");
|
||||
const metronomeBtn = document.getElementById("metronome-btn");
|
||||
const sliceToolBtn = document.getElementById("slice-tool-btn");
|
||||
const mmpFileInput = document.getElementById("mmp-file-input");
|
||||
const sampleFileInput = document.getElementById("sample-file-input");
|
||||
const openProjectModal = document.getElementById("open-project-modal");
|
||||
@@ -48,225 +43,134 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
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");
|
||||
const zoomInBtn = document.getElementById("zoom-in-btn");
|
||||
const zoomOutBtn = document.getElementById("zoom-out-btn");
|
||||
|
||||
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: [],
|
||||
audioTracks: [],
|
||||
activeTrackId: null,
|
||||
isPlaying: false,
|
||||
playbackIntervalId: null,
|
||||
currentStep: 0,
|
||||
metronomeEnabled: false,
|
||||
originalXmlDoc: null,
|
||||
currentBeatBasslineName: 'Novo Projeto',
|
||||
masterVolume: DEFAULT_VOLUME,
|
||||
masterPan: DEFAULT_PAN
|
||||
});
|
||||
if ((appState.pattern.tracks.length > 0 || appState.audio.clips.length > 0) && !confirm("Você tem certeza? Alterações não salvas serão perdidas.")) return;
|
||||
resetProjectState();
|
||||
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();
|
||||
renderAll();
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
mmpFileInput.addEventListener("change", (event) => { const file = event.target.files[0]; if (file) { handleFileLoad(file).then(() => closeOpenProjectModal()); } });
|
||||
uploadSampleBtn.addEventListener("click", () => sampleFileInput.click());
|
||||
|
||||
sampleFileInput.addEventListener("change", async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("sampleFile", file);
|
||||
|
||||
try {
|
||||
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!");
|
||||
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;
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
metronomeBtn.addEventListener("click", () => { initializeAudioContext(); appState.global.metronomeEnabled = !appState.global.metronomeEnabled; metronomeBtn.classList.toggle("active", appState.global.metronomeEnabled); });
|
||||
if(sliceToolBtn) { sliceToolBtn.addEventListener("click", () => { appState.global.sliceToolActive = !appState.global.sliceToolActive; sliceToolBtn.classList.toggle("active", appState.global.sliceToolActive); document.body.classList.toggle("slice-tool-active", appState.global.sliceToolActive); }); }
|
||||
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";
|
||||
|
||||
// ### CORREÇÃO 2: Adicionada verificação 'if (icon)' ###
|
||||
sidebarToggle.addEventListener("click", () => {
|
||||
document.body.classList.toggle("sidebar-hidden");
|
||||
const icon = sidebarToggle.querySelector("i");
|
||||
if (icon) {
|
||||
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);
|
||||
if (appState.global.isPlaying && (event.target.id.startsWith("compasso-") || event.target.id === 'bars-input')) { stopPlayback(); }
|
||||
if (event.target.id.startsWith("compasso-") || event.target.id === 'bars-input' || event.target.id === 'bpm-input') { renderAll(); }
|
||||
});
|
||||
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);
|
||||
}
|
||||
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); } }); });
|
||||
|
||||
if (zoomInBtn) {
|
||||
zoomInBtn.addEventListener("click", () => {
|
||||
if (appState.global.zoomLevelIndex < ZOOM_LEVELS.length - 1) {
|
||||
appState.global.zoomLevelIndex++;
|
||||
renderAll();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
audioEditorPlayBtn.addEventListener("click", () => {
|
||||
if (appState.isAudioEditorPlaying) {
|
||||
stopAudioEditorPlayback();
|
||||
} else {
|
||||
startAudioEditorPlayback();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (zoomOutBtn) {
|
||||
zoomOutBtn.addEventListener("click", () => {
|
||||
if (appState.global.zoomLevelIndex > 0) {
|
||||
appState.global.zoomLevelIndex--;
|
||||
renderAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
audioEditorPlayBtn.addEventListener("click", () => { if (appState.global.isAudioEditorPlaying) { stopAudioEditorPlayback(); } else { startAudioEditorPlayback(); } });
|
||||
audioEditorStopBtn.addEventListener("click", stopAudioEditorPlayback);
|
||||
|
||||
|
||||
// ### CORREÇÃO 1: Listeners duplicados combinados em um só ###
|
||||
// No main.js
|
||||
audioEditorLoopBtn.addEventListener("click", () => {
|
||||
appState.isAudioEditorLoopEnabled = !appState.isAudioEditorLoopEnabled;
|
||||
audioEditorLoopBtn.classList.toggle("active", appState.isAudioEditorLoopEnabled);
|
||||
console.log("--- Botão de Loop Clicado ---"); // DEBUG 1
|
||||
|
||||
// 1. Altera o estado global de loop
|
||||
appState.global.isLoopActive = !appState.global.isLoopActive;
|
||||
console.log("Estado appState.global.isLoopActive:", appState.global.isLoopActive); // DEBUG 2
|
||||
|
||||
// 2. Sincroniza o estado do loop do editor
|
||||
appState.audio.isAudioEditorLoopEnabled = appState.global.isLoopActive;
|
||||
|
||||
// 3. Atualiza a aparência do botão
|
||||
audioEditorLoopBtn.classList.toggle("active", appState.global.isLoopActive);
|
||||
|
||||
// 4. Sincroniza o Tone.Transport
|
||||
updateTransportLoop();
|
||||
|
||||
// 5. Mostra/esconde a área de loop
|
||||
const loopArea = document.getElementById("loop-region");
|
||||
|
||||
// ESTE É O TESTE MAIS IMPORTANTE:
|
||||
if (loopArea) {
|
||||
console.log("Elemento #loop-region ENCONTRADO. Alterando classe 'visible'."); // DEBUG 3
|
||||
loopArea.classList.toggle("visible", appState.global.isLoopActive);
|
||||
} else {
|
||||
console.error("ERRO GRAVE: Elemento #loop-region NÃO FOI ENCONTRADO!"); // DEBUG 4
|
||||
}
|
||||
|
||||
// 6. Reinicia o playback se estiver tocando
|
||||
restartAudioEditorIfPlaying();
|
||||
});
|
||||
|
||||
if (addAudioTrackBtn) { addAudioTrackBtn.addEventListener("click", () => { addAudioTrackLane(); renderAudioEditor(); }); }
|
||||
|
||||
// ### CORREÇÃO 3: Ordem de execução corrigida ###
|
||||
|
||||
// 1. Carrega o conteúdo do navegador de samples
|
||||
loadAndRenderSampleBrowser();
|
||||
renderApp();
|
||||
setupMasterKnobs();
|
||||
|
||||
// 2. Adiciona o listener DEPOIS que o conteúdo supostamente existe
|
||||
const browserContent = document.getElementById('browser-content');
|
||||
if (browserContent) {
|
||||
browserContent.addEventListener('click', function(event) {
|
||||
const folderName = event.target.closest('.folder-name');
|
||||
if (folderName) {
|
||||
const folderItem = folderName.parentElement;
|
||||
folderItem.classList.toggle('open');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Renderiza o resto
|
||||
renderAll();
|
||||
});
|
||||
Reference in new issue
Block a user