melhorando a leitura de projetos no mmpCreator
Deploy / Deploy (push) Successful in 2m7s
Details
Deploy / Deploy (push) Successful in 2m7s
Details
This commit is contained in:
parent
9b73c3ba33
commit
8d2380c704
|
|
@ -10,6 +10,11 @@ import {
|
|||
getAudioContext,
|
||||
getMainGainNode,
|
||||
} from "../audio.js";
|
||||
import {
|
||||
startSongPatternPlaybackOnTransport,
|
||||
stopSongPatternPlaybackOnTransport,
|
||||
} from "../pattern/pattern_audio.js";
|
||||
|
||||
import { getPixelsPerSecond } from "../utils.js";
|
||||
// 🔊 ADIÇÃO: usar a MESMA instância do Tone que o projeto usa
|
||||
import * as Tone from "https://esm.sh/tone";
|
||||
|
|
@ -313,10 +318,6 @@ export async function startAudioEditorPlayback(seekTime) {
|
|||
// alinhamento de relógio próprio (mantido para o seu scheduler)
|
||||
startTime = audioCtx.currentTime;
|
||||
|
||||
// =================================================================
|
||||
// 👇 INÍCIO DA CORREÇÃO (Bugs 1 & 2)
|
||||
// =================================================================
|
||||
|
||||
// 1. Determine o tempo de início:
|
||||
let timeToStart =
|
||||
seekTime !== null && seekTime !== undefined && !isNaN(seekTime)
|
||||
|
|
@ -334,16 +335,13 @@ export async function startAudioEditorPlayback(seekTime) {
|
|||
Tone.Transport.seconds = timeToStart; // 👈 Usa o tempo sincronizado
|
||||
} catch {}
|
||||
|
||||
// =================================================================
|
||||
// 👆 FIM DA CORREÇÃO
|
||||
// =================================================================
|
||||
|
||||
updateTransportLoop();
|
||||
|
||||
console.log("%cIniciando Playback...", "color: #3498db;");
|
||||
|
||||
// inicia o Transport (para disparar os Players .sync())
|
||||
try {
|
||||
startSongPatternPlaybackOnTransport();
|
||||
Tone.Transport.start();
|
||||
} catch {}
|
||||
|
||||
|
|
@ -366,6 +364,7 @@ export function stopAudioEditorPlayback(rewind = false) {
|
|||
|
||||
// para o Transport (para Players .sync())
|
||||
try {
|
||||
stopSongPatternPlaybackOnTransport();
|
||||
Tone.Transport.stop();
|
||||
} catch {}
|
||||
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ export async function parseMmpContent(xmlString) {
|
|||
|
||||
const firstInst = newTracks.find((t) => t.type !== "bassline");
|
||||
appState.pattern.activeTrackId = firstInst ? firstInst.id : null;
|
||||
//appState.pattern.activePatternIndex = 0;
|
||||
appState.pattern.activePatternIndex = 0;
|
||||
|
||||
loadStateFromSession();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue