Volume e Pan master
Deploy / Deploy (push) Successful in 36s Details

This commit is contained in:
JotaChina 2025-08-29 20:27:21 -03:00
parent ec4d346791
commit fb95d52534
4 changed files with 134 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import { getTotalSteps } from "./utils.js";
let audioContext;
let mainGainNode;
let masterPannerNode;
const timerDisplay = document.getElementById('timer-display');
@ -19,13 +20,29 @@ export function initializeAudioContext() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
mainGainNode = audioContext.createGain();
mainGainNode.connect(audioContext.destination);
masterPannerNode = audioContext.createStereoPanner();
// Roteamento: Gain Master -> Panner Master -> Saída
mainGainNode.connect(masterPannerNode);
masterPannerNode.connect(audioContext.destination);
}
if (audioContext.state === "suspended") {
audioContext.resume();
}
}
export function updateMasterVolume(volume) {
if (mainGainNode) {
mainGainNode.gain.setValueAtTime(volume, audioContext.currentTime);
}
}
export function updateMasterPan(pan) {
if (masterPannerNode) {
masterPannerNode.pan.setValueAtTime(pan, audioContext.currentTime);
}
}
function formatTime(milliseconds) {
const totalSeconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');

View File

@ -9,6 +9,8 @@ import {
stopPlayback,
rewindPlayback,
initializeAudioContext,
updateMasterVolume,
updateMasterPan,
} from "./audio.js";
import { handleFileLoad, generateMmpFile } from "./file.js";
import {
@ -19,6 +21,7 @@ import {
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", () => {
@ -38,6 +41,9 @@ document.addEventListener("DOMContentLoaded", () => {
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 &&
@ -51,6 +57,8 @@ document.addEventListener("DOMContentLoaded", () => {
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;
@ -58,6 +66,7 @@ document.addEventListener("DOMContentLoaded", () => {
document.getElementById('compasso-a-input').value = 4;
document.getElementById('compasso-b-input').value = 4;
renderApp();
setupMasterKnobs(); // Re-inicializa os knobs master
});
addBarBtn.addEventListener("click", () => {
@ -67,9 +76,95 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
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) {
@ -77,7 +172,6 @@ document.addEventListener("DOMContentLoaded", () => {
closeOpenProjectModal();
}
});
saveMmpBtn.addEventListener("click", generateMmpFile);
addInstrumentBtn.addEventListener("click", addTrackToState);
removeInstrumentBtn.addEventListener("click", removeLastTrackFromState);
@ -100,7 +194,6 @@ document.addEventListener("DOMContentLoaded", () => {
? "fa-solid fa-caret-right"
: "fa-solid fa-caret-left";
});
const inputs = document.querySelectorAll(".value-input");
inputs.forEach((input) => {
input.addEventListener("input", (event) => {
@ -118,7 +211,6 @@ document.addEventListener("DOMContentLoaded", () => {
adjustValue(event.target, step);
});
});
const buttons = document.querySelectorAll(".adjust-btn");
buttons.forEach((button) => {
button.addEventListener("click", () => {
@ -134,4 +226,5 @@ document.addEventListener("DOMContentLoaded", () => {
// Inicia a aplicação
loadAndRenderSampleBrowser();
renderApp();
setupMasterKnobs();
});

View File

@ -15,10 +15,10 @@ export let appState = {
currentStep: 0,
metronomeEnabled: false,
originalXmlDoc: null,
masterVolume: DEFAULT_VOLUME,
masterPan: DEFAULT_PAN,
};
// 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.");
@ -35,7 +35,7 @@ export async function loadAudioForTrack(track) {
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
track.audioBuffer = null;
}
return track;
}
@ -77,7 +77,7 @@ export async function updateTrackSample(trackId, samplePath) {
track.name = samplePath.split("/").pop();
track.audioBuffer = null;
renderApp();
await loadAudioForTrack(track); // Reutiliza a nova função aqui
await loadAudioForTrack(track);
}
}

View File

@ -149,6 +149,21 @@
<button id="metronome-btn" title="Metrônomo On/Off">Metrônomo</button>
</div>
<div class="spacer"></div>
<div class="control-group master-controls">
<div class="knob-container">
<div class="knob" id="master-volume-knob">
<div class="knob-indicator"></div>
</div>
<span>VOL MASTER</span>
</div>
<div class="knob-container">
<div class="knob" id="master-pan-knob">
<div class="knob-indicator"></div>
</div>
<span>PAN MASTER</span>
</div>
</div>
</header>
<main class="main-content">