259 lines
12 KiB
JavaScript
259 lines
12 KiB
JavaScript
// js/main.js
|
|
import { appState, resetProjectState } from "./state.js";
|
|
import { addTrackToState, removeLastTrackFromState } from "./pattern/pattern_state.js";
|
|
// --- CORREÇÃO AQUI ---
|
|
import { toggleRecording } from "./recording.js";
|
|
import { addAudioTrackLane, removeAudioClip } from "./audio/audio_state.js";
|
|
import { updateTransportLoop } from "./audio/audio_audio.js";
|
|
import {
|
|
togglePlayback,
|
|
stopPlayback,
|
|
rewindPlayback,
|
|
} from "./pattern/pattern_audio.js";
|
|
import {
|
|
startAudioEditorPlayback,
|
|
stopAudioEditorPlayback,
|
|
restartAudioEditorIfPlaying,
|
|
} from "./audio/audio_audio.js";
|
|
import { initializeAudioContext } from "./audio.js";
|
|
import { handleFileLoad, generateMmpFile } from "./file.js";
|
|
import { renderAll, loadAndRenderSampleBrowser, showOpenProjectModal, closeOpenProjectModal } from "./ui.js";
|
|
import { renderAudioEditor } from "./audio/audio_ui.js";
|
|
import { adjustValue, enforceNumericInput } from "./utils.js";
|
|
import { ZOOM_LEVELS } from "./config.js";
|
|
|
|
// --- NOVA FUNÇÃO ---
|
|
// Atualiza a aparência dos botões de ferramenta
|
|
function updateToolButtons() {
|
|
const sliceToolBtn = document.getElementById("slice-tool-btn");
|
|
const trimToolBtn = document.getElementById("resize-tool-trim");
|
|
const stretchToolBtn = document.getElementById("resize-tool-stretch");
|
|
|
|
if(sliceToolBtn) sliceToolBtn.classList.toggle("active", appState.global.sliceToolActive);
|
|
if(trimToolBtn) trimToolBtn.classList.toggle("active", !appState.global.sliceToolActive && appState.global.resizeMode === 'trim');
|
|
if(stretchToolBtn) stretchToolBtn.classList.toggle("active", !appState.global.sliceToolActive && appState.global.resizeMode === 'stretch');
|
|
|
|
// Desativa a ferramenta de corte se outra for selecionada
|
|
document.body.classList.toggle("slice-tool-active", appState.global.sliceToolActive);
|
|
}
|
|
|
|
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 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");
|
|
|
|
// --- NOVOS BOTÕES ---
|
|
const resizeToolTrimBtn = document.getElementById("resize-tool-trim");
|
|
const resizeToolStretchBtn = document.getElementById("resize-tool-stretch");
|
|
const recordBtn = document.getElementById("record-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 zoomInBtn = document.getElementById("zoom-in-btn");
|
|
const zoomOutBtn = document.getElementById("zoom-out-btn");
|
|
|
|
// --- LISTENERS ADICIONADOS (COM LÓGICA DE CONTROLLER) ---
|
|
|
|
// --- NOVO LISTENER PARA O BOTÃO DE GRAVAR ---
|
|
if (recordBtn) {
|
|
recordBtn.addEventListener("click", () => {
|
|
// Garante que o contexto de áudio foi iniciado por um gesto do usuário
|
|
initializeAudioContext();
|
|
toggleRecording();
|
|
});
|
|
}
|
|
|
|
// Listener para o botão "Excluir Clipe" no menu de contexto
|
|
const deleteClipBtn = document.getElementById('delete-clip');
|
|
if (deleteClipBtn) {
|
|
deleteClipBtn.addEventListener('click', () => {
|
|
const clipId = appState.global.selectedClipId; // 1. Lê o estado
|
|
if (clipId) {
|
|
if (removeAudioClip(clipId)) { // 2. Chama a função de state
|
|
appState.global.selectedClipId = null; // 3. Atualiza o estado global
|
|
renderAudioEditor(); // 4. Renderiza a mudança
|
|
}
|
|
}
|
|
// Esconde o menu
|
|
const menu = document.getElementById('timeline-context-menu');
|
|
if (menu) menu.style.display = 'none';
|
|
});
|
|
}
|
|
|
|
// Listener global para a tecla Delete/Backspace
|
|
document.addEventListener('keydown', (e) => {
|
|
// Ignora se estiver digitando em um input
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
|
return;
|
|
}
|
|
|
|
const clipId = appState.global.selectedClipId; // 1. Lê o estado
|
|
// Verifica se há um clipe selecionado e a tecla pressionada é Delete ou Backspace
|
|
if ((e.key === 'Delete' || e.key === 'Backspace') && clipId) {
|
|
e.preventDefault(); // Impede o navegador de voltar a página (ação do Backspace)
|
|
|
|
if (removeAudioClip(clipId)) { // 2. Chama a função de state
|
|
appState.global.selectedClipId = null; // 3. Atualiza o estado global
|
|
renderAudioEditor(); // 4. Renderiza a mudança
|
|
}
|
|
}
|
|
});
|
|
|
|
// Listener global para fechar menu de contexto ou desselecionar clipe
|
|
document.addEventListener('click', (e) => {
|
|
// Esconde o menu de contexto se clicar fora dele
|
|
const menu = document.getElementById('timeline-context-menu');
|
|
if (menu && !e.target.closest('#timeline-context-menu')) {
|
|
menu.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// --- FIM DOS LISTENERS ADICIONADOS ---
|
|
|
|
newProjectBtn.addEventListener("click", () => {
|
|
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';
|
|
renderAll();
|
|
});
|
|
|
|
addBarBtn.addEventListener("click", () => {
|
|
const barsInput = document.getElementById("bars-input");
|
|
if (barsInput) adjustValue(barsInput, 1);
|
|
});
|
|
|
|
openMmpBtn.addEventListener("click", showOpenProjectModal);
|
|
loadFromComputerBtn.addEventListener("click", () => mmpFileInput.click());
|
|
mmpFileInput.addEventListener("change", (event) => { const file = event.target.files[0]; if (file) { handleFileLoad(file).then(() => closeOpenProjectModal()); } });
|
|
uploadSampleBtn.addEventListener("click", () => sampleFileInput.click());
|
|
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.global.metronomeEnabled = !appState.global.metronomeEnabled; metronomeBtn.classList.toggle("active", appState.global.metronomeEnabled); });
|
|
|
|
// --- LISTENERS DE FERRAMENTAS ATUALIZADOS ---
|
|
if(sliceToolBtn) {
|
|
sliceToolBtn.addEventListener("click", () => {
|
|
appState.global.sliceToolActive = !appState.global.sliceToolActive;
|
|
updateToolButtons();
|
|
});
|
|
}
|
|
if(resizeToolTrimBtn) {
|
|
resizeToolTrimBtn.addEventListener("click", () => {
|
|
appState.global.resizeMode = 'trim';
|
|
appState.global.sliceToolActive = false;
|
|
updateToolButtons();
|
|
});
|
|
}
|
|
if(resizeToolStretchBtn) {
|
|
resizeToolStretchBtn.addEventListener("click", () => {
|
|
appState.global.resizeMode = 'stretch';
|
|
appState.global.sliceToolActive = false;
|
|
updateToolButtons();
|
|
});
|
|
}
|
|
|
|
openModalCloseBtn.addEventListener("click", closeOpenProjectModal);
|
|
|
|
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.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); } }); });
|
|
|
|
if (zoomInBtn) {
|
|
zoomInBtn.addEventListener("click", () => {
|
|
if (appState.global.zoomLevelIndex < ZOOM_LEVELS.length - 1) {
|
|
appState.global.zoomLevelIndex++;
|
|
renderAll();
|
|
}
|
|
});
|
|
}
|
|
if (zoomOutBtn) {
|
|
zoomOutBtn.addEventListener("click", () => {
|
|
if (appState.global.zoomLevelIndex > 0) {
|
|
appState.global.zoomLevelIndex--;
|
|
renderAll();
|
|
}
|
|
});
|
|
}
|
|
|
|
audioEditorPlayBtn.addEventListener("click", () => {
|
|
if (appState.global.isAudioEditorPlaying) {
|
|
stopAudioEditorPlayback(false); // Pausa
|
|
} else {
|
|
startAudioEditorPlayback();
|
|
}
|
|
});
|
|
audioEditorStopBtn.addEventListener("click", () => stopAudioEditorPlayback(true)); // Stop (rebobina)
|
|
|
|
audioEditorLoopBtn.addEventListener("click", () => {
|
|
appState.global.isLoopActive = !appState.global.isLoopActive;
|
|
appState.audio.isAudioEditorLoopEnabled = appState.global.isLoopActive;
|
|
audioEditorLoopBtn.classList.toggle("active", appState.global.isLoopActive);
|
|
updateTransportLoop();
|
|
const loopArea = document.getElementById("loop-region");
|
|
if (loopArea) {
|
|
loopArea.classList.toggle("visible", appState.global.isLoopActive);
|
|
}
|
|
restartAudioEditorIfPlaying();
|
|
});
|
|
|
|
if (addAudioTrackBtn) { addAudioTrackBtn.addEventListener("click", () => { addAudioTrackLane(); renderAudioEditor(); }); }
|
|
|
|
loadAndRenderSampleBrowser();
|
|
|
|
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');
|
|
}
|
|
});
|
|
}
|
|
|
|
renderAll();
|
|
updateToolButtons(); // Define o estado inicial dos botões
|
|
}); |