82 lines
2.7 KiB
JavaScript
82 lines
2.7 KiB
JavaScript
// js/plugins/TripleOscillator.js
|
|
import * as Tone from "https://esm.sh/tone";
|
|
|
|
export class TripleOscillator {
|
|
constructor(toneContext, data = {}) {
|
|
// Não precisamos mais do contexto cru, usamos o Tone direto
|
|
this.params = data.tripleoscillator || data || {};
|
|
|
|
// Volume Mestre do Instrumento
|
|
this.output = new Tone.Gain(1); // Começa com volume 1
|
|
|
|
// Envelope de Amplitude (ADSR)
|
|
// Convertendo valores do LMMS (geralmente 0-1 ou 0-100) para segundos/níveis
|
|
const envData = data.elvol || {};
|
|
this.envelope = new Tone.AmplitudeEnvelope({
|
|
attack: this.normalizeParam(envData.att, 0, 0.01),
|
|
decay: this.normalizeParam(envData.dec, 0.5, 0.1),
|
|
sustain: this.normalizeParam(envData.sustain, 1, 0.5),
|
|
release: this.normalizeParam(envData.rel, 0, 0.1)
|
|
});
|
|
|
|
// Conecta Envelope -> Saída
|
|
this.envelope.connect(this.output);
|
|
}
|
|
|
|
normalizeParam(val, defaultVal, scale = 1) {
|
|
if (val === undefined || val === null) return defaultVal;
|
|
let v = parseFloat(val);
|
|
// Se vier > 2, assume escala 0-100 e divide
|
|
if (v > 2) v /= 100;
|
|
return v || defaultVal; // Fallback se for 0 ou NaN
|
|
}
|
|
|
|
getWaveType(lmmsTypeIndex) {
|
|
const types = ["sine", "triangle", "sawtooth", "square", "sawtooth", "sawtooth"]; // Mapeamento básico
|
|
const idx = parseInt(lmmsTypeIndex);
|
|
return types[isNaN(idx) ? 0 : idx];
|
|
}
|
|
|
|
// Método chamado pela UI (Step Sequencer)
|
|
triggerAttackRelease(note, duration, time) {
|
|
const freq = Tone.Frequency(note).toFrequency();
|
|
const dur = Tone.Time(duration).toSeconds();
|
|
const now = time || Tone.now();
|
|
|
|
// Cria os 3 osciladores efêmeros (só para esta nota)
|
|
// Isso é necessário para polifonia (cada nota tem seus osciladores)
|
|
for (let i = 0; i < 3; i++) {
|
|
const volRaw = this.params[`vol${i}`];
|
|
// Default: Osc 1 (índice 0) = 100%, outros = 0%
|
|
const vol = (volRaw !== undefined) ? parseInt(volRaw) : (i === 0 ? 100 : 0);
|
|
|
|
if (vol > 0) {
|
|
const osc = new Tone.Oscillator({
|
|
type: this.getWaveType(this.params[`wavetype${i}`]),
|
|
frequency: freq,
|
|
detune: (parseInt(this.params[`coarse${i}`] || 0) * 100) + parseInt(this.params[`fine${i}`] || 0),
|
|
volume: Tone.gainToDb((vol / 100) * 0.3), // 0.3 para evitar clipar a soma
|
|
onstop: () => { osc.dispose(); } // Limpa memória ao acabar
|
|
});
|
|
|
|
// Conecta Osc -> Envelope (Mestre)
|
|
osc.connect(this.envelope);
|
|
|
|
osc.start(now);
|
|
osc.stop(now + dur + this.envelope.release);
|
|
}
|
|
}
|
|
|
|
// Dispara o envelope
|
|
this.envelope.triggerAttackRelease(dur, now);
|
|
}
|
|
|
|
connect(dest) {
|
|
this.output.connect(dest);
|
|
}
|
|
|
|
dispose() {
|
|
this.output.dispose();
|
|
this.envelope.dispose();
|
|
}
|
|
} |