teste
This commit is contained in:
1 parent
42da68afa0
commit
12c5b55bce
29 files changed
+162410
-343433
No files matched your search
@@ -0,0 +1,33 @@
|
||||
import { TripleOscillator } from "./plugins/TripleOscillator.js";
|
||||
import { PLUGINS } from "utils.js";
|
||||
|
||||
export class InstrumentFactory {
|
||||
constructor() {
|
||||
// Cria um contexto de áudio único para a página
|
||||
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
}
|
||||
|
||||
play(instrumentData) {
|
||||
// Pega o nome do plugin (que vem do python em minúsculo)
|
||||
// O parser as vezes bota o nome na raiz ou dentro de tags.
|
||||
// Vamos assumir que o "instrument_name" é a chave principal.
|
||||
|
||||
// Normalização de nome (ex: "TripleOscillator" -> "tripleoscillator")
|
||||
const pluginName = (
|
||||
instrumentData.instrument_name ||
|
||||
instrumentData.name ||
|
||||
""
|
||||
).toLowerCase();
|
||||
|
||||
if (PLUGINS[pluginName]) {
|
||||
const InstrumentClass = PLUGINS[pluginName];
|
||||
const instr = new InstrumentClass(this.audioCtx, instrumentData);
|
||||
|
||||
// Toca uma nota de teste (Dó central - MIDI 60)
|
||||
instr.playNote(60, this.audioCtx.currentTime, 0.5);
|
||||
} else {
|
||||
console.warn(`Plugin não implementado no JS: ${pluginName}`);
|
||||
alert(`O plugin ${pluginName} ainda não tem implementação Web Audio.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
export class TripleOscillator {
|
||||
constructor(audioCtx, data) {
|
||||
this.ctx = audioCtx;
|
||||
// Os dados do plugin geralmente vêm dentro de uma chave com o nome dele (ex: data.tripleoscillator)
|
||||
// Mas às vezes vêm "flat" dependendo do parser. Vamos garantir.
|
||||
this.params = data.tripleoscillator || data;
|
||||
|
||||
// Dados do envelope (ADSR) ficam em 'elvol' (Envelope Volume)
|
||||
this.env = data.elvol || {};
|
||||
}
|
||||
|
||||
getWaveType(lmmsTypeIndex) {
|
||||
// Mapeamento: 0=Sine, 1=Triangle, 2=Sawtooth, 3=Square, 4=Noise, 5=Exp
|
||||
const types = [
|
||||
"sine",
|
||||
"triangle",
|
||||
"sawtooth",
|
||||
"square",
|
||||
"sawtooth",
|
||||
"sawtooth",
|
||||
];
|
||||
const idx = parseInt(lmmsTypeIndex);
|
||||
return types[isNaN(idx) ? 0 : idx]; // Default para sine
|
||||
}
|
||||
|
||||
playNote(midiNote, startTime, duration) {
|
||||
// Fórmula de conversão MIDI -> Frequência
|
||||
const freq = 440 * Math.pow(2, (midiNote - 69) / 12);
|
||||
|
||||
// Ganho Mestre desta nota (evita estouro de áudio)
|
||||
const masterGain = this.ctx.createGain();
|
||||
masterGain.connect(this.ctx.destination);
|
||||
|
||||
// Aplica Envelope ADSR no volume mestre
|
||||
this.applyEnvelope(masterGain, startTime, duration);
|
||||
|
||||
// O TripleOscillator tem 3 osciladores (Osc1, Osc2, Osc3)
|
||||
// No XML/JSON eles são sufixados com 0, 1 e 2 (ex: vol0, vol1...)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
// Volume: O LMMS usa 0-100. O Web Audio usa 0.0-1.0.
|
||||
const volRaw = this.params[`vol${i}`];
|
||||
const vol = parseInt(volRaw !== undefined ? volRaw : i === 0 ? 100 : 0);
|
||||
|
||||
// Se volume for 0, não gasta processamento criando oscilador
|
||||
if (vol > 0) {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const oscGain = this.ctx.createGain();
|
||||
|
||||
// Configura Onda
|
||||
osc.type = this.getWaveType(this.params[`wavetype${i}`]);
|
||||
|
||||
// Configura Afinação (Coarse = Semitons, Fine = Cents)
|
||||
const coarse = parseInt(this.params[`coarse${i}`] || 0);
|
||||
const fine = parseInt(
|
||||
this.params[`fine${i}`] || this.params[`finer${i}`] || 0
|
||||
);
|
||||
const detuneTotal = coarse * 100 + fine;
|
||||
|
||||
osc.frequency.value = freq;
|
||||
osc.detune.value = detuneTotal;
|
||||
|
||||
// Configura Volume do Oscilador
|
||||
// Dividimos por 300 (3 osciladores x 100) para normalizar e não distorcer
|
||||
oscGain.gain.value = (vol / 100) * 0.3;
|
||||
|
||||
// Conexões: Osc -> OscGain -> MasterGain
|
||||
osc.connect(oscGain);
|
||||
oscGain.connect(masterGain);
|
||||
|
||||
// Toca
|
||||
osc.start(startTime);
|
||||
osc.stop(startTime + duration + 2.0); // +2s de margem para o release do envelope
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyEnvelope(gainNode, time, duration) {
|
||||
// Valores padrão do LMMS se não existirem no JSON
|
||||
// O LMMS usa escala 0-100 para tempo em alguns contextos, mas o parser XML geralmente traz valores brutos.
|
||||
// Vamos assumir valores pequenos como segundos ou normalizar.
|
||||
|
||||
let att = parseFloat(this.env.att || 0);
|
||||
let dec = parseFloat(this.env.dec || 0.5);
|
||||
let sus = parseFloat(this.env.sustain || 0.5);
|
||||
let rel = parseFloat(this.env.rel || 0.1);
|
||||
|
||||
// Ajuste empírico: Se os valores forem muito grandes (> 5), provavelmente estão em escala 0-100 ou similar
|
||||
if (att > 2) att /= 100;
|
||||
if (dec > 5) dec /= 100;
|
||||
// Sustain é sempre nível (0-1), mas as vezes vem como 100
|
||||
if (sus > 1) sus /= 100;
|
||||
if (rel > 5) rel /= 100;
|
||||
|
||||
const now = time;
|
||||
|
||||
// Garante que começa zerado
|
||||
gainNode.gain.cancelScheduledValues(now);
|
||||
gainNode.gain.setValueAtTime(0, now);
|
||||
|
||||
// Attack: Sobe até o volume máximo (1.0 relativo ao masterGain)
|
||||
gainNode.gain.linearRampToValueAtTime(1.0, now + att + 0.005);
|
||||
|
||||
// Decay: Desce até o nível de Sustain
|
||||
gainNode.gain.exponentialRampToValueAtTime(
|
||||
Math.max(sus, 0.001),
|
||||
now + att + dec
|
||||
);
|
||||
|
||||
// Sustain: Mantém o nível até o fim da nota
|
||||
gainNode.gain.setValueAtTime(Math.max(sus, 0.001), now + duration);
|
||||
|
||||
// Release: Desce a zero após soltar a nota
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.001, now + duration + rel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Mapeamento de Strings do LMMS para Classes JS
|
||||
const PLUGINS = {
|
||||
tripleoscillator: TripleOscillator,
|
||||
// 'kicker': Kicker, // Implementaremos em breve
|
||||
// 'organic': Organic
|
||||
};
|
||||
|
||||
SUPPORTED_PLUGINS = [
|
||||
"audiofileprocessor",
|
||||
"bitinvader",
|
||||
"freeboy",
|
||||
"papu",
|
||||
"gigplayer",
|
||||
"kicker",
|
||||
"lb302",
|
||||
"malletsstk",
|
||||
"monstro",
|
||||
"nes",
|
||||
"opl2",
|
||||
"organic",
|
||||
"patman",
|
||||
"sf2player",
|
||||
"sfxr",
|
||||
"sid",
|
||||
"tripleoscillator",
|
||||
"vibedstrings",
|
||||
"watsyn",
|
||||
"zynaddsubfx",
|
||||
]
|
||||
Reference in new issue
Block a user