// js/utils.js import { appState } from './state.js'; import { PIXELS_PER_STEP, ZOOM_LEVELS } from './config.js'; export const DEFAULT_PROJECT_XML = `


]]>
`; export const SAMPLE_MANIFEST = `src_mmpSearch/metadata/samples-manifest.json` export const MMP_MANIFEST = `src_mmpSearch/metadata/mmp-manifest.json` export const SAMPLE_SRC = `src_mmpSearch/samples` /** * Helper interna para ler o BPM do input. * @returns {number} O BPM atual. */ function _getBpm() { const bpmInput = document.getElementById("bpm-input"); return parseFloat(bpmInput.value, 10) || 120; } /** * Calcula e exporta quantos compassos (beats) existem por compasso (bar). * @returns {number} O número de batidas por compasso (ex: 4 para 4/4). */ export function getBeatsPerBar() { const compassoAInput = document.getElementById("compasso-a-input"); return parseInt(compassoAInput.value, 10) || 4; } /** * Calcula e exporta quantos segundos dura uma "batida" (beat). * No contexto de BPM, uma "batida" é quase sempre uma semínima (1/4). * @returns {number} Duração da batida em segundos. */ export function getSecondsPerBeat() { return 60.0 / _getBpm(); } /** * Calcula e exporta quantos segundos dura um "step". * Baseado na config, um "step" é uma semicolcheia (1/16). * Há 4 steps (1/16) por batida (1/4). * @returns {number} Duração do step em segundos. */ export function getSecondsPerStep() { return getSecondsPerBeat() / 4.0; // 4 steps (1/16) por beat (1/4) } /** * Quantiza (arredonda) um tempo em segundos para o "step" do grid mais próximo. * @param {number} timeInSeconds - O tempo arbitrário (ex: 1.234s). * @returns {number} O tempo alinhado ao grid (ex: 1.250s). */ export function quantizeTime(timeInSeconds) { // TODO: Adicionar um toggle global (appState.global.isSnapEnabled) const secondsPerStep = getSecondsPerStep(); if (secondsPerStep <= 0) return timeInSeconds; // Evita divisão por zero const roundedSteps = Math.round(timeInSeconds / secondsPerStep); return roundedSteps * secondsPerStep; } /** * Calcula a quantidade de pixels que representa um segundo na timeline, * levando em conta o BPM e o nível de zoom atual. * @returns {number} A quantidade de pixels por segundo. */ export function getPixelsPerSecond() { const bpm = _getBpm(); // Usa a helper interna // (bpm / 60) = batidas por segundo // * 4 = steps por segundo (assumindo 4 steps/beat) const stepsPerSecond = (bpm / 60) * 4; const zoomFactor = ZOOM_LEVELS[appState.global.zoomLevelIndex]; return stepsPerSecond * PIXELS_PER_STEP * zoomFactor; } /** * Calcula o número total de steps no sequenciador de patterns. * @returns {number} O número total de steps. */ export function getTotalSteps() { const barsInput = document.getElementById("bars-input"); const compassoAInput = document.getElementById("compasso-a-input"); const compassoBInput = document.getElementById("compasso-b-input"); const numberOfBars = parseInt(barsInput.value, 10) || 1; const beatsPerBar = parseInt(compassoAInput.value, 10) || 4; const noteValue = parseInt(compassoBInput.value, 10) || 4; const subdivisions = Math.round(16 / noteValue); return numberOfBars * beatsPerBar * subdivisions; } /** * Garante que apenas números sejam inseridos em um campo de input. * @param {Event} event - O evento de input. */ export function enforceNumericInput(event) { event.target.value = event.target.value.replace(/[^0-9]/g, ""); } /** * Ajusta o valor de um elemento de input com base em um passo (step), * respeitando os limites de min/max definidos no elemento. * @param {HTMLInputElement} inputElement - O elemento de input a ser ajustado. * @param {number} step - O valor a ser adicionado (pode ser negativo). */ export function adjustValue(inputElement, step) { let currentValue = parseInt(inputElement.value, 10) || 0; let min = parseInt(inputElement.dataset.min, 10); let max = parseInt(inputElement.dataset.max, 10); let newValue = currentValue + step; if (!isNaN(min) && newValue < min) newValue = min; if (!isNaN(max) && newValue > max) newValue = max; inputElement.value = newValue; inputElement.dispatchEvent(new Event("input", { bubbles: true })); } // =============================== // LMMS ticks helpers (Song Editor) // =============================== export const LMMS_TICKS_PER_STEP = 12; // 1 step (1/16) = 12 ticks export const STEPS_PER_BAR = 16; // 4/4: 16 steps por compasso export const LMMS_TICKS_PER_BAR = LMMS_TICKS_PER_STEP * STEPS_PER_BAR; // 192 export function secondsToSongTicks(seconds, bpm = null) { const b = bpm ?? (parseFloat(document.getElementById("bpm-input")?.value) || 120); // stepIntervalSec = 60/(bpm*4) => ticksPerSec = 12/stepIntervalSec = bpm*0.8 const ticks = seconds * b * 0.8; return Math.round(ticks); } export function songTicksToSeconds(ticks, bpm = null) { const b = bpm ?? (parseFloat(document.getElementById("bpm-input")?.value) || 120); // seconds = ticks / (bpm*0.8) return ticks / (b * 0.8); } export function snapSongTicks(ticks, snap = LMMS_TICKS_PER_STEP) { const s = Math.max(1, Math.floor(snap)); return Math.round(ticks / s) * s; }