230 lines
8.7 KiB
JavaScript
230 lines
8.7 KiB
JavaScript
// js/main.js
|
|
import {
|
|
appState,
|
|
addTrackToState,
|
|
removeLastTrackFromState,
|
|
} from "./state.js";
|
|
import {
|
|
togglePlayback,
|
|
stopPlayback,
|
|
rewindPlayback,
|
|
initializeAudioContext,
|
|
updateMasterVolume,
|
|
updateMasterPan,
|
|
} from "./audio.js";
|
|
import { handleFileLoad, generateMmpFile } from "./file.js";
|
|
import {
|
|
renderApp,
|
|
redrawSequencer,
|
|
loadAndRenderSampleBrowser,
|
|
showOpenProjectModal,
|
|
closeOpenProjectModal,
|
|
} from "./ui.js";
|
|
import { adjustValue, enforceNumericInput } from "./utils.js";
|
|
import { DEFAULT_PAN, DEFAULT_VOLUME } from "./config.js";
|
|
|
|
// --- INICIALIZAÇÃO E EVENTOS FINAIS ---
|
|
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 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 rewindBtn = document.getElementById("rewind-btn");
|
|
const metronomeBtn = document.getElementById("metronome-btn");
|
|
const mmpFileInput = document.getElementById("mmp-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: [],
|
|
isPlaying: false,
|
|
playbackIntervalId: null,
|
|
currentStep: 0,
|
|
metronomeEnabled: false,
|
|
originalXmlDoc: null,
|
|
masterVolume: DEFAULT_VOLUME,
|
|
masterPan: DEFAULT_PAN
|
|
});
|
|
// Reseta os inputs para o padrão
|
|
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;
|
|
renderApp();
|
|
setupMasterKnobs(); // Re-inicializa os knobs master
|
|
});
|
|
|
|
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 { // pan
|
|
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;
|
|
const clampedValue = Math.max(0, Math.min(1.5, newValue));
|
|
appState.masterVolume = clampedValue;
|
|
updateMasterVolume(clampedValue);
|
|
} else {
|
|
const newValue = appState.masterPan + direction * step;
|
|
const clampedValue = Math.max(-1, Math.min(1, newValue));
|
|
appState.masterPan = clampedValue;
|
|
updateMasterPan(clampedValue);
|
|
}
|
|
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") {
|
|
const clampedValue = Math.max(0, Math.min(1.5, newValue));
|
|
appState.masterVolume = clampedValue;
|
|
updateMasterVolume(clampedValue);
|
|
} else {
|
|
const clampedValue = Math.max(-1, Math.min(1, newValue));
|
|
appState.masterPan = clampedValue;
|
|
updateMasterPan(clampedValue);
|
|
}
|
|
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();
|
|
}
|
|
});
|
|
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);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Inicia a aplicação
|
|
loadAndRenderSampleBrowser();
|
|
renderApp();
|
|
setupMasterKnobs();
|
|
}); |