fix: synth để gắn soundfont cho midi

This commit is contained in:
2026-07-23 19:26:26 +07:00
parent 055f351a17
commit a21266736c
7 changed files with 366 additions and 35 deletions
+111 -9
View File
@@ -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;
},