inserindo server flask de upload e watchdog para verificar alterações em samples
This commit is contained in:
1 parent
c35f3233b7
commit
8c391412e1
6 files changed
+450
-177
No files matched your search
+79
-31
@@ -6,33 +6,35 @@ import {
|
||||
updateTrackVolume,
|
||||
updateTrackPan,
|
||||
} from "./state.js";
|
||||
import { playSample } from "./audio.js";
|
||||
import { playSample, stopPlayback } from "./audio.js";
|
||||
import { getTotalSteps } from "./utils.js";
|
||||
import { loadProjectFromServer } from "./file.js";
|
||||
|
||||
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;
|
||||
if (globalPatternSelector) {
|
||||
globalPatternSelector.addEventListener('change', () => {
|
||||
// A linha stopPlayback() foi REMOVIDA daqui, permitindo a troca em tempo real.
|
||||
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();
|
||||
});
|
||||
redrawSequencer();
|
||||
});
|
||||
}
|
||||
|
||||
export function updateGlobalPatternSelector() {
|
||||
if (!globalPatternSelector) return;
|
||||
const referenceTrack = appState.tracks[0];
|
||||
globalPatternSelector.innerHTML = '';
|
||||
|
||||
if (referenceTrack && referenceTrack.patterns.length > 0) {
|
||||
referenceTrack.patterns.forEach((pattern, index) => {
|
||||
const option = document.createElement('option');
|
||||
@@ -40,7 +42,6 @@ export function updateGlobalPatternSelector() {
|
||||
option.textContent = pattern.name;
|
||||
globalPatternSelector.appendChild(option);
|
||||
});
|
||||
// Usa o índice GLOBAL
|
||||
globalPatternSelector.selectedIndex = appState.activePatternIndex;
|
||||
globalPatternSelector.disabled = false;
|
||||
} else {
|
||||
@@ -51,6 +52,42 @@ export function updateGlobalPatternSelector() {
|
||||
}
|
||||
}
|
||||
|
||||
export function handleSampleUpload(file) {
|
||||
const validExtensions = ['.wav', '.flac', '.ogg', '.mp3'];
|
||||
const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
||||
|
||||
if (!validExtensions.includes(fileExtension)) {
|
||||
alert("Formato de arquivo inválido. Por favor, envie .wav, .flac, .ogg, ou .mp3.");
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const dataURL = e.target.result;
|
||||
const browserContent = document.getElementById("browser-content");
|
||||
const list = browserContent.querySelector("ul");
|
||||
|
||||
if (list) {
|
||||
const li = document.createElement("li");
|
||||
li.innerHTML = `<i class="fa-solid fa-file-audio"></i> ${file.name}`;
|
||||
li.setAttribute("draggable", true);
|
||||
|
||||
li.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
playSample(dataURL, null);
|
||||
});
|
||||
|
||||
li.addEventListener("dragstart", (event) => {
|
||||
event.dataTransfer.setData("text/plain", dataURL);
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
});
|
||||
|
||||
list.prepend(li);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
export function getSamplePathMap() {
|
||||
return samplePathMap;
|
||||
}
|
||||
@@ -89,15 +126,11 @@ export function renderApp() {
|
||||
</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>
|
||||
<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>
|
||||
<div class="knob" data-control="pan" data-track-id="${trackData.id}"><div class="knob-indicator"></div></div>
|
||||
<span>PAN</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,11 +139,25 @@ export function renderApp() {
|
||||
|
||||
trackLane.addEventListener('click', () => {
|
||||
if (appState.activeTrackId === trackData.id) return;
|
||||
|
||||
// A linha stopPlayback() também foi REMOVIDA daqui
|
||||
|
||||
appState.activeTrackId = trackData.id;
|
||||
document.querySelectorAll('.track-lane').forEach(lane => lane.classList.remove('active-track'));
|
||||
trackLane.classList.add('active-track');
|
||||
updateGlobalPatternSelector();
|
||||
redrawSequencer();
|
||||
|
||||
// Apenas redesenha a UI, sem parar a música
|
||||
const activeTrack = appState.tracks.find(t => t.id === appState.activeTrackId);
|
||||
if (activeTrack) {
|
||||
const activePattern = activeTrack.patterns[activeTrack.activePatternIndex];
|
||||
if (activePattern) {
|
||||
const stepsPerBar = 16;
|
||||
const requiredBars = Math.ceil(activePattern.steps.length / stepsPerBar);
|
||||
document.getElementById("bars-input").value = requiredBars > 0 ? requiredBars : 1;
|
||||
redrawSequencer();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
trackLane.addEventListener("dragover", (e) => { e.preventDefault(); trackLane.classList.add("drag-over"); });
|
||||
@@ -152,15 +199,12 @@ export function redrawSequencer() {
|
||||
const trackData = appState.tracks.find((t) => t.id == trackId);
|
||||
|
||||
if (!trackData || !trackData.patterns || trackData.patterns.length === 0) {
|
||||
sequencerContainer.innerHTML = "";
|
||||
return;
|
||||
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;
|
||||
sequencerContainer.innerHTML = ""; return;
|
||||
}
|
||||
const patternSteps = activePattern.steps;
|
||||
|
||||
@@ -315,7 +359,11 @@ export function highlightStep(stepIndex, isActive) {
|
||||
export async function loadAndRenderSampleBrowser() {
|
||||
const browserContent = document.getElementById("browser-content");
|
||||
try {
|
||||
const response = await fetch("metadata/samples-manifest.json");
|
||||
// --- CORREÇÃO AQUI ---
|
||||
// Adiciona um timestamp à URL para evitar que o navegador use uma versão antiga (em cache) do arquivo.
|
||||
const response = await fetch(`metadata/samples-manifest.json?v=${Date.now()}`);
|
||||
// --- FIM DA CORREÇÃO ---
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Arquivo samples-manifest.json não encontrado.");
|
||||
}
|
||||
@@ -323,7 +371,7 @@ export async function loadAndRenderSampleBrowser() {
|
||||
|
||||
samplePathMap = {};
|
||||
buildSamplePathMap(fileTree, "src/samples");
|
||||
|
||||
|
||||
renderFileTree(fileTree, browserContent, "src/samples");
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar samples:", error);
|
||||
|
||||
Reference in new issue
Block a user