melhorando a leitura de projetos no mmpCreator

This commit is contained in:
JotaChina committed 2025-12-25 14:54:14 -03:00
1 parent 96f74552e5
commit 9f06aed297
3 files changed
+88 -11

No files matched your search

+80 -2
View File
@@ -320,7 +320,7 @@ function schedulePianoRoll() {
}
// =========================================================================
// Renderizar o Pattern atual para um Blob de Áudio (MANTIDO ORIGINAL)
// Renderizar o Pattern atual para um Blob de Áudio
// =========================================================================
export async function renderActivePatternToBlob() {
@@ -477,7 +477,7 @@ export async function renderActivePatternToBlob() {
}
// =========================================================================
// FUNÇÃO UTILITÁRIA: Converte AudioBuffer para Blob WAV (MANTIDO ORIGINAL)
// FUNÇÃO UTILITÁRIA: Converte AudioBuffer para Blob WAV
// =========================================================================
function bufferToWave(abuffer) {
@@ -539,3 +539,81 @@ function bufferToWave(abuffer) {
return new Blob([buffer], { type: "audio/wav" });
}
// ===============================
// Song/Playlist Pattern Scheduler
// (toca patterns arranjadas na Playlist)
// ===============================
const LMMS_TICKS_PER_STEP = 12;
let songPatternScheduleId = null;
export function startSongPatternPlaybackOnTransport() {
initializeAudioContext();
if (songPatternScheduleId !== null) return;
songPatternScheduleId = Tone.Transport.scheduleRepeat((time) => {
// bpm atual
const bpm = parseInt(document.getElementById("bpm-input")?.value, 10) || 120;
const stepIntervalSec = 60 / (bpm * 4);
// step absoluto do song (considera seek do Transport)
const songStep = Math.floor(Tone.Transport.seconds / stepIntervalSec + 1e-6);
const songTick = songStep * LMMS_TICKS_PER_STEP;
// quais patterns (colunas) estão ativas neste tick?
const basslineTracks = appState.pattern.tracks.filter(
(t) => t.type === "bassline" && !t.isMuted
);
const activePatternHits = [];
for (const b of basslineTracks) {
const clips = b.playlist_clips || [];
const clip = clips.find((c) => songTick >= c.pos && songTick < c.pos + c.len);
if (!clip) continue;
const localStep = Math.floor((songTick - clip.pos) / LMMS_TICKS_PER_STEP);
activePatternHits.push({ patternIndex: b.patternIndex, localStep });
}
if (activePatternHits.length === 0) return;
// dispara instrumentos reais (samplers/plugins)
for (const track of appState.pattern.tracks) {
if (track.type === "bassline") continue;
if (track.muted) continue;
for (const hit of activePatternHits) {
const patt = track.patterns?.[hit.patternIndex];
if (!patt?.steps) continue;
if (patt.steps[hit.localStep]) {
// SAMPLER
if (track.type === "sampler" && track.player) {
track.player.restart = true; // baterias precisam retrigger
try {
track.player.start(time);
} catch {}
}
// PLUGIN (step sem piano roll)
else if (track.type === "plugin" && track.instrument) {
const hasNotes = patt.notes && patt.notes.length > 0;
if (!hasNotes) {
try {
track.instrument.triggerAttackRelease("C5", "16n", time);
} catch {}
}
}
}
}
}
}, "16n");
}
export function stopSongPatternPlaybackOnTransport() {
if (songPatternScheduleId === null) return;
try {
Tone.Transport.clear(songPatternScheduleId);
} catch {}
songPatternScheduleId = null;
}