patterns funcionais, alguns nomes estão ao contrário. mas o conteúdo agora está correto e fidedigno ao projeto.

This commit is contained in:
JotaChina committed 2025-10-03 18:02:36 -03:00
1 parent 1174d71518
commit 6212750429
5 files changed
+365 -214

No files matched your search

+99 -51
View File
@@ -10,66 +10,111 @@ import { playSample } from "./audio.js";
import { getTotalSteps } from "./utils.js";
import { loadProjectFromServer } from "./file.js";
// Variável para armazenar o mapa de samples (nome do arquivo -> caminho completo)
let samplePathMap = {};
const globalPatternSelector = document.getElementById('global-pattern-selector');
globalPatternSelector.addEventListener('change', () => {
// Atualiza o índice GLOBAL
appState.activePatternIndex = parseInt(globalPatternSelector.value, 10);
const firstTrack = appState.tracks[0];
if (firstTrack) {
const activePattern = firstTrack.patterns[appState.activePatternIndex];
if (activePattern) {
const stepsPerBar = 16;
const requiredBars = Math.ceil(activePattern.steps.length / stepsPerBar);
document.getElementById("bars-input").value = requiredBars > 0 ? requiredBars : 1;
}
}
redrawSequencer();
});
export function updateGlobalPatternSelector() {
const referenceTrack = appState.tracks[0];
globalPatternSelector.innerHTML = '';
if (referenceTrack && referenceTrack.patterns.length > 0) {
referenceTrack.patterns.forEach((pattern, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = pattern.name;
globalPatternSelector.appendChild(option);
});
// Usa o índice GLOBAL
globalPatternSelector.selectedIndex = appState.activePatternIndex;
globalPatternSelector.disabled = false;
} else {
const option = document.createElement('option');
option.textContent = 'Sem patterns';
globalPatternSelector.appendChild(option);
globalPatternSelector.disabled = true;
}
}
// Função para exportar o mapa de samples, que estava faltando
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
if (key === "_isFile") continue;
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() {
const trackContainer = document.getElementById("track-container");
trackContainer.innerHTML = "";
appState.tracks.forEach((trackData) => {
const trackLane = document.createElement("div");
trackLane.className = "track-lane";
trackLane.dataset.trackId = trackData.id;
trackLane.innerHTML = `
<div class="track-info"><i class="fa-solid fa-gear"></i><div class="track-mute"></div><span class="track-name">${trackData.name}</span></div>
<div class="track-controls">
<div class="knob-container">
<div class="knob" data-control="volume" data-track-id="${trackData.id}">
<div class="knob-indicator"></div>
</div>
<span>VOL</span>
</div>
<div class="knob-container">
<div class="knob" data-control="pan" data-track-id="${trackData.id}">
<div class="knob-indicator"></div>
</div>
<span>PAN</span>
</div>
</div>
<div class="step-sequencer-wrapper"></div>
`;
if (trackData.id === appState.activeTrackId) {
trackLane.classList.add('active-track');
}
trackLane.addEventListener("dragover", (e) => {
e.preventDefault();
trackLane.classList.add("drag-over");
trackLane.innerHTML = `
<div class="track-info">
<i class="fa-solid fa-gear"></i>
<div class="track-mute"></div>
<span class="track-name">${trackData.name}</span>
</div>
<div class="track-controls">
<div class="knob-container">
<div class="knob" data-control="volume" data-track-id="${trackData.id}">
<div class="knob-indicator"></div>
</div>
<span>VOL</span>
</div>
<div class="knob-container">
<div class="knob" data-control="pan" data-track-id="${trackData.id}">
<div class="knob-indicator"></div>
</div>
<span>PAN</span>
</div>
</div>
<div class="step-sequencer-wrapper"></div>
`;
trackLane.addEventListener('click', () => {
if (appState.activeTrackId === trackData.id) return;
appState.activeTrackId = trackData.id;
document.querySelectorAll('.track-lane').forEach(lane => lane.classList.remove('active-track'));
trackLane.classList.add('active-track');
updateGlobalPatternSelector();
redrawSequencer();
});
trackLane.addEventListener("dragleave", () =>
trackLane.classList.remove("drag-over")
);
trackLane.addEventListener("dragover", (e) => { e.preventDefault(); trackLane.classList.add("drag-over"); });
trackLane.addEventListener("dragleave", () => trackLane.classList.remove("drag-over"));
trackLane.addEventListener("drop", (e) => {
e.preventDefault();
trackLane.classList.remove("drag-over");
@@ -80,25 +125,20 @@ export function renderApp() {
});
trackContainer.appendChild(trackLane);
const volumeKnob = trackLane.querySelector(".knob[data-control='volume']");
addKnobInteraction(volumeKnob);
updateKnobVisual(volumeKnob, "volume");
const panKnob = trackLane.querySelector(".knob[data-control='pan']");
addKnobInteraction(panKnob);
updateKnobVisual(panKnob, "pan");
});
updateGlobalPatternSelector();
redrawSequencer();
}
export function redrawSequencer() {
const beatsPerBar = parseInt(document.getElementById("compasso-a-input").value, 10) || 4;
const noteValue = parseInt(document.getElementById("compasso-b-input").value, 10) || 4;
const subdivisions = Math.round(16 / noteValue);
const stepsPerBar = beatsPerBar * subdivisions;
const totalSteps = getTotalSteps();
const totalGridSteps = getTotalSteps();
document.querySelectorAll(".step-sequencer-wrapper").forEach((wrapper) => {
let sequencerContainer = wrapper.querySelector(".step-sequencer");
if (!sequencerContainer) {
@@ -111,37 +151,45 @@ export function redrawSequencer() {
const trackId = parentTrackElement.dataset.trackId;
const trackData = appState.tracks.find((t) => t.id == trackId);
if (trackData && trackData.steps.length !== totalSteps) {
const newStepsState = new Array(totalSteps).fill(false);
for (let i = 0; i < Math.min(trackData.steps.length, totalSteps); i++) {
newStepsState[i] = trackData.steps[i];
}
trackData.steps = newStepsState;
if (!trackData || !trackData.patterns || trackData.patterns.length === 0) {
sequencerContainer.innerHTML = "";
return;
}
// A CORREÇÃO FINAL: Usa o índice GLOBAL para desenhar CADA faixa
const activePattern = trackData.patterns[appState.activePatternIndex];
if (!activePattern) {
sequencerContainer.innerHTML = "";
return;
}
const patternSteps = activePattern.steps;
sequencerContainer.innerHTML = "";
for (let i = 0; i < totalSteps; i++) {
for (let i = 0; i < totalGridSteps; i++) {
const stepWrapper = document.createElement("div");
stepWrapper.className = "step-wrapper";
const stepElement = document.createElement("div");
stepElement.className = "step";
if (trackData && trackData.steps[i] === true) {
if (patternSteps[i] === true) {
stepElement.classList.add("active");
}
stepElement.addEventListener("click", () => {
toggleStepState(trackData.id, i);
toggleStepState(trackData.id, i);
stepElement.classList.toggle("active");
if (trackData && trackData.samplePath) {
playSample(trackData.samplePath, trackData.id);
}
});
const beatsPerBar = parseInt(document.getElementById("compasso-a-input").value, 10) || 4;
const groupIndex = Math.floor(i / beatsPerBar);
if (groupIndex % 2 === 0) {
stepElement.classList.add("step-dark");
}
const stepsPerBar = 16;
if (i > 0 && i % stepsPerBar === 0) {
const marker = document.createElement("div");
marker.className = "step-marker";
@@ -275,7 +323,6 @@ export async function loadAndRenderSampleBrowser() {
samplePathMap = {};
buildSamplePathMap(fileTree, "src/samples");
console.log("Mapa de samples construído:", samplePathMap);
renderFileTree(fileTree, browserContent, "src/samples");
} catch (error) {
@@ -294,6 +341,7 @@ function renderFileTree(tree, parentElement, currentPath) {
return aIsFile ? 1 : -1;
});
for (const key of sortedKeys) {
if (key === '_isFile') continue;
const node = tree[key];
const li = document.createElement("li");
const newPath = `${currentPath}/${key}`;