diff --git a/assets/js/creations/pattern/pattern_state.js b/assets/js/creations/pattern/pattern_state.js index 3ec6fdff..c25c6c90 100755 --- a/assets/js/creations/pattern/pattern_state.js +++ b/assets/js/creations/pattern/pattern_state.js @@ -203,61 +203,60 @@ export async function loadAudioForTrack(track) { export function addTrackToState() { const totalSteps = getTotalSteps(); - // ✅ define o "pai" correto pra UI conseguir renderizar - const focusedId = appState.pattern.focusedBasslineId || null; + const all = appState.pattern.tracks || []; + const nonBass = all.filter(t => t.type !== "bassline"); - let parentBasslineId = null; - if (focusedId) { - const basslineTrack = appState.pattern.tracks.find( - (t) => t.type === "bassline" && t.id === focusedId - ); - // mesmo critério do pattern_ui: srcId = instrumentSourceId || focusedId - parentBasslineId = basslineTrack?.instrumentSourceId || focusedId; - } else { - parentBasslineId = null; // IMPORTANTÍSSIMO: não deixar undefined - } - - // ✅ pega referência do mesmo pai (pra clonar patterns compatíveis) + // Prefere usar como referência um instrumento do rack raiz que já tenha patterns const referenceTrack = - appState.pattern.tracks.find( - (t) => - t.type !== "bassline" && - (t.parentBasslineId ?? null) === parentBasslineId && - Array.isArray(t.patterns) && - t.patterns.length > 0 - ) || - appState.pattern.tracks.find( - (t) => t.type !== "bassline" && Array.isArray(t.patterns) && t.patterns.length > 0 - ) || + nonBass.find(t => t.parentBasslineId == null && Array.isArray(t.patterns) && t.patterns.length) || + nonBass.find(t => Array.isArray(t.patterns) && t.patterns.length) || null; - const newId = Date.now() + Math.random(); + // Quantos patterns existem no projeto? (pega o máximo entre instrumentos) + const patternCount = Math.max( + 1, + ...nonBass.map(t => Array.isArray(t.patterns) ? t.patterns.length : 0) + ); + const patterns = Array.from({ length: patternCount }, (_, i) => { + const ref = referenceTrack?.patterns?.[i]; + + const refStepsLen = Array.isArray(ref?.steps) ? ref.steps.length : totalSteps; + const name = String(ref?.name || `Pattern ${i + 1}`); + const pos = Number.isFinite(Number(ref?.pos)) ? Number(ref.pos) : i * 192; // 1 bar = 192 ticks no LMMS + + return { + name, + steps: new Array(refStepsLen).fill(false), + notes: [], + pos + }; + }); + + const rootCount = nonBass.filter(t => t.parentBasslineId == null).length; + + const newId = Date.now() + Math.random(); const newTrack = { id: newId, - name: `Novo Instrumento ${appState.pattern.tracks.filter(t => t.type !== "bassline").length + 1}`, + name: `Novo Instrumento ${rootCount + 1}`, samplePath: null, type: "plugin", - // ✅ AQUI a chave: agora a track entra no lugar certo (rack ou root) - parentBasslineId, + // ✅ Rack raiz + parentBasslineId: null, + // instrumento default instrumentName: "kicker", instrumentXml: DEFAULT_KICKER_XML, player: null, buffer: null, + instrument: null, + previewPlayer: null, - patterns: referenceTrack - ? referenceTrack.patterns.map((p) => ({ - name: p.name, - steps: new Array((p.steps?.length || totalSteps)).fill(false), - notes: [], - pos: p.pos, - })) - : [{ name: "Pattern 1", steps: new Array(totalSteps).fill(false), notes: [], pos: 0 }], + patterns, + activePatternIndex: appState.pattern.activePatternIndex ?? 0, - activePatternIndex: 0, volume: DEFAULT_VOLUME, pan: DEFAULT_PAN, volumeNode: new Tone.Volume(Tone.gainToDb(DEFAULT_VOLUME)), @@ -272,10 +271,9 @@ export function addTrackToState() { appState.pattern.tracks.push(newTrack); appState.pattern.activeTrackId = newTrack.id; - console.log("Faixa adicionada ao estado com Kicker padrão."); + console.log("✅ Instrumento adicionado no rack raiz:", newTrack.name); } - export function removeTrackById(trackId) { const index = appState.pattern.tracks.findIndex(t => t.id === trackId); diff --git a/assets/js/creations/pattern/pattern_ui.js b/assets/js/creations/pattern/pattern_ui.js index 79c60638..b1e47e54 100755 --- a/assets/js/creations/pattern/pattern_ui.js +++ b/assets/js/creations/pattern/pattern_ui.js @@ -19,26 +19,28 @@ export function renderPatternEditor() { let contextName = "Song Editor"; // Nome da visualização atual if (appState.pattern.focusedBasslineId) { - isFocusedMode = true; + isFocusedMode = true; - // 1) acha o container (Caixa/Kick/...) focado - const basslineTrack = appState.pattern.tracks.find( - t => t.id === appState.pattern.focusedBasslineId && t.type === "bassline" - ); + // 1) acha o container (Caixa/Kick/...) focado + const basslineTrack = appState.pattern.tracks.find( + t => t.id === appState.pattern.focusedBasslineId && t.type === "bassline" + ); - // 2) usa o rack real como "pai" (fallback: id do próprio container) - const srcId = basslineTrack?.instrumentSourceId || appState.pattern.focusedBasslineId; + // 2) usa o rack real como "pai" (fallback: id do próprio container) + const srcId = basslineTrack?.instrumentSourceId || appState.pattern.focusedBasslineId; - // 3) mostra somente instrumentos pertencentes ao rack - tracksToRender = appState.pattern.tracks.filter( - t => t.type !== "bassline" && t.parentBasslineId === srcId - ); + // 3) mostra somente instrumentos pertencentes ao rack + tracksToRender = appState.pattern.tracks.filter( + t => t.type !== "bassline" && (t.parentBasslineId === srcId || t.parentBasslineId == null) + ); - // Nome no header - if (basslineTrack) contextName = basslineTrack.name; + // Nome no header + if (basslineTrack) contextName = basslineTrack.name; } else { // Modo Padrão: Mostra trilhas da raiz (sem pai) e que NÃO são containers de bassline - tracksToRender = appState.pattern.tracks.filter(t => t.type !== 'bassline' && t.parentBasslineId === null); + tracksToRender = appState.pattern.tracks.filter( + t => t.type !== "bassline" && t.parentBasslineId == null + ); // Fallback: Se a lista ficar vazia (projeto antigo ou mal formatado), mostra tudo que não é container if (tracksToRender.length === 0) {