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:
1 parent
1174d71518
commit
6212750429
5 files changed
+365
-214
No files matched your search
+99
-51
@@ -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}`;
|
||||
|
||||
Reference in new issue
Block a user