fix: synth để gắn soundfont cho midi
This commit is contained in:
@@ -30,21 +30,123 @@
|
||||
return buffer;
|
||||
},
|
||||
|
||||
// Play a MIDI note using Web Audio fallback
|
||||
playNote: function (note, velocity, durationMs) {
|
||||
playNote: function (note, velocity, durationMs, startTime, program, destinationNode) {
|
||||
const ctx = getCtx();
|
||||
const freq = 440 * Math.pow(2, (note - 69) / 12);
|
||||
if (freq <= 0 || isNaN(freq)) return null;
|
||||
|
||||
const osc = ctx.createOscillator();
|
||||
const noteGain = ctx.createGain();
|
||||
osc.type = 'triangle';
|
||||
|
||||
// Default settings
|
||||
let oscType = 'triangle';
|
||||
let attackTime = 0.01;
|
||||
let decayTime = 0.1;
|
||||
let sustainLevel = 0.5;
|
||||
let releaseTime = 0.2;
|
||||
let volFactor = 0.3;
|
||||
|
||||
const prog = program !== undefined ? parseInt(program) : 0;
|
||||
if (prog >= 0 && prog <= 7) { // Pianos
|
||||
oscType = 'sine';
|
||||
decayTime = 0.3;
|
||||
sustainLevel = 0.1;
|
||||
releaseTime = 0.2;
|
||||
} else if (prog >= 8 && prog <= 15) { // Chromatic Perc
|
||||
oscType = 'sine';
|
||||
decayTime = 0.1;
|
||||
sustainLevel = 0.0;
|
||||
releaseTime = 0.1;
|
||||
} else if (prog >= 16 && prog <= 23) { // Organs
|
||||
oscType = 'sine';
|
||||
attackTime = 0.05;
|
||||
sustainLevel = 0.8;
|
||||
releaseTime = 0.1;
|
||||
} else if (prog >= 24 && prog <= 31) { // Guitars
|
||||
oscType = 'triangle';
|
||||
decayTime = 0.4;
|
||||
sustainLevel = 0.2;
|
||||
releaseTime = 0.3;
|
||||
} else if (prog >= 32 && prog <= 39) { // Basses
|
||||
oscType = 'triangle';
|
||||
attackTime = 0.02;
|
||||
decayTime = 0.2;
|
||||
sustainLevel = 0.6;
|
||||
releaseTime = 0.2;
|
||||
} else if (prog >= 40 && prog <= 47) { // Strings
|
||||
oscType = 'sawtooth';
|
||||
attackTime = 0.15;
|
||||
sustainLevel = 0.8;
|
||||
releaseTime = 0.5;
|
||||
volFactor = 0.15;
|
||||
} else if (prog >= 48 && prog <= 55) { // Ensemble / Choir
|
||||
oscType = 'sawtooth';
|
||||
attackTime = 0.2;
|
||||
sustainLevel = 0.8;
|
||||
releaseTime = 0.6;
|
||||
volFactor = 0.12;
|
||||
} else if (prog >= 56 && prog <= 63) { // Brass
|
||||
oscType = 'sawtooth';
|
||||
attackTime = 0.08;
|
||||
sustainLevel = 0.7;
|
||||
releaseTime = 0.3;
|
||||
volFactor = 0.15;
|
||||
} else if (prog >= 64 && prog <= 71) { // Reed
|
||||
oscType = 'square';
|
||||
attackTime = 0.05;
|
||||
sustainLevel = 0.6;
|
||||
releaseTime = 0.2;
|
||||
volFactor = 0.15;
|
||||
} else if (prog >= 72 && prog <= 79) { // Pipe
|
||||
oscType = 'sine';
|
||||
attackTime = 0.1;
|
||||
sustainLevel = 0.7;
|
||||
releaseTime = 0.3;
|
||||
volFactor = 0.2;
|
||||
} else if (prog >= 80 && prog <= 119) { // Synth Lead/Pad/FX
|
||||
oscType = 'sawtooth';
|
||||
attackTime = 0.05;
|
||||
sustainLevel = 0.6;
|
||||
releaseTime = 0.4;
|
||||
volFactor = 0.15;
|
||||
}
|
||||
|
||||
osc.type = oscType;
|
||||
osc.frequency.value = freq;
|
||||
noteGain.gain.setValueAtTime(velocity / 127 * 0.3, ctx.currentTime);
|
||||
noteGain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + durationMs / 1000);
|
||||
|
||||
const startAt = startTime !== undefined ? startTime : ctx.currentTime;
|
||||
const durSec = durationMs / 1000;
|
||||
|
||||
const vel = typeof velocity === 'number' ? (velocity > 1 ? velocity / 127 : velocity) : 0.8;
|
||||
const targetGain = vel * volFactor;
|
||||
|
||||
// ADSR Envelope
|
||||
noteGain.gain.setValueAtTime(0, startAt);
|
||||
noteGain.gain.linearRampToValueAtTime(targetGain, startAt + attackTime);
|
||||
noteGain.gain.linearRampToValueAtTime(targetGain * sustainLevel, startAt + attackTime + decayTime);
|
||||
|
||||
const releaseStart = startAt + Math.max(attackTime + decayTime, durSec);
|
||||
noteGain.gain.setValueAtTime(targetGain * sustainLevel, releaseStart);
|
||||
noteGain.gain.exponentialRampToValueAtTime(0.001, releaseStart + releaseTime);
|
||||
|
||||
osc.connect(noteGain);
|
||||
noteGain.connect(ctx.destination);
|
||||
osc.start(ctx.currentTime);
|
||||
osc.stop(ctx.currentTime + durationMs / 1000 + 0.05);
|
||||
activeOscillators[note] = osc;
|
||||
|
||||
const dest = destinationNode || gainNode || ctx.destination;
|
||||
noteGain.connect(dest);
|
||||
|
||||
osc.start(startAt);
|
||||
|
||||
const stopAt = releaseStart + releaseTime + 0.05;
|
||||
osc.stop(stopAt);
|
||||
|
||||
const oscId = `${note}_${Date.now()}_${Math.random()}`;
|
||||
activeOscillators[oscId] = osc;
|
||||
|
||||
// Clean up active oscillator reference after it stops
|
||||
setTimeout(() => {
|
||||
delete activeOscillators[oscId];
|
||||
}, (stopAt - ctx.currentTime) * 1000 + 100);
|
||||
|
||||
return osc;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user