Arrumando botões de volume e pan, criação de novos compassos.
This commit is contained in:
1 parent
e776a830c8
commit
e10621da17
8 files changed
+411
-291
No files matched your search
+183
-150
@@ -8,7 +8,32 @@ import {
|
||||
} from "./state.js";
|
||||
import { playSample } from "./audio.js";
|
||||
import { getTotalSteps } from "./utils.js";
|
||||
import { loadProjectFromServer } from "./file.js"; // (CORREÇÃO) Importa a função que faltava
|
||||
import { loadProjectFromServer } from "./file.js";
|
||||
|
||||
// Variável para armazenar o mapa de samples (nome do arquivo -> caminho completo)
|
||||
let samplePathMap = {};
|
||||
|
||||
// Função para exportar o mapa de samples para outros módulos
|
||||
export function getSamplePathMap() {
|
||||
return samplePathMap;
|
||||
}
|
||||
|
||||
// Função recursiva para construir o mapa de samples a partir do manifest
|
||||
function buildSamplePathMap(tree, currentPath) {
|
||||
for (const key in tree) {
|
||||
if (key === "_isFile") continue; // Ignora a propriedade de metadados
|
||||
|
||||
const node = tree[key];
|
||||
const newPath = `${currentPath}/${key}`;
|
||||
if (node._isFile) {
|
||||
// Se for um arquivo, adiciona ao mapa
|
||||
samplePathMap[key] = newPath;
|
||||
} else {
|
||||
// Se for um diretório, continua a busca recursivamente
|
||||
buildSamplePathMap(node, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RENDERIZAÇÃO PRINCIPAL
|
||||
export function renderApp() {
|
||||
@@ -116,94 +141,97 @@ export function redrawSequencer() {
|
||||
|
||||
// LÓGICA DE INTERAÇÃO DOS KNOBS
|
||||
function addKnobInteraction(knobElement) {
|
||||
const controlType = knobElement.dataset.control;
|
||||
knobElement.addEventListener("mousedown", (e) => {
|
||||
if (e.button === 1) {
|
||||
const controlType = knobElement.dataset.control;
|
||||
knobElement.addEventListener("mousedown", (e) => {
|
||||
if (e.button === 1) { // Middle mouse button
|
||||
e.preventDefault();
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const defaultValue = controlType === "volume" ? 0.8 : 0.0;
|
||||
if (controlType === "volume") {
|
||||
updateTrackVolume(trackId, defaultValue);
|
||||
} else {
|
||||
updateTrackPan(trackId, defaultValue);
|
||||
}
|
||||
updateKnobVisual(knobElement, controlType);
|
||||
}
|
||||
});
|
||||
knobElement.addEventListener("mousedown", (e) => {
|
||||
if (e.button !== 0) return; // Apenas botão esquerdo
|
||||
e.preventDefault();
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const defaultValue = controlType === "volume" ? 0.8 : 0.0;
|
||||
if (controlType === "volume") {
|
||||
updateTrackVolume(trackId, defaultValue);
|
||||
} else {
|
||||
updateTrackPan(trackId, defaultValue);
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
const startY = e.clientY;
|
||||
const startValue = controlType === "volume" ? track.volume : track.pan;
|
||||
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") {
|
||||
updateTrackVolume(trackId, newValue);
|
||||
} else {
|
||||
updateTrackPan(trackId, newValue);
|
||||
}
|
||||
updateKnobVisual(knobElement, controlType);
|
||||
}
|
||||
}
|
||||
});
|
||||
knobElement.addEventListener("mousedown", (e) => {
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
const startY = e.clientY;
|
||||
const startValue = controlType === "volume" ? track.volume : track.pan;
|
||||
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;
|
||||
function onMouseUp() {
|
||||
document.body.classList.remove("knob-dragging");
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
document.removeEventListener("mouseup", onMouseUp);
|
||||
}
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
knobElement.addEventListener("wheel", (e) => {
|
||||
e.preventDefault();
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
const step = 0.05;
|
||||
const direction = e.deltaY < 0 ? 1 : -1;
|
||||
if (controlType === "volume") {
|
||||
const newValue = track.volume + direction * step;
|
||||
updateTrackVolume(trackId, newValue);
|
||||
} else {
|
||||
const newValue = track.pan + direction * step;
|
||||
updateTrackPan(trackId, newValue);
|
||||
}
|
||||
}
|
||||
function onMouseUp() {
|
||||
document.body.classList.remove("knob-dragging");
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
document.removeEventListener("mouseup", onMouseUp);
|
||||
}
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
knobElement.addEventListener("wheel", (e) => {
|
||||
e.preventDefault();
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
const step = 0.05;
|
||||
const direction = e.deltaY < 0 ? 1 : -1;
|
||||
if (controlType === "volume") {
|
||||
const newValue = track.volume + direction * step;
|
||||
updateTrackVolume(trackId, newValue);
|
||||
} else {
|
||||
const newValue = track.pan + direction * step;
|
||||
updateTrackPan(trackId, newValue);
|
||||
}
|
||||
});
|
||||
updateKnobVisual(knobElement, controlType);
|
||||
});
|
||||
}
|
||||
|
||||
function updateKnobVisual(knobElement, controlType) {
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
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 = track.volume;
|
||||
const clampedValue = Math.max(0, Math.min(1.5, value));
|
||||
percentage = clampedValue / 1.5;
|
||||
title = `Volume: ${Math.round(clampedValue * 100)}%`;
|
||||
} else {
|
||||
const value = track.pan;
|
||||
const clampedValue = Math.max(-1, Math.min(1, value));
|
||||
percentage = (clampedValue + 1) / 2;
|
||||
const panDisplay = Math.round(clampedValue * 100);
|
||||
title = `Pan: ${
|
||||
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;
|
||||
const trackId = knobElement.dataset.trackId;
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (!track) return;
|
||||
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 = track.volume;
|
||||
const clampedValue = Math.max(0, Math.min(1.5, value));
|
||||
percentage = clampedValue / 1.5;
|
||||
title = `Volume: ${Math.round(clampedValue * 100)}%`;
|
||||
} else {
|
||||
const value = track.pan;
|
||||
const clampedValue = Math.max(-1, Math.min(1, value));
|
||||
percentage = (clampedValue + 1) / 2;
|
||||
const panDisplay = Math.round(clampedValue * 100);
|
||||
title = `Pan: ${
|
||||
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;
|
||||
}
|
||||
|
||||
export function highlightStep(stepIndex, isActive) {
|
||||
@@ -230,6 +258,11 @@ export async function loadAndRenderSampleBrowser() {
|
||||
throw new Error("Arquivo samples-manifest.json não encontrado.");
|
||||
}
|
||||
const fileTree = await response.json();
|
||||
|
||||
samplePathMap = {};
|
||||
buildSamplePathMap(fileTree, "src/samples");
|
||||
console.log("Mapa de samples construído:", samplePathMap);
|
||||
|
||||
renderFileTree(fileTree, browserContent, "src/samples");
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar samples:", error);
|
||||
@@ -238,82 +271,82 @@ export async function loadAndRenderSampleBrowser() {
|
||||
}
|
||||
|
||||
function renderFileTree(tree, parentElement, currentPath) {
|
||||
parentElement.innerHTML = "";
|
||||
const ul = document.createElement("ul");
|
||||
const sortedKeys = Object.keys(tree).sort((a, b) => {
|
||||
const aIsFile = tree[a]._isFile;
|
||||
const bIsFile = tree[b]._isFile;
|
||||
if (aIsFile === bIsFile) return a.localeCompare(b);
|
||||
return aIsFile ? 1 : -1;
|
||||
});
|
||||
for (const key of sortedKeys) {
|
||||
const node = tree[key];
|
||||
const li = document.createElement("li");
|
||||
const newPath = `${currentPath}/${key}`;
|
||||
if (node._isFile) {
|
||||
li.innerHTML = `<i class="fa-solid fa-file-audio"></i> ${key}`;
|
||||
li.setAttribute("draggable", true);
|
||||
li.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
playSample(newPath, null);
|
||||
});
|
||||
li.addEventListener("dragstart", (e) => {
|
||||
e.dataTransfer.setData("text/plain", newPath);
|
||||
e.dataTransfer.effectAllowed = "copy";
|
||||
});
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
li.className = "directory";
|
||||
li.innerHTML = `<i class="fa-solid fa-folder"></i> ${key}`;
|
||||
const nestedUl = document.createElement("ul");
|
||||
renderFileTree(node, nestedUl, newPath);
|
||||
li.appendChild(nestedUl);
|
||||
li.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
li.classList.toggle("open");
|
||||
});
|
||||
ul.appendChild(li);
|
||||
parentElement.innerHTML = "";
|
||||
const ul = document.createElement("ul");
|
||||
const sortedKeys = Object.keys(tree).sort((a, b) => {
|
||||
const aIsFile = tree[a]._isFile;
|
||||
const bIsFile = tree[b]._isFile;
|
||||
if (aIsFile === bIsFile) return a.localeCompare(b);
|
||||
return aIsFile ? 1 : -1;
|
||||
});
|
||||
for (const key of sortedKeys) {
|
||||
const node = tree[key];
|
||||
const li = document.createElement("li");
|
||||
const newPath = `${currentPath}/${key}`;
|
||||
if (node._isFile) {
|
||||
li.innerHTML = `<i class="fa-solid fa-file-audio"></i> ${key}`;
|
||||
li.setAttribute("draggable", true);
|
||||
li.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
playSample(newPath, null);
|
||||
});
|
||||
li.addEventListener("dragstart", (e) => {
|
||||
e.dataTransfer.setData("text/plain", newPath);
|
||||
e.dataTransfer.effectAllowed = "copy";
|
||||
});
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
li.className = "directory";
|
||||
li.innerHTML = `<i class="fa-solid fa-folder"></i> ${key}`;
|
||||
const nestedUl = document.createElement("ul");
|
||||
renderFileTree(node, nestedUl, newPath);
|
||||
li.appendChild(nestedUl);
|
||||
li.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
li.classList.toggle("open");
|
||||
});
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
parentElement.appendChild(ul);
|
||||
parentElement.appendChild(ul);
|
||||
}
|
||||
|
||||
export async function showOpenProjectModal() {
|
||||
const openProjectModal = document.getElementById("open-project-modal");
|
||||
const serverProjectsList = document.getElementById("server-projects-list");
|
||||
serverProjectsList.innerHTML = "<p>Carregando...</p>";
|
||||
openProjectModal.classList.add("visible");
|
||||
try {
|
||||
const response = await fetch("metadata/mmp-manifest.json");
|
||||
if (!response.ok)
|
||||
throw new Error("Arquivo mmp-manifest.json não encontrado.");
|
||||
const projects = await response.json();
|
||||
|
||||
serverProjectsList.innerHTML = "";
|
||||
if (projects.length === 0) {
|
||||
serverProjectsList.innerHTML =
|
||||
'<p style="color:var(--text-dark);">Nenhum projeto encontrado no servidor.</p>';
|
||||
}
|
||||
|
||||
projects.forEach((projectName) => {
|
||||
const projectItem = document.createElement("div");
|
||||
projectItem.className = "project-item";
|
||||
projectItem.textContent = projectName;
|
||||
projectItem.addEventListener("click", async () => {
|
||||
const success = await loadProjectFromServer(projectName);
|
||||
if (success) {
|
||||
closeOpenProjectModal();
|
||||
}
|
||||
const openProjectModal = document.getElementById("open-project-modal");
|
||||
const serverProjectsList = document.getElementById("server-projects-list");
|
||||
serverProjectsList.innerHTML = "<p>Carregando...</p>";
|
||||
openProjectModal.classList.add("visible");
|
||||
try {
|
||||
const response = await fetch("metadata/mmp-manifest.json");
|
||||
if (!response.ok)
|
||||
throw new Error("Arquivo mmp-manifest.json não encontrado.");
|
||||
const projects = await response.json();
|
||||
|
||||
serverProjectsList.innerHTML = "";
|
||||
if (projects.length === 0) {
|
||||
serverProjectsList.innerHTML =
|
||||
'<p style="color:var(--text-dark);">Nenhum projeto encontrado no servidor.</p>';
|
||||
}
|
||||
|
||||
projects.forEach((projectName) => {
|
||||
const projectItem = document.createElement("div");
|
||||
projectItem.className = "project-item";
|
||||
projectItem.textContent = projectName;
|
||||
projectItem.addEventListener("click", async () => {
|
||||
const success = await loadProjectFromServer(projectName);
|
||||
if (success) {
|
||||
closeOpenProjectModal();
|
||||
}
|
||||
});
|
||||
serverProjectsList.appendChild(projectItem);
|
||||
});
|
||||
serverProjectsList.appendChild(projectItem);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar lista de projetos:", error);
|
||||
serverProjectsList.innerHTML = `<p style="color:var(--accent-red);">${error.message}</p>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar lista de projetos:", error);
|
||||
serverProjectsList.innerHTML = `<p style="color:var(--accent-red);">${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function closeOpenProjectModal() {
|
||||
const openProjectModal = document.getElementById("open-project-modal");
|
||||
openProjectModal.classList.remove("visible");
|
||||
}
|
||||
const openProjectModal = document.getElementById("open-project-modal");
|
||||
openProjectModal.classList.remove("visible");
|
||||
}
|
||||
Reference in new issue
Block a user