MMPCreator
Deploy / Deploy (push) Successful in 1m5s Details

This commit is contained in:
JotaChina 2025-08-29 16:38:40 -03:00
parent ad814d8fde
commit a6c5443503
1028 changed files with 51341 additions and 218 deletions

View File

@ -7,7 +7,8 @@
<a href="{{ site.baseurl }}projetosPorTrack/">📁 Projetos por tipo de track</a>
<a href="{{ site.baseurl }}beatsDisponiveis/">🎧 Beats Disponíveis</a>
<a href="{{ site.baseurl }}mmp_pages/">🎼 Projetos</a>
<a href="{{ site.baseurl }}">🔎 Buscas</a>
<a href="{{ site.baseurl }}">🔎 Buscas</a>´
<a href="creation.html">🧠 Crie seu projeto</a>
<a href="{{ site.baseurl }}about/"> Sobre</a>
</div>
</div>

View File

@ -7,6 +7,7 @@
<a href="{{ site.baseurl }}beatsDisponiveis/" style="text-decoration: none; color: #333;">🎧 Beats Disponíveis</a>
<a href="{{ site.baseurl }}mmp_pages/" style="text-decoration: none; color: #333;">🎼 Projetos Disponíveis</a>
<a href="{{ site.baseurl }}" style="text-decoration: none; color: #333;">🔎 Buscas</a>
<a href="creation.html" style="text-decoration: none; color: #333;">🧠 Crie seu projeto</a>
<a href="{{ site.baseurl }}about/" style="text-decoration: none; color: #333;"> Sobre</a>
</nav>
</div>

View File

@ -119,8 +119,8 @@ title: "{{ page.file }}"
<div class="audio-player-section mt-3">
<audio controls>
<!-- O caminho, aparentemente, está correto. mas não está emitindo sons -->
<source src="{{ '/mmp/instruments/lmms/' | append: audiofileprocessor.src | relative_url }}" type="audio/wav">
<source src="{{ '/mmp/instruments/lmms/' | append: audiofileprocessor.src | relative_url }}" type="audio/ogg">
<source src="{{ '/mmp/instruments/lmms/' | append: instrument.audiofileprocessor.src | relative_url }}" type="audio/wav">
<source src="{{ '/mmp/instruments/lmms/' | append: instrument.audiofileprocessor.src | relative_url }}" type="audio/ogg">
Seu navegador não suporta o elemento <code>audio</code>.
</audio>
</div>

View File

@ -6,7 +6,7 @@
background-color: #f4f4f4;
padding: 1rem;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
max-width: 100%;
box-sizing: border-box;
}
@ -40,4 +40,3 @@
padding: 0.3rem 0.6rem;
}
}

View File

@ -1,33 +1,32 @@
.sidebar-center {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #f4f4f4;
padding: 2rem 1.5rem;
border-radius: 12px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
z-index: 1000;
}
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #f4f4f4;
padding: 2rem 1.5rem;
border-radius: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.submenu {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
}
.submenu {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
}
.submenu a {
text-decoration: none;
color: #333;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 6px;
transition: background 0.2s;
white-space: nowrap;
}
.submenu a:hover {
background-color: #e0e0e0;
}
.submenu a {
text-decoration: none;
color: #333;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 6px;
transition: background 0.2s;
white-space: nowrap;
}
.submenu a:hover {
background-color: #e0e0e0;
}

703
assets/css/style.css Normal file
View File

@ -0,0 +1,703 @@
/* =============================================== */
/* VÁRIAVEIS GLOBAIS (ROOT)
/* =============================================== */
:root {
--bg-body: #2d3035;
--bg-toolbar: #3b3f45;
--bg-editor: #33373d;
--border-color: #232529;
--text-light: #c0c0c0;
--text-dark: #888;
--accent-green: #2ecc71;
--accent-red: #d9534f;
}
/* =============================================== */
/* ESTILOS GLOBAIS E LAYOUT PRINCIPAL
/* =============================================== */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background-color: var(--bg-body);
color: var(--text-light);
padding-left: 300px;
transition: padding-left .3s ease;
}
body.sidebar-hidden {
padding-left: 0;
}
body.knob-dragging {
cursor: ns-resize;
}
.main-content {
padding: 2rem;
}
/* =============================================== */
/* BARRA LATERAL (SAMPLE BROWSER)
/* =============================================== */
.sample-browser {
position: fixed;
top: 0;
left: 0;
width: 300px;
height: 100vh;
background-color: var(--bg-toolbar);
border-right: 2px solid var(--border-color);
z-index: 1500;
display: flex;
flex-direction: column;
transform: translateX(0);
transition: transform .3s ease;
}
body.sidebar-hidden .sample-browser {
transform: translateX(-100%);
}
.browser-header {
padding: 15px;
background-color: #2a2c30;
border-bottom: 2px solid var(--border-color);
text-align: center;
font-weight: bold;
color: var(--text-light);
}
.browser-content {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
}
.browser-content ul {
list-style: none;
padding-left: 15px;
}
.browser-content li {
padding: 5px;
cursor: pointer;
border-radius: 3px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
}
.browser-content li:hover {
background-color: var(--bg-editor);
}
.browser-content li i {
margin-right: 8px;
width: 12px;
color: var(--text-dark);
transition: transform .2s;
}
.browser-content li.directory > ul {
display: none;
}
.browser-content li.directory.open > ul {
display: block;
}
.browser-content li.directory.open > .fa-folder {
transform: rotate(90deg);
}
#sidebar-toggle {
position: fixed;
top: 60px;
left: 305px;
z-index: 1400;
background-color: var(--bg-toolbar);
border: 1px solid var(--border-color);
color: var(--text-light);
width: 25px;
height: 40px;
cursor: pointer;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
transition: left .3s ease;
display: flex;
align-items: center;
justify-content: center;
}
body.sidebar-hidden #sidebar-toggle {
left: 5px;
}
/* =============================================== */
/* BARRA DE FERRAMENTAS GLOBAL
/* =============================================== */
.global-toolbar {
padding: 8px 15px;
position: fixed;
top: 0;
/* Removido width 100% para se adaptar ao padding do body */
left: 300px;
right: 0;
z-index: 1000;
display: flex;
align-items: center;
gap: 20px;
background-color: var(--bg-toolbar);
border-bottom: 2px solid var(--border-color);
transition: left .3s ease;
}
body.sidebar-hidden .global-toolbar {
left: 0;
}
/* =============================================== */
/* EDITOR DE BATIDAS (BEAT EDITOR)
/* =============================================== */
.beat-editor {
background-color: var(--bg-body);
border: 1px solid var(--border-color);
width: 100%;
max-width: 900px;
margin: auto;
box-shadow: 0 5px 15px rgba(0, 0, 0, .3);
border-radius: 4px;
overflow: hidden;
}
.editor-header {
background-color: var(--bg-toolbar);
padding: 4px 10px;
font-size: .8rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
.window-controls i {
margin-left: 12px;
cursor: pointer;
}
.editor-toolbar {
background-color: var(--bg-toolbar);
padding: 5px 10px;
display: flex;
align-items: center;
gap: 15px;
border-bottom: 2px solid var(--border-color);
}
.editor-toolbar i {
cursor: pointer;
padding: 5px;
border-radius: 3px;
}
.editor-toolbar i.enabled {
background-color: var(--bg-body);
box-shadow: inset 0 0 2px #000;
}
.pattern-selector {
background-color: var(--bg-body);
padding: 5px 15px;
border: 1px solid var(--border-color);
flex-grow: 1;
font-size: .9rem;
border-radius: 2px;
}
/* =============================================== */
/* FAIXAS (TRACK LANES) E SEQUENCIADOR
/* =============================================== */
.track-lane {
display: flex;
align-items: center;
padding: 8px 10px;
background-color: var(--bg-editor);
border: 2px dashed transparent;
transition: border-color 0.2s;
}
.track-lane.drag-over {
border-color: var(--accent-green);
}
.track-info {
display: flex;
align-items: center;
gap: 8px;
width: 180px;
flex-shrink: 0;
}
.track-info .fa-gear {
font-size: 1.2rem;
cursor: pointer;
}
.track-mute {
width: 25px;
height: 12px;
background-color: var(--accent-green);
border-radius: 6px;
cursor: pointer;
border: 1px solid var(--border-color);
box-shadow: inset 0 0 2px #000;
}
.track-name {
color: var(--accent-red);
font-weight: 700;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.track-controls {
display: flex;
gap: 5px;
margin: 0 10px;
padding-left: 10px;
border-left: 1px solid var(--bg-toolbar);
}
.knob-container {
text-align: center;
font-size: .7rem;
color: var(--text-dark);
}
.knob {
width: 28px;
height: 28px;
background-color: var(--bg-toolbar);
border-radius: 50%;
border: 1px solid var(--border-color);
margin-bottom: 2px;
cursor: grab;
box-shadow: inset 0 0 4px #222;
position: relative;
}
.knob:active {
cursor: grabbing;
}
.knob-indicator {
width: 2px;
height: 8px;
background-color: var(--text-light);
position: absolute;
top: 2px;
left: 50%;
transform-origin: bottom center;
transform: translateX(-50%) rotate(0deg);
border-radius: 1px;
}
.step-sequencer {
display: flex;
flex-grow: 1;
gap: 4px;
overflow-x: auto;
padding-bottom: 8px;
}
.step-sequencer::-webkit-scrollbar {
height: 8px;
}
.step-sequencer::-webkit-scrollbar-track {
background: var(--border-color);
border-radius: 4px;
}
.step-sequencer::-webkit-scrollbar-thumb {
background: var(--bg-toolbar);
border-radius: 4px;
}
.step-sequencer::-webkit-scrollbar-thumb:hover {
background: #555;
}
.step-wrapper {
position: relative;
}
.step-marker {
position: absolute;
top: -16px;
left: 1px;
font-size: .6rem;
color: var(--text-dark);
}
.step {
width: 28px;
aspect-ratio: 1 / 1;
background-color: #2a2a2a;
border: 1px solid #4a4a4a;
border-radius: 2px;
cursor: pointer;
transition: background-color .1s, transform 0.1s;
flex-shrink: 0; /* Impede que os steps encolham */
}
.step-dark {
background-color: #1e1e1e;
}
.step:hover {
background-color: #555;
border-color: #888;
}
.step.active {
background-color: var(--accent-green);
border: 1px solid #fff;
box-shadow: 0 0 8px var(--accent-green);
}
.step.playing {
transform: scale(1.1);
box-shadow: inset 0 0 8px rgba(255, 255, 255, 0.8);
}
/* =============================================== */
/* CONTROLES E INPUTS
/* =============================================== */
.interactive-input-container {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.compasso-group {
display: flex;
align-items: center;
gap: 4px;
}
.value-input {
background: 0 0;
border: 0;
outline: 0;
color: var(--accent-green);
font-weight: 700;
font-size: 1.4rem;
font-family: Courier New, Courier, monospace;
text-align: center;
padding: 0;
width: 55px;
}
.compasso-input {
width: 25px;
}
.compasso-separator {
color: var(--accent-green);
font-weight: 700;
font-size: 1.4rem;
font-family: Courier New, Courier, monospace;
margin: 0 2px;
}
.value-input::-webkit-outer-spin-button,
.value-input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.value-input[type=number] {
-moz-appearance: textfield;
}
.adjust-btn {
background: 0 0;
border: 0;
color: var(--text-dark);
font-size: 1rem;
font-weight: 700;
cursor: pointer;
padding: 0 5px;
transition: color .2s;
line-height: 1;
}
.adjust-btn:hover {
color: #fff;
}
.control-group {
display: flex;
align-items: center;
gap: 15px;
padding: 0 10px;
}
.control-group i {
font-size: 1.2rem;
cursor: pointer;
color: var(--text-light);
transition: color .2s;
}
.control-group i:hover {
color: #fff;
}
.fa-play,
.fa-pause {
color: var(--accent-green) !important;
}
.divider {
width: 1px;
height: 25px;
background-color: var(--border-color);
}
.info-display-group {
display: flex;
align-items: center;
gap: 5px;
}
.info-display {
background-color: #1a1c1e;
padding: 5px 8px;
border-radius: 3px;
text-align: center;
}
.info-display .label {
color: var(--text-dark);
font-size: .6rem;
text-transform: uppercase;
}
.spacer {
flex-grow: 1;
}
#metronome-btn {
background: 0 0;
border: 1px solid var(--text-dark);
color: var(--accent-green);
font-family: inherit;
font-weight: 700;
font-size: .8rem;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
transition: all .2s;
}
#metronome-btn:hover {
border-color: var(--text-light);
background-color: var(--bg-editor);
}
#metronome-btn.active {
background-color: var(--accent-green);
color: var(--bg-body);
border-color: var(--accent-green);
}
/* =============================================== */
/* MODAL (CAIXA DE DIÁLOGO)
/* =============================================== */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 2000;
display: flex;
justify-content: center;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s 0.3s, opacity 0.3s;
}
.modal-overlay.visible {
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.3s;
}
.modal-content {
background-color: var(--bg-body);
padding: 1.5rem 2rem;
border-radius: 6px;
border: 1px solid var(--border-color);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
width: 100%;
max-width: 500px;
position: relative;
}
.modal-close {
position: absolute;
top: 10px;
right: 15px;
font-size: 1.5rem;
color: var(--text-dark);
cursor: pointer;
border: none;
background: none;
}
.modal-close:hover {
color: var(--text-light);
}
.modal-title {
margin-top: 0;
margin-bottom: 1.5rem;
color: var(--text-light);
text-align: center;
}
.modal-section {
margin-bottom: 1.5rem;
}
.modal-section h3 {
margin-top: 0;
margin-bottom: 0.8rem;
border-bottom: 1px solid var(--bg-toolbar);
padding-bottom: 0.5rem;
}
.modal-button {
background-color: var(--bg-toolbar);
color: var(--text-light);
border: 1px solid var(--border-color);
padding: 0.8rem 1.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s, border-color 0.2s;
width: 100%;
}
.modal-button:hover {
background-color: #4a4f57;
border-color: #333;
}
#server-projects-list .project-item {
background-color: var(--bg-editor);
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 8px;
cursor: pointer;
transition: background-color 0.2s;
}
#server-projects-list .project-item:hover {
background-color: var(--bg-body);
color: #fff;
}
/* =============================================== */
/* ESTILOS RESPONSIVOS
/* =============================================== */
/* Para telas menores como laptops pequenos e tablets grandes */
@media (max-width: 992px) {
.main-content {
padding: 1.5rem; /* Reduz o padding */
}
.beat-editor {
max-width: 100%; /* Permite que o editor use mais espaço */
}
}
/* Para tablets e celulares */
@media (max-width: 768px) {
body {
padding-left: 0 !important; /* Remove o padding fixo, !important para garantir */
}
.main-content {
padding: 1rem;
}
/* A sidebar agora se sobrepõe ao conteúdo e fica escondida por padrão */
.sample-browser {
transform: translateX(-100%);
width: 280px; /* Pode diminuir um pouco a largura */
}
/* Quando a sidebar for aberta, ela volta a posição original */
body:not(.sidebar-hidden) .sample-browser {
transform: translateX(0);
}
#sidebar-toggle {
left: 5px; /* Posição fixa do botão */
}
/* A toolbar global agora ocupa 100% da largura */
.global-toolbar {
left: 0;
padding-left: 45px; /* Espaço para o botão do menu */
}
/* Quebra a linha dos itens da toolbar se não couberem */
.editor-toolbar,
.control-group {
flex-wrap: wrap;
gap: 10px;
}
/* Reorganiza a track para um layout vertical */
.track-lane {
flex-direction: column;
align-items: stretch; /* Itens ocupam 100% da largura */
gap: 15px;
padding: 15px;
}
.track-info,
.track-controls {
width: 100%;
}
.track-controls {
border-left: none;
padding-left: 0;
justify-content: space-around; /* Distribui melhor os knobs */
}
.step-sequencer {
width: 100%;
}
/* Ajusta o modal para telas pequenas */
.modal-content {
max-width: 90vw;
padding: 1.5rem 1rem;
}
}

View File

@ -0,0 +1,134 @@
//TODO ver Tone.js
// js/audio.js
import { appState } from "./state.js";
import { highlightStep } from "./ui.js";
import { getTotalSteps } from "./utils.js";
let audioContext;
let mainGainNode;
export function getAudioContext() {
return audioContext;
}
export function getMainGainNode() {
return mainGainNode;
}
export function initializeAudioContext() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
mainGainNode = audioContext.createGain();
mainGainNode.connect(audioContext.destination);
}
if (audioContext.state === "suspended") {
audioContext.resume();
}
}
export function playMetronomeSound(isDownbeat) {
initializeAudioContext();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
const frequency = isDownbeat ? 1000 : 800;
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
oscillator.type = "sine";
gainNode.gain.setValueAtTime(1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(
0.00001,
audioContext.currentTime + 0.05
);
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.05);
}
export function playSample(filePath, trackId) {
initializeAudioContext();
if (!filePath) return;
const track = trackId ? appState.tracks.find((t) => t.id == trackId) : null;
const audio = new Audio(filePath);
const source = audioContext.createMediaElementSource(audio);
if (track && track.gainNode) {
source.connect(track.gainNode);
} else {
source.connect(mainGainNode);
}
audio.play();
}
function tick() {
const totalSteps = getTotalSteps();
if (totalSteps === 0) {
stopPlayback();
return;
}
const lastStepIndex =
appState.currentStep === 0 ? totalSteps - 1 : appState.currentStep - 1;
highlightStep(lastStepIndex, false);
if (appState.metronomeEnabled) {
const noteValue =
parseInt(document.getElementById("compasso-b-input").value, 10) || 4;
const stepsPerBeat = 16 / noteValue;
if (appState.currentStep % stepsPerBeat === 0) {
playMetronomeSound(appState.currentStep === 0);
}
}
appState.tracks.forEach((track) => {
if (track.steps[appState.currentStep] && track.samplePath) {
playSample(track.samplePath, track.id);
}
});
highlightStep(appState.currentStep, true);
appState.currentStep = (appState.currentStep + 1) % totalSteps;
}
export function startPlayback() {
if (appState.isPlaying || appState.tracks.length === 0) return;
initializeAudioContext();
const bpm = parseInt(document.getElementById("bpm-input").value, 10) || 120;
const stepInterval = (60 * 1000) / (bpm * 4);
if (appState.playbackIntervalId) clearInterval(appState.playbackIntervalId);
appState.isPlaying = true;
document.getElementById("play-btn").classList.remove("fa-play");
document.getElementById("play-btn").classList.add("fa-pause");
tick();
appState.playbackIntervalId = setInterval(tick, stepInterval);
}
export function stopPlayback() {
clearInterval(appState.playbackIntervalId);
appState.playbackIntervalId = null;
appState.isPlaying = false;
highlightStep(appState.currentStep - 1, false);
appState.currentStep = 0;
document.getElementById("play-btn").classList.remove("fa-pause");
document.getElementById("play-btn").classList.add("fa-play");
}
export function rewindPlayback() {
appState.currentStep = 0;
if (!appState.isPlaying) {
document
.querySelectorAll(".step.playing")
.forEach((s) => s.classList.remove("playing"));
}
}
export function togglePlayback() {
if (appState.isPlaying) {
stopPlayback();
} else {
startPlayback();
}
}

View File

@ -0,0 +1,9 @@
// js/config.js
// Constantes usadas para o cálculo de posição de notas no arquivo .mmp
export const TICKS_PER_BAR = 192;
export const NOTE_LENGTH = 12;
// Constantes para os valores padrão dos knobs
export const DEFAULT_VOLUME = 0.8;
export const DEFAULT_PAN = 0.0;

243
assets/js/creations/file.js Normal file
View File

@ -0,0 +1,243 @@
// js/file.js
import { appState } from "./state.js";
import { getTotalSteps } from "./utils.js";
import { renderApp } from "./ui.js";
import { NOTE_LENGTH, TICKS_PER_BAR } from "./config.js";
import {
initializeAudioContext,
getAudioContext,
getMainGainNode,
} from "./audio.js";
export async function handleFileLoad(file) {
let xmlContent = "";
try {
if (file.name.toLowerCase().endsWith(".mmpz")) {
const jszip = new JSZip();
const zip = await jszip.loadAsync(file);
const projectFile = Object.keys(zip.files).find((name) =>
name.toLowerCase().endsWith(".mmp")
);
if (!projectFile)
throw new Error(
"Não foi possível encontrar um arquivo .mmp dentro do .mmpz"
);
xmlContent = await zip.files[projectFile].async("string");
} else {
xmlContent = await file.text();
}
parseMmpContent(xmlContent);
} catch (error) {
console.error("Erro ao carregar o projeto:", error);
alert(`Erro ao carregar projeto: ${error.message}`);
}
}
export function parseMmpContent(xmlString) {
initializeAudioContext();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
appState.originalXmlDoc = xmlDoc;
const newTracks = [];
const head = xmlDoc.querySelector("head");
if (head) {
document.getElementById("bpm-input").value =
head.getAttribute("bpm") || 140;
document.getElementById("compasso-a-input").value =
head.getAttribute("timesig_numerator") || 4;
document.getElementById("compasso-b-input").value =
head.getAttribute("timesig_denominator") || 4;
}
const sampleTrackElements = xmlDoc.querySelectorAll(
'instrument[name="audiofileprocessor"]'
);
sampleTrackElements.forEach((instrumentNode) => {
const afpNode = instrumentNode.querySelector("audiofileprocessor");
const instrumentTrackNode = instrumentNode.parentElement;
const trackNode = instrumentTrackNode.parentElement;
if (!afpNode || !instrumentTrackNode || !trackNode) return;
const audioContext = getAudioContext();
const mainGainNode = getMainGainNode();
const totalSteps = parseInt(
trackNode.querySelector("pattern")?.getAttribute("steps") ||
getTotalSteps(),
10
);
const newSteps = new Array(totalSteps).fill(false);
const ticksPerStep = totalSteps > 0 ? TICKS_PER_BAR / totalSteps : 0;
trackNode.querySelectorAll("pattern note").forEach((noteNode) => {
const pos = parseInt(noteNode.getAttribute("pos"), 10);
const stepIndex = Math.round(pos / ticksPerStep);
if (stepIndex < totalSteps) {
newSteps[stepIndex] = true;
}
});
const newTrack = {
id: Date.now() + Math.random(),
name:
afpNode.getAttribute("src").split("/").pop() ||
trackNode.getAttribute("name"),
samplePath: `samples/${afpNode.getAttribute("src")}`,
steps: newSteps,
volume: parseFloat(instrumentTrackNode.getAttribute("vol")) / 100,
pan: parseFloat(instrumentTrackNode.getAttribute("pan")) / 100,
gainNode: audioContext.createGain(),
pannerNode: audioContext.createStereoPanner(),
};
newTrack.gainNode.connect(newTrack.pannerNode);
newTrack.pannerNode.connect(mainGainNode);
newTrack.gainNode.gain.value = newTrack.volume;
newTrack.pannerNode.pan.value = newTrack.pan;
newTracks.push(newTrack);
});
appState.tracks = newTracks;
renderApp();
console.log("Projeto carregado com sucesso!", appState);
}
export function generateMmpFile() {
if (appState.originalXmlDoc) {
modifyAndSaveExistingMmp();
} else {
generateNewMmp();
}
}
function generateNewMmp() {
console.log("Gerando novo arquivo .mmp do zero...");
const bpm = document.getElementById("bpm-input").value;
const sig_num = document.getElementById("compasso-a-input").value;
const sig_den = document.getElementById("compasso-b-input").value;
const tracksXml = appState.tracks
.map((track) => createTrackXml(track))
.join("");
const mmpContent = `<?xml version="1.0"?>
<!DOCTYPE lmms-project>
<lmms-project version="1.0" type="song" creator="MMPCreator" creatorversion="1.0">
<head mastervol="100" timesig_denominator="${sig_den}" bpm="${bpm}" timesig_numerator="${sig_num}" masterpitch="0"/>
<song>
<trackcontainer type="song">
<track type="1" solo="0" muted="0" name="Beat/Bassline 0">
<bbtrack>
<trackcontainer type="bbtrackcontainer">
${tracksXml}
</trackcontainer>
</bbtrack>
<bbtco color="4286611584" len="192" usestyle="1" pos="0" muted="0" name="Pattern 1"/>
</track>
</trackcontainer>
<timeline lp1pos="192" lp0pos="0" lpstate="0"/>
<controllers/>
<projectnotes width="686" y="10" minimized="0" visible="0" x="700" maximized="0" height="400"><![CDATA[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body>
<p>Feito com MMPCreator no https://alice.ufsj.edu.br/MMPSearch/creator</p>
</body></html>]]></projectnotes>
</song>
</lmms-project>`;
downloadFile(mmpContent, "novo_projeto.mmp");
}
function modifyAndSaveExistingMmp() {
console.log("Modificando arquivo .mmp existente...");
const xmlDoc = appState.originalXmlDoc.cloneNode(true);
const head = xmlDoc.querySelector("head");
if (head) {
head.setAttribute("bpm", document.getElementById("bpm-input").value);
head.setAttribute(
"timesig_numerator",
document.getElementById("compasso-a-input").value
);
head.setAttribute(
"timesig_denominator",
document.getElementById("compasso-b-input").value
);
}
const bbTrackContainer = xmlDoc.querySelector("bbtrack > trackcontainer");
if (bbTrackContainer) {
const oldSampleTracks = bbTrackContainer.querySelectorAll(
'instrument[name="audiofileprocessor"]'
);
oldSampleTracks.forEach((node) => node.closest("track").remove());
const tracksXml = appState.tracks
.map((track) => createTrackXml(track))
.join("");
const tempDoc = new DOMParser().parseFromString(
`<root>${tracksXml}</root>`,
"application/xml"
);
Array.from(tempDoc.documentElement.children).forEach((newTrackNode) => {
bbTrackContainer.appendChild(newTrackNode);
});
}
const serializer = new XMLSerializer();
const mmpContent = serializer.serializeToString(xmlDoc);
downloadFile(mmpContent, "projeto_editado.mmp");
}
function createTrackXml(track) {
if (!track.samplePath) return "";
const totalSteps = track.steps.length || getTotalSteps();
const ticksPerStep = totalSteps > 0 ? TICKS_PER_BAR / totalSteps : 0;
const lmmsVolume = Math.round(track.volume * 100);
const lmmsPan = Math.round(track.pan * 100);
const sampleSrc = track.samplePath.replace("samples/", "");
const notesXml = track.steps
.map((isActive, index) => {
if (isActive) {
const notePos = Math.round(index * ticksPerStep);
return `<note vol="100" len="${NOTE_LENGTH}" pos="${notePos}" pan="0" key="57"/>`;
}
return "";
})
.join("\n ");
return `
<track type="0" solo="0" muted="0" name="${track.name}">
<instrumenttrack vol="${lmmsVolume}" pitch="0" fxch="0" pitchrange="1" basenote="57" usemasterpitch="1" pan="${lmmsPan}">
<instrument name="audiofileprocessor">
<audiofileprocessor eframe="1" stutter="0" looped="0" interp="1" reversed="0" lframe="0" src="${sampleSrc}" amp="100" sframe="0"/>
</instrument>
<fxchain enabled="0" numofeffects="0"/>
</instrumenttrack>
<pattern type="0" pos="0" muted="0" steps="${totalSteps}" name="${track.name}">
${notesXml}
</pattern>
</track>`;
}
function downloadFile(content, fileName) {
const blob = new Blob([content], { type: "application/xml;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// (ALTERADO) Adiciona export na frente da função
export async function loadProjectFromServer(fileName) {
try {
const response = await fetch(`mmp/${fileName}`);
if (!response.ok)
throw new Error(`Não foi possível carregar o arquivo ${fileName}`);
const xmlContent = await response.text();
parseMmpContent(xmlContent);
// Retorna true em caso de sucesso
return true;
} catch (error) {
console.error("Erro ao carregar projeto do servidor:", error);
alert(`Erro ao carregar projeto: ${error.message}`);
// Retorna false em caso de falha
return false;
}
}

121
assets/js/creations/main.js Normal file
View File

@ -0,0 +1,121 @@
// js/main.js
import {
appState,
addTrackToState,
removeLastTrackFromState,
} from "./state.js";
import {
togglePlayback,
stopPlayback,
rewindPlayback,
initializeAudioContext,
} from "./audio.js";
import { handleFileLoad, generateMmpFile } from "./file.js";
import {
renderApp,
redrawSequencer,
loadAndRenderSampleBrowser,
showOpenProjectModal,
closeOpenProjectModal,
} from "./ui.js";
import { adjustValue, enforceNumericInput } from "./utils.js";
// --- INICIALIZAÇÃO E EVENTOS FINAIS ---
document.addEventListener("DOMContentLoaded", () => {
const newProjectBtn = document.getElementById("new-project-btn");
const openMmpBtn = document.getElementById("open-mmp-btn");
const saveMmpBtn = document.getElementById("save-mmp-btn");
const addInstrumentBtn = document.getElementById("add-instrument-btn");
const removeInstrumentBtn = document.getElementById("remove-instrument-btn");
const playBtn = document.getElementById("play-btn");
const stopBtn = document.getElementById("stop-btn");
const rewindBtn = document.getElementById("rewind-btn");
const metronomeBtn = document.getElementById("metronome-btn");
const mmpFileInput = document.getElementById("mmp-file-input");
const openProjectModal = document.getElementById("open-project-modal");
const openModalCloseBtn = document.getElementById("open-modal-close-btn");
const loadFromComputerBtn = document.getElementById("load-from-computer-btn");
const sidebarToggle = document.getElementById("sidebar-toggle");
newProjectBtn.addEventListener("click", () => {
if (
appState.tracks.length > 0 &&
!confirm("Você tem certeza? Alterações não salvas serão perdidas.")
)
return;
Object.assign(appState, {
tracks: [],
isPlaying: false,
playbackIntervalId: null,
currentStep: 0,
metronomeEnabled: false,
originalXmlDoc: null,
});
renderApp();
});
openMmpBtn.addEventListener("click", showOpenProjectModal);
loadFromComputerBtn.addEventListener("click", () => mmpFileInput.click());
mmpFileInput.addEventListener("change", (event) => {
const file = event.target.files[0];
if (file) {
handleFileLoad(file);
closeOpenProjectModal();
}
});
saveMmpBtn.addEventListener("click", generateMmpFile);
addInstrumentBtn.addEventListener("click", addTrackToState);
removeInstrumentBtn.addEventListener("click", removeLastTrackFromState);
playBtn.addEventListener("click", togglePlayback);
stopBtn.addEventListener("click", stopPlayback);
rewindBtn.addEventListener("click", rewindPlayback);
metronomeBtn.addEventListener("click", () => {
initializeAudioContext();
appState.metronomeEnabled = !appState.metronomeEnabled;
metronomeBtn.classList.toggle("active", appState.metronomeEnabled);
});
openModalCloseBtn.addEventListener("click", closeOpenProjectModal);
openProjectModal.addEventListener("click", (e) => {
if (e.target === openProjectModal) closeOpenProjectModal();
});
sidebarToggle.addEventListener("click", () => {
document.body.classList.toggle("sidebar-hidden");
const icon = sidebarToggle.querySelector("i");
icon.className = document.body.classList.contains("sidebar-hidden")
? "fa-solid fa-caret-right"
: "fa-solid fa-caret-left";
});
const inputs = document.querySelectorAll(".value-input");
inputs.forEach((input) => {
input.addEventListener("input", (event) => {
enforceNumericInput(event);
if (appState.isPlaying && event.target.id.startsWith("compasso-")) {
stopPlayback();
}
if (event.target.id.startsWith("compasso-")) {
redrawSequencer();
}
});
input.addEventListener("wheel", (event) => {
event.preventDefault();
const step = event.deltaY < 0 ? 1 : -1;
adjustValue(event.target, step);
});
});
const buttons = document.querySelectorAll(".adjust-btn");
buttons.forEach((button) => {
button.addEventListener("click", () => {
const targetId = button.dataset.target + "-input";
const targetInput = document.getElementById(targetId);
if (targetInput) {
adjustValue(targetInput, step);
}
});
});
// Inicia a aplicação
loadAndRenderSampleBrowser();
renderApp();
});

View File

@ -0,0 +1,90 @@
// js/state.js
import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js";
import {
initializeAudioContext,
getAudioContext,
getMainGainNode,
} from "./audio.js";
import { renderApp } from "./ui.js";
// O "cérebro" da aplicação
export let appState = {
tracks: [],
isPlaying: false,
playbackIntervalId: null,
currentStep: 0,
metronomeEnabled: false,
originalXmlDoc: null,
};
export function addTrackToState() {
initializeAudioContext();
const audioContext = getAudioContext();
const mainGainNode = getMainGainNode();
const newTrack = {
id: Date.now(),
name: "novo instrumento",
samplePath: null,
steps: [],
volume: DEFAULT_VOLUME,
pan: DEFAULT_PAN,
gainNode: audioContext.createGain(),
pannerNode: audioContext.createStereoPanner(),
};
newTrack.gainNode.connect(newTrack.pannerNode);
newTrack.pannerNode.connect(mainGainNode);
newTrack.gainNode.gain.value = newTrack.volume;
newTrack.pannerNode.pan.value = newTrack.pan;
appState.tracks.push(newTrack);
renderApp();
}
export function removeLastTrackFromState() {
appState.tracks.pop();
renderApp();
}
export function updateTrackSample(trackId, samplePath) {
const track = appState.tracks.find((t) => t.id == trackId);
if (track) {
track.samplePath = samplePath;
track.name = samplePath.split("/").pop();
}
renderApp();
}
export function toggleStepState(trackId, stepIndex) {
const track = appState.tracks.find((t) => t.id == trackId);
if (track) {
track.steps[stepIndex] = !track.steps[stepIndex];
}
}
export function updateTrackVolume(trackId, volume) {
const track = appState.tracks.find((t) => t.id == trackId);
const audioContext = getAudioContext();
if (track) {
const clampedVolume = Math.max(0, Math.min(1.5, volume));
track.volume = clampedVolume;
if (track.gainNode) {
track.gainNode.gain.setValueAtTime(
clampedVolume,
audioContext.currentTime
);
}
}
}
export function updateTrackPan(trackId, pan) {
const track = appState.tracks.find((t) => t.id == trackId);
const audioContext = getAudioContext();
if (track) {
const clampedPan = Math.max(-1, Math.min(1, pan));
track.pan = clampedPan;
if (track.pannerNode) {
track.pannerNode.pan.setValueAtTime(clampedPan, audioContext.currentTime);
}
}
}

319
assets/js/creations/ui.js Normal file
View File

@ -0,0 +1,319 @@
// js/ui.js
import {
appState,
toggleStepState,
updateTrackSample,
updateTrackVolume,
updateTrackPan,
} from "./state.js";
import { playSample } from "./audio.js";
import { getTotalSteps } from "./utils.js";
import { loadProjectFromServer } from "./file.js"; // (CORREÇÃO) Importa a função que faltava
// 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"></div>
`;
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");
const filePath = e.dataTransfer.getData("text/plain");
if (filePath) {
updateTrackSample(trackData.id, filePath);
}
});
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");
});
redrawSequencer();
}
export function redrawSequencer() {
const beatsPerBar =
parseInt(document.getElementById("compasso-a-input").value, 10) || 4;
const totalSteps = getTotalSteps();
document.querySelectorAll(".step-sequencer").forEach((container) => {
const parentTrackElement = container.closest(".track-lane");
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;
}
container.innerHTML = "";
for (let i = 0; i < totalSteps; i++) {
const stepWrapper = document.createElement("div");
stepWrapper.className = "step-wrapper";
const stepElement = document.createElement("div");
stepElement.className = "step";
if (trackData && trackData.steps[i] === true) {
stepElement.classList.add("active");
}
stepElement.addEventListener("click", () => {
toggleStepState(trackData.id, i);
stepElement.classList.toggle("active");
if (trackData && trackData.samplePath) {
playSample(trackData.samplePath, trackData.id);
}
});
const groupIndex = Math.floor(i / beatsPerBar);
if (groupIndex % 2 === 0) {
stepElement.classList.add("step-dark");
}
if (i > 0 && i % beatsPerBar === 0) {
const marker = document.createElement("div");
marker.className = "step-marker";
marker.textContent = i;
stepWrapper.appendChild(marker);
}
stepWrapper.appendChild(stepElement);
container.appendChild(stepWrapper);
}
});
}
// LÓGICA DE INTERAÇÃO DOS KNOBS
function addKnobInteraction(knobElement) {
const controlType = knobElement.dataset.control;
knobElement.addEventListener("mousedown", (e) => {
if (e.button === 1) {
e.preventDefault();
const trackId = knobElement.dataset.trackId;
const defaultValue = controlType === "volume" ? 0.8 : 0.0;
if (controlType === "volume") {
updateTrackVolume(trackId, defaultValue);
} else {
updateTrackPan(trackId, defaultValue);
}
}
});
knobElement.addEventListener("mousedown", (e) => {
if (e.button !== 0) return;
e.preventDefault();
const trackId = knobElement.dataset.trackId;
const track = appState.tracks.find((t) => t.id == trackId);
if (!track) return;
const startY = e.clientY;
const startValue = controlType === "volume" ? track.volume : track.pan;
document.body.classList.add("knob-dragging");
function onMouseMove(moveEvent) {
const deltaY = startY - moveEvent.clientY;
const sensitivity = controlType === "volume" ? 150 : 200;
const newValue = startValue + deltaY / sensitivity;
if (controlType === "volume") {
updateTrackVolume(trackId, newValue);
} else {
updateTrackPan(trackId, newValue);
}
}
function onMouseUp() {
document.body.classList.remove("knob-dragging");
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
}
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
});
knobElement.addEventListener("wheel", (e) => {
e.preventDefault();
const trackId = knobElement.dataset.trackId;
const track = appState.tracks.find((t) => t.id == trackId);
if (!track) return;
const step = 0.05;
const direction = e.deltaY < 0 ? 1 : -1;
if (controlType === "volume") {
const newValue = track.volume + direction * step;
updateTrackVolume(trackId, newValue);
} else {
const newValue = track.pan + direction * step;
updateTrackPan(trackId, newValue);
}
});
}
function updateKnobVisual(knobElement, controlType) {
const trackId = knobElement.dataset.trackId;
const track = appState.tracks.find((t) => t.id == trackId);
if (!track) return;
const indicator = knobElement.querySelector(".knob-indicator");
if (!indicator) return;
const minAngle = -135;
const maxAngle = 135;
let percentage = 0.5;
let title = "";
if (controlType === "volume") {
const value = track.volume;
const clampedValue = Math.max(0, Math.min(1.5, value));
percentage = clampedValue / 1.5;
title = `Volume: ${Math.round(clampedValue * 100)}%`;
} else {
const value = track.pan;
const clampedValue = Math.max(-1, Math.min(1, value));
percentage = (clampedValue + 1) / 2;
const panDisplay = Math.round(clampedValue * 100);
title = `Pan: ${
panDisplay === 0
? "Centro"
: panDisplay < 0
? `${-panDisplay} L`
: `${panDisplay} R`
}`;
}
const angle = minAngle + percentage * (maxAngle - minAngle);
indicator.style.transform = `translateX(-50%) rotate(${angle}deg)`;
knobElement.title = title;
}
export function highlightStep(stepIndex, isActive) {
if (stepIndex < 0) return;
document.querySelectorAll(".track-lane").forEach((track) => {
const stepWrapper = track.querySelector(
`.step-sequencer .step-wrapper:nth-child(${stepIndex + 1})`
);
if (stepWrapper) {
const stepElement = stepWrapper.querySelector(".step");
if (stepElement) {
stepElement.classList.toggle("playing", isActive);
}
}
});
}
// LÓGICA DA SIDEBAR E MODAL
export async function loadAndRenderSampleBrowser() {
const browserContent = document.getElementById("browser-content");
try {
const response = await fetch("metadata/samples-manifest.json");
if (!response.ok) {
throw new Error("Arquivo samples-manifest.json não encontrado.");
}
const fileTree = await response.json();
renderFileTree(fileTree, browserContent, "src/samples");
} catch (error) {
console.error("Erro ao carregar samples:", error);
browserContent.innerHTML = `<p style="color:var(--accent-red); padding: 10px;">${error.message}</p>`;
}
}
function renderFileTree(tree, parentElement, currentPath) {
parentElement.innerHTML = "";
const ul = document.createElement("ul");
const sortedKeys = Object.keys(tree).sort((a, b) => {
const aIsFile = tree[a]._isFile;
const bIsFile = tree[b]._isFile;
if (aIsFile === bIsFile) return a.localeCompare(b);
return aIsFile ? 1 : -1;
});
for (const key of sortedKeys) {
const node = tree[key];
const li = document.createElement("li");
const newPath = `${currentPath}/${key}`;
if (node._isFile) {
li.innerHTML = `<i class="fa-solid fa-file-audio"></i> ${key}`;
li.setAttribute("draggable", true);
li.addEventListener("click", (e) => {
e.stopPropagation();
playSample(newPath, null);
});
li.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", newPath);
e.dataTransfer.effectAllowed = "copy";
});
ul.appendChild(li);
} else {
li.className = "directory";
li.innerHTML = `<i class="fa-solid fa-folder"></i> ${key}`;
const nestedUl = document.createElement("ul");
renderFileTree(node, nestedUl, newPath);
li.appendChild(nestedUl);
li.addEventListener("click", (e) => {
e.stopPropagation();
li.classList.toggle("open");
});
ul.appendChild(li);
}
}
parentElement.appendChild(ul);
}
export async function showOpenProjectModal() {
const openProjectModal = document.getElementById("open-project-modal");
const serverProjectsList = document.getElementById("server-projects-list");
serverProjectsList.innerHTML = "<p>Carregando...</p>";
openProjectModal.classList.add("visible");
try {
const response = await fetch("metadata/mmp-manifest.json");
if (!response.ok)
throw new Error("Arquivo mmp-manifest.json não encontrado.");
const projects = await response.json();
serverProjectsList.innerHTML = "";
if (projects.length === 0) {
serverProjectsList.innerHTML =
'<p style="color:var(--text-dark);">Nenhum projeto encontrado no servidor.</p>';
}
projects.forEach((projectName) => {
const projectItem = document.createElement("div");
projectItem.className = "project-item";
projectItem.textContent = projectName;
projectItem.addEventListener("click", async () => {
const success = await loadProjectFromServer(projectName);
if (success) {
closeOpenProjectModal();
}
});
serverProjectsList.appendChild(projectItem);
});
} catch (error) {
console.error("Erro ao carregar lista de projetos:", error);
serverProjectsList.innerHTML = `<p style="color:var(--accent-red);">${error.message}</p>`;
}
}
export function closeOpenProjectModal() {
const openProjectModal = document.getElementById("open-project-modal");
openProjectModal.classList.remove("visible");
}

View File

@ -0,0 +1,27 @@
// js/utils.js
export function getTotalSteps() {
const compassoAInput = document.getElementById("compasso-a-input");
const compassoBInput = document.getElementById("compasso-b-input");
const beatsPerBar = parseInt(compassoAInput.value, 10) || 4;
const noteValue = parseInt(compassoBInput.value, 10) || 4;
const subdivisions = Math.round(16 / noteValue);
return beatsPerBar * subdivisions;
}
export function enforceNumericInput(event) {
event.target.value = event.target.value.replace(/[^0-9]/g, "");
}
export function adjustValue(inputElement, step) {
let currentValue = parseInt(inputElement.value, 10) || 0;
let min = parseInt(inputElement.dataset.min, 10);
let max = parseInt(inputElement.dataset.max, 10);
let newValue = currentValue + step;
if (!isNaN(min) && newValue < min) newValue = min;
if (!isNaN(max) && newValue > max) newValue = max;
inputElement.value = newValue;
// Dispara um evento 'input' para que outros listeners (como o que redesenha o sequenciador) sejam acionados.
inputElement.dispatchEvent(new Event("input", { bubbles: true }));
}

View File

@ -1,108 +1,123 @@
document.addEventListener("DOMContentLoaded", function() {
var fileItemsMMP = document.querySelectorAll('#file-list-mmp .file-item');
var fileItemsWAV = document.querySelectorAll('#file-list-wav .file-wav-item');
document.addEventListener("DOMContentLoaded", function () {
var fileItemsMMP = document.querySelectorAll("#file-list-mmp .file-item");
var fileItemsWAV = document.querySelectorAll("#file-list-wav .file-wav-item");
// Função para exibir apenas os arquivos WAV correspondentes aos instrumentos encontrados nos projetos MMP
function showWAVFilesByInstrument(selectedInstrument) {
fileItemsWAV.forEach(function(item) {
var fileName = item.getAttribute('data-file').toLowerCase();
fileItemsWAV.forEach(function (item) {
var fileName = item.getAttribute("data-file").toLowerCase();
if (fileName.includes(selectedInstrument)) {
item.style.display = 'block';
item.style.display = "block";
} else {
item.style.display = 'none';
item.style.display = "none";
}
});
}
// Buscar por instrumento usando o campo de entrada para MMP
var searchButton = document.getElementById('search-button');
searchButton.addEventListener('click', function() {
var searchInput = document.getElementById('instrument-search').value.trim().toLowerCase();
if (searchInput === '') {
var searchButton = document.getElementById("search-button");
searchButton.addEventListener("click", function () {
var searchInput = document
.getElementById("instrument-search")
.value.trim()
.toLowerCase();
if (searchInput === "") {
resetSearch();
return;
}
fileItemsMMP.forEach(function(item) {
var instrumentsList = item.querySelectorAll('.instrument-list .instrument-name');
fileItemsMMP.forEach(function (item) {
var instrumentsList = item.querySelectorAll(
".instrument-list .instrument-name"
);
var hasInstrument = false;
instrumentsList.forEach(function(instrument) {
var instrumentName = instrument.getAttribute('data-instrument').toLowerCase();
instrumentsList.forEach(function (instrument) {
var instrumentName = instrument
.getAttribute("data-instrument")
.toLowerCase();
if (instrumentName.includes(searchInput)) {
hasInstrument = true;
}
});
if (hasInstrument) {
item.style.display = 'block';
item.style.display = "block";
} else {
item.style.display = 'none';
item.style.display = "none";
}
});
// Exibir a lista de arquivos MMP após a busca
document.getElementById('file-list-mmp').style.display = 'block';
document.getElementById("file-list-mmp").style.display = "block";
});
// Buscar por nome de arquivo WAV usando o campo de entrada
var searchWavButton = document.getElementById('search-wav-button');
searchWavButton.addEventListener('click', function() {
var searchInput = document.getElementById('file-wav-search').value.trim().toLowerCase();
if (searchInput === '') {
var searchWavButton = document.getElementById("search-wav-button");
searchWavButton.addEventListener("click", function () {
var searchInput = document
.getElementById("file-wav-search")
.value.trim()
.toLowerCase();
if (searchInput === "") {
resetSearch();
return;
}
fileItemsWAV.forEach(function(item) {
var fileName = item.querySelector('span').textContent.toLowerCase();
fileItemsWAV.forEach(function (item) {
var fileName = item.querySelector("span").textContent.toLowerCase();
if (fileName.includes(searchInput)) {
item.style.display = 'block';
item.style.display = "block";
} else {
item.style.display = 'none';
item.style.display = "none";
}
});
// Exibir a lista de arquivos WAV após a busca
document.getElementById('file-list-wav').style.display = 'block';
document.getElementById("file-list-wav").style.display = "block";
});
// Buscar por instrumento ou nome de arquivo WAV usando o campo de entrada combinado
var searchBothButton = document.getElementById('search-both-button');
searchBothButton.addEventListener('click', function() {
var searchInput = document.getElementById('instrument-wav-search').value.trim().toLowerCase();
if (searchInput === '') {
var searchBothButton = document.getElementById("search-both-button");
searchBothButton.addEventListener("click", function () {
var searchInput = document
.getElementById("instrument-wav-search")
.value.trim()
.toLowerCase();
if (searchInput === "") {
resetSearch();
return;
}
// Buscar em arquivos MMP por instrumento
fileItemsMMP.forEach(function(item) {
var instrumentsList = item.querySelectorAll('.instrument-list .instrument-name');
fileItemsMMP.forEach(function (item) {
var instrumentsList = item.querySelectorAll(
".instrument-list .instrument-name"
);
var hasInstrument = false;
instrumentsList.forEach(function(instrument) {
var instrumentName = instrument.getAttribute('data-instrument').toLowerCase();
instrumentsList.forEach(function (instrument) {
var instrumentName = instrument
.getAttribute("data-instrument")
.toLowerCase();
if (instrumentName.includes(searchInput)) {
hasInstrument = true;
// Mostrar arquivo WAV correspondente ao instrumento
showWAVFilesByInstrument(item.getAttribute('data-file'));
showWAVFilesByInstrument(item.getAttribute("data-file"));
}
});
if (hasInstrument) {
item.style.display = 'block';
item.style.display = "block";
} else {
item.style.display = 'none';
item.style.display = "none";
}
});
// Exibir as listas de arquivos após a busca
document.getElementById('file-list-mmp').style.display = 'block';
document.getElementById('file-list-wav').style.display = 'block';
document.getElementById("file-list-mmp").style.display = "block";
document.getElementById("file-list-wav").style.display = "block";
});
// Função para resetar a busca
function resetSearch() {
fileItemsMMP.forEach(function(item) {
item.style.display = 'block';
fileItemsMMP.forEach(function (item) {
item.style.display = "block";
});
fileItemsWAV.forEach(function(item) {
item.style.display = 'block';
fileItemsWAV.forEach(function (item) {
item.style.display = "block";
});
document.getElementById('file-list-mmp').style.display = 'none';
document.getElementById('file-list-wav').style.display = 'none';
document.getElementById("file-list-mmp").style.display = "none";
document.getElementById("file-list-wav").style.display = "none";
}
});

View File

@ -3,18 +3,19 @@ layout: default
title: Projetos com a tag Automation
permalink: /automation/
---
<meta charset="utf-8">
<meta charset="utf-8" />
<main class="main-content">
<div class="publication">
{% include sidebar.html %}
</div>
<div class="publication">{% include sidebar.html %}</div>
<div class="container">
<div class="columns is-mobile is-vcentered" style="margin-bottom: 2rem;">
<div class="columns is-mobile is-vcentered" style="margin-bottom: 2rem">
<!-- Título -->
<div class="column is-auto">
<h2 class="title is-4"><code>Projetos que possuem a tag automation:</code></h2>
<h2 class="title is-4">
<code>Projetos que possuem a tag automation:</code>
</h2>
</div>
<!-- Botão Limpar Filtro -->
<div class="column is-auto">
@ -26,99 +27,104 @@ permalink: /automation/
<!-- Projetos -->
<div id="project-list" class="columns is-multiline">
{% for projeto in site.data.all %}
{% if projeto.tags.TAG contains "automation" %}
<div class="column is-6 project-item" data-automation="{{ projeto.tags.automation | join: ',' }}">
<div class="box">
<!-- Botão do projeto -->
{% assign file_url = projeto.file | downcase
| replace: ' ', '-'
| replace: 'ç', 'c'
| replace: 'ã', 'a'
| replace: 'á', 'a'
| replace: 'â', 'a'
| replace: 'é', 'e'
| replace: 'ê', 'e'
| replace: 'í', 'i'
| replace: 'ó', 'o'
| replace: 'ô', 'o'
| replace: 'õ', 'o'
| replace: 'ú', 'u' %}
{% for projeto in site.data.all %} {% if projeto.tags.TAG contains
"automation" %}
<div
class="column is-6 project-item"
data-automation="{{ projeto.tags.automation | join: ',' }}"
>
<div class="box">
<!-- Botão do projeto -->
{% assign file_url = projeto.file | downcase | replace: ' ', '-' |
replace: 'ç', 'c' | replace: 'ã', 'a' | replace: 'á', 'a' | replace:
'â', 'a' | replace: 'é', 'e' | replace: 'ê', 'e' | replace: 'í', 'i' |
replace: 'ó', 'o' | replace: 'ô', 'o' | replace: 'õ', 'o' | replace:
'ú', 'u' %}
<a href="../mmp_pages/{{ file_url }}.html" class="button is-link is-fullwidth">
{{ projeto.file }}
</a>
<a
href="../mmp_pages/{{ file_url }}.html"
class="button is-link is-fullwidth"
>
{{ projeto.file }}
</a>
<!-- Lista de automations clicáveis -->
{% if projeto.tags.automation and projeto.tags.automation.size > 0 %}
<div style="margin-top: 1rem;">
<p><strong>Automation:</strong></p>
<ul style="list-style-type: disc; padding-left: 1.25rem;">
{% for automation in projeto.tags.automation %}
{% if automation != "" %}
<li><a href="#" class="automation-item" data-automation="{{ automation }}">{{ automation }}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<!-- Lista de automations clicáveis -->
{% if projeto.tags.automation and projeto.tags.automation.size > 0 %}
<div style="margin-top: 1rem">
<p><strong>Automation:</strong></p>
<ul style="list-style-type: disc; padding-left: 1.25rem">
{% for automation in projeto.tags.automation %} {% if automation
!= "" %}
<li>
<a
href="#"
class="automation-item"
data-automation="{{ automation }}"
>{{ automation }}</a
>
</li>
{% endif %} {% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %} {% endfor %}
</div>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', function () {
const projects = document.querySelectorAll('.project-item');
document.addEventListener("DOMContentLoaded", function () {
const projects = document.querySelectorAll(".project-item");
// Função para aplicar filtro por automation
function filterByAutomation(automationName) {
projects.forEach(project => {
const projectAutomations = project.getAttribute('data-automation').split(',');
projects.forEach((project) => {
const projectAutomations = project
.getAttribute("data-automation")
.split(",");
if (projectAutomations.includes(automationName)) {
project.style.display = 'block';
project.style.display = "block";
} else {
project.style.display = 'none';
project.style.display = "none";
}
});
}
// Lê a automation da URL se houver
const urlParams = new URLSearchParams(window.location.search);
const automationFromUrl = urlParams.get('automation');
const automationFromUrl = urlParams.get("automation");
if (automationFromUrl) {
filterByAutomation(automationFromUrl);
}
// Ao clicar numa automation na lista
const automationItems = document.querySelectorAll('.automation-item');
automationItems.forEach(item => {
item.addEventListener('click', function (event) {
const automationItems = document.querySelectorAll(".automation-item");
automationItems.forEach((item) => {
item.addEventListener("click", function (event) {
event.preventDefault();
const selectedAutomation = item.getAttribute('data-automation');
const selectedAutomation = item.getAttribute("data-automation");
filterByAutomation(selectedAutomation);
// Atualiza a URL sem recarregar a página
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('automation', selectedAutomation);
window.history.replaceState({}, '', newUrl);
newUrl.searchParams.set("automation", selectedAutomation);
window.history.replaceState({}, "", newUrl);
});
});
// Botão para limpar filtro
const clearFilterButton = document.querySelector('#clearFilterButton');
clearFilterButton.addEventListener('click', function () {
projects.forEach(project => {
project.style.display = 'block';
const clearFilterButton = document.querySelector("#clearFilterButton");
clearFilterButton.addEventListener("click", function () {
projects.forEach((project) => {
project.style.display = "block";
});
// Remove o filtro da URL
const newUrl = new URL(window.location.href);
newUrl.searchParams.delete('automation');
window.history.replaceState({}, '', newUrl);
newUrl.searchParams.delete("automation");
window.history.replaceState({}, "", newUrl);
});
});
</script>

View File

@ -3,18 +3,19 @@ layout: default
title: Projetos com a tag Bassline
permalink: /bassline/
---
<meta charset="utf-8">
<meta charset="utf-8" />
<main class="main-content">
<div class="publication">
{% include sidebar.html %}
</div>
<div class="publication">{% include sidebar.html %}</div>
<div class="container">
<div class="columns is-mobile is-vcentered" style="margin-bottom: 2rem;">
<div class="columns is-mobile is-vcentered" style="margin-bottom: 2rem">
<!-- Título -->
<div class="column is-auto">
<h2 class="title is-4"><code>Projetos que possuem a tag bassline:</code></h2>
<h2 class="title is-4">
<code>Projetos que possuem a tag bassline:</code>
</h2>
</div>
<!-- Botão Limpar Filtro -->
<div class="column is-auto">
@ -26,109 +27,111 @@ permalink: /bassline/
<!-- Projetos -->
<div id="project-list" class="columns is-multiline">
{% for projeto in site.data.all %}
{% if projeto.tags.TAG contains "bassline" %}
<div class="column is-6 project-item" data-bassline="{{ projeto.tags.bassline | join: ',' }}">
<div class="box">
<!-- Botão do projeto -->
{% assign file_url = projeto.file | downcase
| replace: ' ', '-'
| replace: 'ç', 'c'
| replace: 'ã', 'a'
| replace: 'á', 'a'
| replace: 'â', 'a'
| replace: 'é', 'e'
| replace: 'ê', 'e'
| replace: 'í', 'i'
| replace: 'ó', 'o'
| replace: 'ô', 'o'
| replace: 'õ', 'o'
| replace: 'ú', 'u' %}
{% for projeto in site.data.all %} {% if projeto.tags.TAG contains
"bassline" %}
<div
class="column is-6 project-item"
data-bassline="{{ projeto.tags.bassline | join: ',' }}"
>
<div class="box">
<!-- Botão do projeto -->
{% assign file_url = projeto.file | downcase | replace: ' ', '-' |
replace: 'ç', 'c' | replace: 'ã', 'a' | replace: 'á', 'a' | replace:
'â', 'a' | replace: 'é', 'e' | replace: 'ê', 'e' | replace: 'í', 'i' |
replace: 'ó', 'o' | replace: 'ô', 'o' | replace: 'õ', 'o' | replace:
'ú', 'u' %}
<a href="../mmp_pages/{{ file_url }}.html" class="button is-link is-fullwidth">
{{ projeto.file }}
</a>
<a
href="../mmp_pages/{{ file_url }}.html"
class="button is-link is-fullwidth"
>
{{ projeto.file }}
</a>
<!-- Lista de Basslines clicáveis -->
{% if projeto.tags.bassline and projeto.tags.bassline.size > 0 %}
<div style="margin-top: 1rem;">
<p><strong>Bassline:</strong></p>
<ul style="list-style-type: disc; padding-left: 1.25rem;">
{% for bassline in projeto.tags.bassline %}
{% if bassline != "" %}
<li><a href="#" class="bassline-item" data-bassline="{{ bassline }}">{{ bassline }}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<!-- Lista de Basslines clicáveis -->
{% if projeto.tags.bassline and projeto.tags.bassline.size > 0 %}
<div style="margin-top: 1rem">
<p><strong>Bassline:</strong></p>
<ul style="list-style-type: disc; padding-left: 1.25rem">
{% for bassline in projeto.tags.bassline %} {% if bassline != ""
%}
<li>
<a href="#" class="bassline-item" data-bassline="{{ bassline }}"
>{{ bassline }}</a
>
</li>
{% endif %} {% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %} {% endfor %}
</div>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', function () {
const projects = document.querySelectorAll('.project-item');
document.addEventListener("DOMContentLoaded", function () {
const projects = document.querySelectorAll(".project-item");
// Função para aplicar filtro por bassline
function filterByBassline(basslineName) {
projects.forEach(project => {
const projectBasslines = project.getAttribute('data-bassline').split(',');
projects.forEach((project) => {
const projectBasslines = project
.getAttribute("data-bassline")
.split(",");
if (projectBasslines.includes(basslineName)) {
project.style.display = 'block';
project.style.display = "block";
} else {
project.style.display = 'none';
project.style.display = "none";
}
});
}
// Lê o parâmetro bassline da URL (se houver)
const urlParams = new URLSearchParams(window.location.search);
let basslineFromUrl = urlParams.get('bassline');
let basslineFromUrl = urlParams.get("bassline");
// Substituir "+" por " " (espaços)
if (basslineFromUrl) {
basslineFromUrl = basslineFromUrl.replace(/\+/g, ' ');
basslineFromUrl = basslineFromUrl.replace(/\+/g, " ");
filterByBassline(basslineFromUrl);
}
// Ao clicar numa bassline na lista
const basslineItems = document.querySelectorAll('.bassline-item');
const basslineItems = document.querySelectorAll(".bassline-item");
if (basslineItems.length > 0) {
basslineItems.forEach(item => {
item.addEventListener('click', function (event) {
basslineItems.forEach((item) => {
item.addEventListener("click", function (event) {
event.preventDefault();
const selectedBassline = item.getAttribute('data-bassline');
const selectedBassline = item.getAttribute("data-bassline");
// Substitui espaços por "+"
const basslineForUrl = selectedBassline.replace(/ /g, '+');
const basslineForUrl = selectedBassline.replace(/ /g, "+");
filterByBassline(selectedBassline);
// Atualiza a URL sem recarregar a página
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('bassline', basslineForUrl);
window.history.replaceState({}, '', newUrl);
newUrl.searchParams.set("bassline", basslineForUrl);
window.history.replaceState({}, "", newUrl);
});
});
}
// Botão para limpar filtro
const clearFilterButton = document.querySelector('#clearFilterButton');
const clearFilterButton = document.querySelector("#clearFilterButton");
if (clearFilterButton) {
clearFilterButton.addEventListener('click', function () {
projects.forEach(project => {
project.style.display = 'block';
clearFilterButton.addEventListener("click", function () {
projects.forEach((project) => {
project.style.display = "block";
});
// Remove o parâmetro bassline da URL
const newUrl = new URL(window.location.href);
newUrl.searchParams.delete('bassline');
window.history.replaceState({}, '', newUrl);
newUrl.searchParams.delete("bassline");
window.history.replaceState({}, "", newUrl);
});
}
});

201
creation.html Normal file
View File

@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MMPCreator</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
/>
<link rel="stylesheet" href="assets/css/style.css" />
</head>
<body>
<aside class="sample-browser">
<div class="browser-header">Navegador de Samples</div>
<div class="browser-content" id="browser-content"></div>
</aside>
<button id="sidebar-toggle"><i class="fa-solid fa-caret-left"></i></button>
<header class="global-toolbar">
<div class="control-group">
<i
class="fa-solid fa-file"
id="new-project-btn"
title="Novo Projeto"
></i>
<i
class="fa-solid fa-folder-open"
id="open-mmp-btn"
title="Abrir Projeto (.mmp)"
></i>
<i
class="fa-solid fa-save"
id="save-mmp-btn"
title="Salvar Projeto (.mmp)"
></i>
</div>
<div class="divider"></div>
<div class="control-group">
<i
class="fa-solid fa-backward-step"
id="rewind-btn"
title="Voltar ao Início"
></i>
<i class="fa-solid fa-play" title="Play/Pause Global (Futuro)"></i>
<i class="fa-solid fa-stop" title="Stop Global (Futuro)"></i>
<i class="fa-solid fa-circle-dot" title="Gravar"></i>
</div>
<div class="divider"></div>
<div class="info-display-group">
<div class="info-display">
<div class="interactive-input-container">
<button class="adjust-btn" data-target="bpm" data-step="-1">
-</button
><input
type="text"
class="value-input"
id="bpm-input"
value="140"
data-min="20"
data-max="400"
/><button class="adjust-btn" data-target="bpm" data-step="1">
+
</button>
</div>
<div class="label">ANDAMENTO/BPM</div>
</div>
<div class="info-display">
<div class="interactive-input-container">
<div class="compasso-group">
<button
class="adjust-btn"
data-target="compasso-a"
data-step="-1"
>
-</button
><input
type="text"
class="value-input compasso-input"
id="compasso-a-input"
value="4"
data-min="1"
data-max="16"
/><button
class="adjust-btn"
data-target="compasso-a"
data-step="1"
>
+
</button>
</div>
<span class="compasso-separator">/</span>
<div class="compasso-group">
<button
class="adjust-btn"
data-target="compasso-b"
data-step="-1"
>
-</button
><input
type="text"
class="value-input compasso-input"
id="compasso-b-input"
value="4"
data-min="1"
data-max="16"
/><button
class="adjust-btn"
data-target="compasso-b"
data-step="1"
>
+
</button>
</div>
</div>
<div class="label">COMPASSO</div>
</div>
<div class="info-display">
<div
class="interactive-input-container"
style="font-size: 0.7rem; color: var(--text-dark)"
>
0:00:00
</div>
<div class="label">MIN:SEC:MSEC</div>
</div>
</div>
<div class="control-group">
<button id="metronome-btn" title="Metrônomo On/Off">Metrônomo</button>
</div>
<div class="spacer"></div>
</header>
<main class="main-content">
<div class="beat-editor">
<div class="editor-header">
Mostrar/esconder Editor de Bases
<div class="window-controls">
<i class="fa-solid fa-minus"></i><i class="fa-regular fa-square"></i
><i class="fa-solid fa-xmark"></i>
</div>
</div>
<div class="editor-toolbar">
<div class="playback-controls">
<i class="fa-solid fa-play" id="play-btn" title="Play/Pause"></i>
<i class="fa-solid fa-stop" id="stop-btn" title="Stop"></i>
</div>
<div class="pattern-manager">
<select
id="pattern-selector"
class="pattern-selector-dropdown"
></select>
<button id="add-pattern-btn" class="pattern-btn">+</button>
<button id="remove-pattern-btn" class="pattern-btn">-</button>
</div>
<div class="tool-icons">
<i class="fa-solid fa-table-cells"></i
><i class="fa-solid fa-bars-staggered"></i
><i class="fa-solid fa-wave-square enabled"></i
><i class="fa-solid fa-plus"></i>
</div>
<div class="zoom-controls">
<i class="fa-solid fa-minus" id="remove-instrument-btn"></i
><i class="fa-solid fa-plus" id="add-instrument-btn"></i>
</div>
</div>
<div id="track-container"></div>
</div>
</main>
<input
type="file"
id="mmp-file-input"
accept=".mmp, .mmpz"
style="display: none"
/>
<div class="modal-overlay" id="open-project-modal">
<div class="modal-content">
<button class="modal-close" id="open-modal-close-btn">&times;</button>
<h2 class="modal-title">Abrir Projeto</h2>
<div class="modal-section">
<h3>Projetos no Servidor</h3>
<div id="server-projects-list"><p>Carregando...</p></div>
</div>
<div class="modal-section">
<h3>Carregar do Computador</h3>
<button class="modal-button" id="load-from-computer-btn">
<i class="fa-solid fa-desktop"></i> Selecionar arquivo .mmp ou .mmpz
</button>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="assets/js/creations/main.js" type="module" defer></script>
</body>
</html>

View File

@ -0,0 +1,21 @@
[
"Computacao_Musical_Thiago_Rocha_de_Moraes.mmp",
"FelipeFerraz.mmp",
"Gustavo Castro.mmp",
"Iara Rodrigues.mmp",
"JoãoVitorSimão-2023008380-megaMainSong.mmp",
"Samanta Freire.mmp",
"SonsAlignígenas.mmp",
"Um começo.mmp",
"VitorAugusto.mmp",
"Wesley_Silva_Guimarães.mmp",
"beatJulioCesardeSousa.mmp",
"carlos.mmp",
"deprecado_wallace.mmp",
"eliasMendesSong.mmp",
"gabriel_gomes.mmp",
"leandro_souza_atvd1.mmp",
"teste.mmp",
"tp1 - Luiz Filipe Almada.mmp",
"tp1_MarcoAntonio.mmp"
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
import os
import json
# --- Configuração de Caminhos Dinâmicos ---
# Descobre o caminho absoluto do script que está sendo executado.
# __file__ é uma variável especial do Python que contém o caminho para o arquivo atual.
SCRIPT_PATH = os.path.abspath(__file__)
# Descobre o diretório onde o script está (ex: /.../mmpSearch/scripts/creation)
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
# Define a raiz do projeto subindo dois níveis de diretório (de 'creation' para 'scripts', e de 'scripts' para 'mmpSearch')
PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..'))
print(f"Raiz do projeto detectada em: {PROJECT_ROOT}")
# --- Configuração dos Manifestos ---
# Os caminhos aqui são relativos à RAIZ DO PROJETO.
CONFIGS = [
{
"source_dir": "src/samples",
"output_file": "metadata/samples-manifest.json",
"scan_type": "tree"
},
{
"source_dir": "mmp",
"output_file": "metadata/mmp-manifest.json",
"scan_type": "list",
"extensions": ['.mmp', '.mmpz']
}
]
def scan_directory_tree(path):
"""
Função recursiva que escaneia um diretório e constrói uma
estrutura de dicionário (árvore) representando as pastas e arquivos.
"""
tree = {}
if not os.path.isdir(path):
return tree
for item in os.listdir(path):
full_path = os.path.join(path, item)
if os.path.isdir(full_path):
tree[item] = scan_directory_tree(full_path)
else:
# (CORREÇÃO) Volte a usar o objeto que o JavaScript espera.
tree[item] = {"_isFile": True}
return tree
def scan_directory_list(path, allowed_extensions):
"""
Função que escaneia um diretório e retorna uma lista simples
de nomes de arquivos, filtrando por extensões permitidas.
"""
file_list = []
if not os.path.isdir(path): # Adiciona verificação
return file_list
for item in os.listdir(path):
full_path = os.path.join(path, item)
if os.path.isfile(full_path) and any(item.lower().endswith(ext) for ext in allowed_extensions):
file_list.append(item)
return sorted(file_list)
# --- Execução Principal do Script ---
if __name__ == "__main__":
print("\nIniciando geração de arquivos de manifesto...")
for config in CONFIGS:
# Monta os caminhos ABSOLUTOS usando a raiz do projeto
source_dir_abs = os.path.join(PROJECT_ROOT, config["source_dir"])
output_file_abs = os.path.join(PROJECT_ROOT, config["output_file"])
scan_type = config["scan_type"]
print(f"\nProcessando '{config['source_dir']}' -> '{config['output_file']}'...")
if not os.path.isdir(source_dir_abs):
print(f"AVISO: A pasta de origem '{source_dir_abs}' não foi encontrada. Arquivo não será gerado.")
continue
result_data = None
try:
if scan_type == "tree":
result_data = scan_directory_tree(source_dir_abs)
elif scan_type == "list":
extensions = config.get("extensions", [])
result_data = scan_directory_list(source_dir_abs, extensions)
if result_data is not None:
# Garante que o diretório de saída exista antes de salvar
output_dir = os.path.dirname(output_file_abs)
os.makedirs(output_dir, exist_ok=True)
with open(output_file_abs, 'w', encoding='utf-8') as f:
json.dump(result_data, f, indent=2, ensure_ascii=False)
print(f"SUCESSO: Arquivo '{output_file_abs}' gerado!")
except Exception as e:
print(f"ERRO inesperado ao processar a pasta '{source_dir_abs}': {e}")
print("\nGeração de manifestos concluída.")

View File

@ -26,6 +26,8 @@ def parse_automation(file_path):
automation_info['automationtrack'] = 'Present'
# Lendo as tags de automationpattern (caso existam)
# caso não existam, seta um valor default
# (assim acontece para, basicamente, todas as tags)
for automationpattern in track.findall('./automationpattern'):
automation_pattern_info = {
'name': automationpattern.attrib.get('name', 'N/A'),

179
src/dtd/mmp.md Normal file
View File

@ -0,0 +1,179 @@
# Documentação do Arquivo .mmp (LMMS)
## `<lmms-project>`
- **Ocorrências**: 1
- **Atributos**:
- `version` (**Obrigatório**) — Exemplos: `1.0`
- `type` (**Obrigatório**) — Exemplos: `song`
- `creator` (**Obrigatório**) — Exemplos: `LMMS`
- `creatorversion` (**Obrigatório**) — Exemplos: `1.2.2`
## `<head>`
- **Ocorrências**: 1
- **Atributos**:
- `mastervol` (**Obrigatório**) — Exemplos: `100`
- `timesig_denominator` (**Obrigatório**) — Exemplos: `4`
- `bpm` (**Obrigatório**) — Exemplos: `140`
- `timesig_numerator` (**Obrigatório**) — Exemplos: `4`
- `masterpitch` (**Obrigatório**) — Exemplos: `0`
## `<song>`
- **Ocorrências**: 1
- *(Sem atributos)*
## `<trackcontainer>`
- **Ocorrências**: 2
- **Atributos**:
- `width` (**Obrigatório**) — Exemplos: `700`, `600`
- `y` (**Obrigatório**) — Exemplos: `50`, `5`
- `minimized` (**Obrigatório**) — Exemplos: `0`
- `type` (**Obrigatório**) — Exemplos: `bbtrackcontainer`, `song`
- `visible` (**Obrigatório**) — Exemplos: `1`
- `x` (**Obrigatório**) — Exemplos: `266`, `5`
- `maximized` (**Obrigatório**) — Exemplos: `0`
- `height` (**Obrigatório**) — Exemplos: `400`, `300`
## `<track>`
- **Ocorrências**: 7
- **Atributos**:
- `type` (**Obrigatório**) — Exemplos: `5`, `0`, `1`
- `solo` (**Obrigatório**) — Exemplos: `0`
- `muted` (**Obrigatório**) — Exemplos: `0`
- `name` (**Obrigatório**) — Exemplos: `Automation track`, `Kicker`
## `<instrumenttrack>`
- **Ocorrências**: 3
- **Atributos**:
- `vol` (**Obrigatório**) — Exemplos: `100`
- `pitch` (**Obrigatório**) — Exemplos: `0`
- `fxch` (**Obrigatório**) — Exemplos: `0`
- `pitchrange` (**Obrigatório**) — Exemplos: `1`
- `basenote` (**Obrigatório**) — Exemplos: `57`
- `usemasterpitch` (**Obrigatório**) — Exemplos: `1`
- `pan` (**Obrigatório**) — Exemplos: `0`
## `<instrument>`
- **Ocorrências**: 3
- **Atributos**:
- `name` (**Obrigatório**) — Exemplos: `audiofileprocessor`, `kicker`, `tripleoscillator`
## `<tripleoscillator>`
- **Ocorrências**: 1
- **Atributos**:
- Todos são **Obrigatórios** neste contexto (pois há uma única ocorrência)
- Exemplos incluem:
- `wavetype0`: `0`, `userwavefile0`: ``, `vol0`: `33`, `pan0`: `0`, `phoffset0`: `0`, etc.
## `<eldata>`, `<elvol>`, `<elcut>`, `<elres>`
- **Ocorrências**: 3 (cada)
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `att`: `0`, `dec`: `0.5`, `rel`: `0.1`, `lspd`: `0.1`, `userwavefile`: ``
## `<chordcreator>`
- **Ocorrências**: 3
- **Atributos**:
- `chordrange` (**Obrigatório**) — Exemplos: `1`
- `chord-enabled` (**Obrigatório**) — Exemplos: `0`
- `chord` (**Obrigatório**) — Exemplos: `0`
## `<arpeggiator>`
- **Ocorrências**: 3
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `arp`: `0`, `arpdir`: `0`, `arpgate`: `100`
## `<midiport>`
- **Ocorrências**: 3
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `inputchannel`: `0`, `outputchannel`: `1`
## `<fxchain>`
- **Ocorrências**: 5
- **Atributos**:
- `enabled` (**Obrigatório**) — Exemplos: `0`
- `numofeffects` (**Obrigatório**) — Exemplos: `0`
## `<sampletrack>`
- **Ocorrências**: 1
- **Atributos**:
- `vol`, `pan` (**Obrigatórios**) — Exemplos: `100`, `0`
## `<bbtrack>`
- **Ocorrências**: 1
- *(Sem atributos)*
## `<kicker>`
- **Ocorrências**: 1
- **Atributos**:
- Todos são **Obrigatórios**
- Exemplos: `decay`: `440`, `dist`: `0.8`, `startfreq`: `150`
## `<pattern>`
- **Ocorrências**: 2
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `steps`: `16`, `name`: `Kicker`, `clap04.ogg`
## `<note>`
- **Ocorrências**: 5
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `key`: `57`, `vol`: `100`, `len`: `-192`
## `<audiofileprocessor>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `src`: `drums/clap04.ogg`, `looped`: `0`, `reversed`: `0`
## `<bbtco>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `name`: ``, `pos`: `0`, `len`: `192`
## `<automationtrack>`
- **Ocorrências**: 2
- *(Sem atributos)*
## `<automationpattern>`
- **Ocorrências**: 5
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `name`: `Tempo`, `len`: `192`, `prog`: `0`
## `<fxmixer>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `width`: `543`, `height`: `333`, `visible`: `1`
## `<fxchannel>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `name`: `Master`, `volume`: `1`
## `<ControllerRackView>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `width`: `350`, `visible`: `1`
## `<pianoroll>`, `<automationeditor>`, `<projectnotes>`
- **Ocorrências**: 1 (cada)
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `visible`: `0`, `width`: `860`
## `<timeline>`
- **Ocorrências**: 1
- **Atributos**:
- Todos **Obrigatórios**
- Exemplos: `lp0pos`: `0`, `lp1pos`: `192`
## `<controllers>`
- **Ocorrências**: 1
- *(Sem atributos)*

512
src/dtd/mmp.xsd Normal file
View File

@ -0,0 +1,512 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<!-- Raiz do projeto -->
<xs:element name="lmms-project">
<xs:complexType>
<xs:sequence>
<xs:element name="head" type="HeadType" />
<xs:element name="song" type="SongType" />
<xs:element name="fxmixer" type="FxMixerType" minOccurs="0" />
<xs:element name="ControllerRackView" type="SimplePanelType" minOccurs="0" />
<xs:element name="pianoroll" type="SimplePanelType" minOccurs="0" />
<xs:element name="automationeditor" type="SimplePanelType" minOccurs="0" />
<xs:element name="projectnotes" type="SimplePanelType" minOccurs="0" />
<xs:element name="timeline" type="TimelineType" minOccurs="0" />
<xs:element name="controllers" type="ControllersType" minOccurs="0" />
</xs:sequence>
<xs:attribute name="version" type="xs:string" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
<xs:attribute name="creator" type="xs:string" use="required" />
<xs:attribute name="creatorversion" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<!-- Cabeçalho do projeto -->
<xs:complexType name="HeadType">
<xs:attribute name="bpm" type="xs:decimal" use="required" />
<xs:attribute name="timesig_numerator" type="xs:integer" use="required" />
<xs:attribute name="timesig_denominator" type="xs:integer" use="required" />
<xs:attribute name="mastervol" type="xs:integer" use="required" />
<xs:attribute name="masterpitch" type="xs:integer" use="required" />
</xs:complexType>
<!-- SongType (RAÍZ) -->
<xs:complexType name="SongType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
</xs:complexType>
<!-- Container de trilhas -->
<xs:element name="trackcontainer">
<xs:complexType>
<xs:sequence>
<xs:element name="track" type="TrackType" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="type" type="xs:string" use="required" />
<xs:attribute name="x" type="xs:integer" use="required" />
<xs:attribute name="y" type="xs:integer" use="required" />
<xs:attribute name="width" type="xs:integer" use="required" />
<xs:attribute name="height" type="xs:integer" use="required" />
<xs:attribute name="visible" type="xs:boolean" use="required" />
<xs:attribute name="minimized" type="xs:boolean" use="required" />
<xs:attribute name="maximized" type="xs:boolean" use="required" />
</xs:complexType>
</xs:element>
<!-- Trilhas -->
<xs:complexType name="TrackType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:attribute name="type" type="xs:integer" use="required" />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="muted" type="xs:boolean" use="required" />
<xs:attribute name="solo" type="xs:boolean" use="required" />
</xs:complexType>
<!-- FxMixerType -->
<xs:complexType name="FxMixerType">
<xs:sequence>
<xs:element name="fxchannel" type="FxChannelType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="minimized" type="xs:boolean" />
<xs:attribute name="maximized" type="xs:boolean" />
<xs:attribute name="height" type="xs:integer" />
<xs:attribute name="y" type="xs:integer" />
<xs:attribute name="x" type="xs:integer" />
<xs:attribute name="visible" type="xs:boolean" />
<xs:attribute name="width" type="xs:integer" />
</xs:complexType>
<!-- FxChannelType -->
<xs:complexType name="FxChannelType">
<xs:sequence>
<xs:element name="fxchain" type="FxChainType" minOccurs="0" maxOccurs="1" />
<xs:element name="send" type="SendType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="muted" type="xs:boolean" />
<xs:attribute name="num" type="xs:integer" />
<xs:attribute name="volume" type="xs:decimal" />
<xs:attribute name="soloed" type="xs:boolean" />
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
<!-- FxChainType -->
<xs:complexType name="FxChainType">
<xs:attribute name="enabled" type="xs:boolean" />
<xs:attribute name="numofeffects" type="xs:integer" />
</xs:complexType>
<!-- SendType -->
<xs:complexType name="SendType">
<xs:attribute name="channel" type="xs:integer" />
<xs:attribute name="amount" type="xs:decimal" />
</xs:complexType>
<!-- Trilha de instrumento -->
<xs:element name="instrumenttrack">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="vol" type="xs:integer" use="required" />
<xs:attribute name="pan" type="xs:integer" use="required" />
<xs:attribute name="pitch" type="xs:integer" use="required" />
<xs:attribute name="fxch" type="xs:integer" use="required" />
<xs:attribute name="basenote" type="xs:integer" use="required" />
<xs:attribute name="usemasterpitch" type="xs:boolean" use="required" />
<xs:attribute name="pitchrange" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<!-- Pattern -->
<xs:element name="pattern">
<xs:complexType>
<xs:sequence>
<xs:element name="note" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="pan" type="xs:integer" use="required"/>
<xs:attribute name="pos" type="xs:integer" use="required"/>
<xs:attribute name="key" type="xs:integer" use="required"/>
<xs:attribute name="vol" type="xs:integer" use="required"/>
<xs:attribute name="len" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="muted" type="xs:integer" use="required"/>
<xs:attribute name="steps" type="xs:integer" use="required"/>
<xs:attribute name="type" type="xs:integer" use="required"/>
<xs:attribute name="pos" type="xs:integer" use="required"/>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<!-- automationtrack -->
<xs:element name="automationtrack">
<xs:complexType/>
</xs:element>
<!-- automationpattern -->
<xs:element name="automationpattern">
<xs:complexType>
<xs:attribute name="mute" type="xs:integer" use="required"/>
<xs:attribute name="prog" type="xs:integer" use="required"/>
<xs:attribute name="pos" type="xs:integer" use="required"/>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="tens" type="xs:integer" use="required"/>
<xs:attribute name="len" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
<!-- sampletrack -->
<xs:element name="sampletrack">
<xs:complexType>
<xs:sequence>
<xs:element name="fxchain" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:attribute name="enabled" type="xs:int" use="required"/>
<xs:attribute name="numofeffects" type="xs:int" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="pan" type="xs:int" use="required"/>
<xs:attribute name="vol" type="xs:int" use="required"/>
</xs:complexType>
</xs:element>
<!-- Instrumento TEM QUE VERIFICAR -->
<xs:element name="name">
<xs:complexType>
<xs:choice>
<xs:element name="audiofileprocessor">
<xs:complexType>
<xs:attribute name="eframe" type="xs:integer" use="required" />
<xs:attribute name="stutter" type="xs:boolean" use="required" />
<xs:attribute name="looped" type="xs:boolean" use="required" />
<xs:attribute name="interp" type="xs:boolean" use="required" />
<xs:attribute name="reversed" type="xs:boolean" use="required" />
<xs:attribute name="lframe" type="xs:integer" use="required" />
<xs:attribute name="src" type="xs:string" use="required" />
<xs:attribute name="amp" type="xs:integer" use="required" />
<xs:attribute name="sframe" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="kicker">
<xs:complexType>
<xs:attribute name="decay" type="xs:decimal" use="required" />
<xs:attribute name="gain" type="xs:decimal" use="required" />
<xs:attribute name="slope" type="xs:decimal" use="required" />
<xs:attribute name="endfreq" type="xs:decimal" use="required" />
<xs:attribute name="startfreq" type="xs:decimal" use="required" />
<xs:attribute name="version" type="xs:integer" use="required" />
<xs:attribute name="dist" type="xs:decimal" use="required" />
<xs:attribute name="click" type="xs:decimal" use="required" />
<xs:attribute name="distend" type="xs:decimal" use="required" />
<xs:attribute name="env" type="xs:decimal" use="required" />
<xs:attribute name="endnote" type="xs:integer" use="required" />
<xs:attribute name="startnote" type="xs:integer" use="required" />
<xs:attribute name="decay_numerator" type="xs:integer" use="required" />
<xs:attribute name="decay_syncmode" type="xs:integer" use="required" />
<xs:attribute name="decay_denominator" type="xs:integer" use="required" />
<xs:attribute name="noise" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="tripleoscillator">
<xs:complexType>
<xs:attribute name="pan0" type="xs:decimal" use="required" />
<xs:attribute name="phoffset1" type="xs:decimal" use="required" />
<xs:attribute name="modalgo2" type="xs:integer" use="required" />
<xs:attribute name="vol0" type="xs:integer" use="required" />
<xs:attribute name="wavetype1" type="xs:integer" use="required" />
<xs:attribute name="userwavefile2" type="xs:string" use="required" />
<xs:attribute name="phoffset2" type="xs:decimal" use="required" />
<xs:attribute name="modalgo1" type="xs:integer" use="required" />
<xs:attribute name="finer0" type="xs:integer" use="required" />
<xs:attribute name="finer1" type="xs:integer" use="required" />
<xs:attribute name="finer2" type="xs:integer" use="required" />
<xs:attribute name="coarse1" type="xs:integer" use="required" />
<xs:attribute name="stphdetun0" type="xs:decimal" use="required" />
<xs:attribute name="stphdetun1" type="xs:decimal" use="required" />
<xs:attribute name="modalgo3" type="xs:integer" use="required" />
<xs:attribute name="pan1" type="xs:decimal" use="required" />
<xs:attribute name="finel2" type="xs:integer" use="required" />
<xs:attribute name="coarse0" type="xs:integer" use="required" />
<xs:attribute name="userwavefile0" type="xs:string" use="required" />
<xs:attribute name="userwavefile1" type="xs:string" use="required" />
<xs:attribute name="stphdetun2" type="xs:decimal" use="required" />
<xs:attribute name="finel1" type="xs:integer" use="required" />
<xs:attribute name="vol2" type="xs:integer" use="required" />
<xs:attribute name="pan2" type="xs:decimal" use="required" />
<xs:attribute name="finel0" type="xs:integer" use="required" />
<xs:attribute name="vol1" type="xs:integer" use="required" />
<xs:attribute name="phoffset0" type="xs:decimal" use="required" />
<xs:attribute name="coarse2" type="xs:integer" use="required" />
<xs:attribute name="wavetype0" type="xs:integer" use="required" />
<xs:attribute name="wavetype2" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="BitinvaderType">
<xs:complexType>
<xs:attribute name="sampleLength" type="xs:integer" use="required" />
<xs:attribute name="version" type="xs:string" use="required" />
<xs:attribute name="sampleShape" type="xs:string" use="required" />
<xs:attribute name="interpolation" type="xs:integer" use="optional" />
<xs:attribute name="normalize" type="xs:integer" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="PapuType">
<xs:complexType>
<xs:attribute name="ch4vsd" type="xs:integer" use="optional" />
<xs:attribute name="ch1wpd" type="xs:integer" use="optional" />
<xs:attribute name="ch1so2" type="xs:integer" use="optional" />
<xs:attribute name="ch1ssl" type="xs:integer" use="optional" />
<xs:attribute name="Treble" type="xs:integer" use="optional" />
<xs:attribute name="sd" type="xs:integer" use="optional" />
<xs:attribute name="ch2wpd" type="xs:integer" use="optional" />
<xs:attribute name="ch3so2" type="xs:integer" use="optional" />
<xs:attribute name="srs" type="xs:integer" use="optional" />
<xs:attribute name="ch2vol" type="xs:integer" use="optional" />
<xs:attribute name="ch1vsd" type="xs:integer" use="optional" />
<xs:attribute name="ch4so2" type="xs:integer" use="optional" />
<xs:attribute name="ch1so1" type="xs:integer" use="optional" />
<xs:attribute name="srw" type="xs:integer" use="optional" />
<xs:attribute name="ch2vsd" type="xs:integer" use="optional" />
<xs:attribute name="ch3so1" type="xs:integer" use="optional" />
<xs:attribute name="ch4ssl" type="xs:integer" use="optional" />
<xs:attribute name="st" type="xs:integer" use="optional" />
<xs:attribute name="ch1vol" type="xs:integer" use="optional" />
<xs:attribute name="ch2so1" type="xs:integer" use="optional" />
<xs:attribute name="ch4so1" type="xs:integer" use="optional" />
<xs:attribute name="sampleShape" type="xs:string" use="optional" />
<xs:attribute name="ch2so2" type="xs:integer" use="optional" />
<xs:attribute name="Bass" type="xs:integer" use="optional" />
<xs:attribute name="ch3vol" type="xs:integer" use="optional" />
<xs:attribute name="ch2ssl" type="xs:integer" use="optional" />
<xs:attribute name="so1vol" type="xs:integer" use="optional" />
<xs:attribute name="ch4vol" type="xs:integer" use="optional" />
<xs:attribute name="so2vol" type="xs:integer" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="GigplayerType">
<xs:complexType>
<xs:attribute name="patch" type="xs:integer" use="optional" />
<xs:attribute name="src" type="xs:string" use="optional" />
<xs:attribute name="gain" type="xs:decimal" use="optional" />
<xs:attribute name="bank" type="xs:integer" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="Lb302Type">
<xs:complexType>
<xs:attribute name="vcf_dec" type="xs:decimal" use="optional" />
<xs:attribute name="vcf_mod" type="xs:decimal" use="optional" />
<xs:attribute name="slide" type="xs:integer" use="optional" />
<xs:attribute name="dist" type="xs:integer" use="optional" />
<xs:attribute name="slide_dec" type="xs:decimal" use="optional" />
<xs:attribute name="dead" type="xs:integer" use="optional" />
<xs:attribute name="vcf_cut" type="xs:decimal" use="optional" />
<xs:attribute name="vcf_res" type="xs:decimal" use="optional" />
<xs:attribute name="shape" type="xs:integer" use="optional" />
<xs:attribute name="db24" type="xs:integer" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="MalletsstkType">
<xs:complexType>
<xs:attribute name="vib_freq" type="xs:integer" use="optional" />
<xs:attribute name="preset" type="xs:integer" use="optional" />
<xs:attribute name="velocity" type="xs:integer" use="optional" />
<xs:attribute name="vib_gain" type="xs:integer" use="optional" />
<xs:attribute name="spread" type="xs:integer" use="optional" />
<xs:attribute name="lfo_speed" type="xs:integer" use="optional" />
<xs:attribute name="version" type="xs:integer" use="optional" />
<xs:attribute name="stick_mix" type="xs:integer" use="optional" />
<xs:attribute name="adsr" type="xs:integer" use="optional" />
<xs:attribute name="hardness" type="xs:integer" use="optional" />
<xs:attribute name="oldversion" type="xs:integer" use="optional" />
<xs:attribute name="modulator" type="xs:integer" use="optional" />
<xs:attribute name="lfo_depth" type="xs:integer" use="optional" />
<xs:attribute name="pressure" type="xs:integer" use="optional" />
<xs:attribute name="strike" type="xs:integer" use="optional" />
<xs:attribute name="crossfade" type="xs:integer" use="optional" />
<xs:attribute name="position" type="xs:integer" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="watsyn">
<xs:complexType>
<xs:attribute name="a1_pan" type="xs:int" use="required" />
<xs:attribute name="envDec" type="xs:int" use="required" />
<xs:attribute name="a2_rtune" type="xs:int" use="required" />
<xs:attribute name="amod" type="xs:int" use="required" />
<xs:attribute name="b1_vol" type="xs:int" use="required" />
<xs:attribute name="b2_pan" type="xs:int" use="required" />
<xs:attribute name="abmix" type="xs:int" use="required" />
<xs:attribute name="xtalk" type="xs:int" use="required" />
<xs:attribute name="a1_wave" type="xs:string" use="required" />
<xs:attribute name="a1_rtune" type="xs:int" use="required" />
<xs:attribute name="envHold" type="xs:int" use="required" />
<xs:attribute name="b2_vol" type="xs:int" use="required" />
<xs:attribute name="b2_mult" type="xs:int" use="required" />
<xs:attribute name="a1_vol" type="xs:int" use="required" />
<xs:attribute name="a2_ltune" type="xs:int" use="required" />
<xs:attribute name="bmod" type="xs:int" use="required" />
<xs:attribute name="b1_mult" type="xs:int" use="required" />
<xs:attribute name="envAtt" type="xs:int" use="required" />
<xs:attribute name="envAtt_denominator" type="xs:int" use="required" />
<xs:attribute name="envHold_numerator" type="xs:int" use="required" />
<xs:attribute name="b2_ltune" type="xs:int" use="required" />
<xs:attribute name="b1_rtune" type="xs:int" use="required" />
<xs:attribute name="a1_mult" type="xs:int" use="required" />
<xs:attribute name="a2_vol" type="xs:int" use="required" />
<xs:attribute name="a2_pan" type="xs:int" use="required" />
<xs:attribute name="envDec_numerator" type="xs:int" use="required" />
<xs:attribute name="envDec_syncmode" type="xs:int" use="required" />
<xs:attribute name="envDec_denominator" type="xs:int" use="required" />
<xs:attribute name="envAmt" type="xs:int" use="required" />
<xs:attribute name="b2_wave" type="xs:string" use="required" />
<xs:attribute name="envHold_syncmode" type="xs:int" use="required" />
<xs:attribute name="a2_mult" type="xs:int" use="required" />
<xs:attribute name="b2_rtune" type="xs:int" use="required" />
<xs:attribute name="a2_wave" type="xs:string" use="required" />
<xs:attribute name="envAtt_syncmode" type="xs:int" use="required" />
<xs:attribute name="envAtt_numerator" type="xs:int" use="required" />
<xs:attribute name="b1_ltune" type="xs:int" use="required" />
<xs:attribute name="envHold_denominator" type="xs:int" use="required" />
<xs:attribute name="a1_ltune" type="xs:int" use="required" />
<xs:attribute name="b1_pan" type="xs:int" use="required" />
<xs:attribute name="b1_wave" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="vibedstrings">
<xs:complexType>
<xs:attribute name="pick0" type="xs:int" use="required" />
<xs:attribute name="active6" type="xs:int" use="required" />
<xs:attribute name="active7" type="xs:int" use="required" />
<xs:attribute name="octave0" type="xs:int" use="required" />
<xs:attribute name="active1" type="xs:int" use="required" />
<xs:attribute name="impulse0" type="xs:int" use="required" />
<xs:attribute name="active4" type="xs:int" use="required" />
<xs:attribute name="version" type="xs:string" use="required" />
<xs:attribute name="active5" type="xs:int" use="required" />
<xs:attribute name="detune0" type="xs:int" use="required" />
<xs:attribute name="slap0" type="xs:int" use="required" />
<xs:attribute name="volume0" type="xs:int" use="required" />
<xs:attribute name="pickup0" type="xs:decimal" use="required" />
<xs:attribute name="active8" type="xs:int" use="required" />
<xs:attribute name="active2" type="xs:int" use="required" />
<xs:attribute name="active0" type="xs:int" use="required" />
<xs:attribute name="stiffness0" type="xs:int" use="required" />
<xs:attribute name="pan0" type="xs:int" use="required" />
<xs:attribute name="graph0" type="xs:string" use="required" />
<xs:attribute name="length0" type="xs:int" use="required" />
<xs:attribute name="active3" type="xs:int" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="vestige">
<xs:complexType>
<xs:attribute name="plugin" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="sid">
<xs:complexType>
<xs:attribute name="pulsewidth2" type="xs:int" use="required" />
<xs:attribute name="release0" type="xs:int" use="required" />
<xs:attribute name="ringmod2" type="xs:int" use="required" />
<xs:attribute name="filterResonance" type="xs:int" use="required" />
<xs:attribute name="attack0" type="xs:int" use="required" />
<xs:attribute name="filtered0" type="xs:int" use="required" />
<xs:attribute name="chipModel" type="xs:int" use="required" />
<xs:attribute name="waveform0" type="xs:int" use="required" />
<xs:attribute name="sustain0" type="xs:int" use="required" />
<xs:attribute name="attack2" type="xs:int" use="required" />
<xs:attribute name="ringmod1" type="xs:int" use="required" />
<xs:attribute name="sync1" type="xs:int" use="required" />
<xs:attribute name="release2" type="xs:int" use="required" />
<xs:attribute name="filtered1" type="xs:int" use="required" />
<xs:attribute name="release1" type="xs:int" use="required" />
<xs:attribute name="waveform2" type="xs:int" use="required" />
<xs:attribute name="voice3Off" type="xs:int" use="required" />
<xs:attribute name="pulsewidth1" type="xs:int" use="required" />
<xs:attribute name="sustain1" type="xs:int" use="required" />
<xs:attribute name="test0" type="xs:int" use="required" />
<xs:attribute name="sync0" type="xs:int" use="required" />
<xs:attribute name="sync2" type="xs:int" use="required" />
<xs:attribute name="sustain2" type="xs:int" use="required" />
<xs:attribute name="coarse2" type="xs:int" use="required" />
<xs:attribute name="decay2" type="xs:int" use="required" />
<xs:attribute name="coarse0" type="xs:int" use="required" />
<xs:attribute name="ringmod0" type="xs:int" use="required" />
<xs:attribute name="volume" type="xs:int" use="required" />
<xs:attribute name="filtered2" type="xs:int" use="required" />
<xs:attribute name="filterMode" type="xs:int" use="required" />
<xs:attribute name="waveform1" type="xs:int" use="required" />
<xs:attribute name="decay0" type="xs:int" use="required" />
<xs:attribute name="decay1" type="xs:int" use="required" />
<xs:attribute name="pulsewidth0" type="xs:int" use="required" />
<xs:attribute name="voice1Off" type="xs:int" use="required" />
<xs:attribute name="coarse1" type="xs:int" use="required" />
<xs:attribute name="voice2Off" type="xs:int" use="required" />
<xs:attribute name="attack1" type="xs:int" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="analo4">
<xs:complexType>
<xs:attribute name="freq" type="xs:int" use="required" />
<xs:attribute name="sweep" type="xs:int" use="required" />
<xs:attribute name="envAmt" type="xs:int" use="required" />
<xs:attribute name="cutoff" type="xs:int" use="required" />
<xs:attribute name="mod" type="xs:int" use="required" />
<xs:attribute name="envDec" type="xs:int" use="required" />
<xs:attribute name="res" type="xs:int" use="required" />
<xs:attribute name="decay" type="xs:int" use="required" />
<xs:attribute name="envAtt" type="xs:int" use="required" />
<xs:attribute name="attack" type="xs:int" use="required" />
<xs:attribute name="sweepDepth" type="xs:int" use="required" />
<xs:attribute name="sustain" type="xs:int" use="required" />
<xs:attribute name="vol" type="xs:int" use="required" />
<xs:attribute name="envHold" type="xs:int" use="required" />
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
<!-- Tipos simples para painéis -->
<xs:complexType name="SimplePanelType">
<xs:attribute name="height" type="xs:integer" />
<xs:attribute name="y" type="xs:integer" />
<xs:attribute name="x" type="xs:integer" />
<xs:attribute name="visible" type="xs:boolean" />
<xs:attribute name="width" type="xs:integer" />
</xs:complexType>
<xs:complexType name="TimelineType">
<xs:attribute name="height" type="xs:integer" />
<xs:attribute name="y" type="xs:integer" />
<xs:attribute name="x" type="xs:integer" />
<xs:attribute name="visible" type="xs:boolean" />
<xs:attribute name="width" type="xs:integer" />
</xs:complexType>
<xs:complexType name="ControllersType">
<xs:attribute name="height" type="xs:integer" />
<xs:attribute name="y" type="xs:integer" />
<xs:attribute name="x" type="xs:integer" />
<xs:attribute name="visible" type="xs:boolean" />
<xs:attribute name="width" type="xs:integer" />
</xs:complexType>
</xs:schema>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More