76 lines
2.7 KiB
JavaScript
76 lines
2.7 KiB
JavaScript
// SonicForge Studio SoundFont Player Service
|
|
(function () {
|
|
'use strict';
|
|
|
|
// Web Audio API fallback synth
|
|
let audioCtx = null;
|
|
let gainNode = null;
|
|
const activeOscillators = {};
|
|
|
|
function getCtx() {
|
|
if (!audioCtx) {
|
|
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
gainNode = audioCtx.createGain();
|
|
gainNode.gain.value = 0.3;
|
|
gainNode.connect(audioCtx.destination);
|
|
}
|
|
return audioCtx;
|
|
}
|
|
|
|
const SonicSF = {
|
|
loadedFonts: {},
|
|
|
|
// Load SoundFont from URL → ArrayBuffer → store in memory
|
|
loadSoundFont: async function (url) {
|
|
if (this.loadedFonts[url]) return this.loadedFonts[url];
|
|
const resp = await fetch(url);
|
|
if (!resp.ok) throw new Error('Failed to load SoundFont: ' + url);
|
|
const buffer = await resp.arrayBuffer();
|
|
this.loadedFonts[url] = buffer;
|
|
return buffer;
|
|
},
|
|
|
|
// Play a MIDI note using Web Audio fallback
|
|
playNote: function (note, velocity, durationMs) {
|
|
const ctx = getCtx();
|
|
const freq = 440 * Math.pow(2, (note - 69) / 12);
|
|
const osc = ctx.createOscillator();
|
|
const noteGain = ctx.createGain();
|
|
osc.type = 'triangle';
|
|
osc.frequency.value = freq;
|
|
noteGain.gain.setValueAtTime(velocity / 127 * 0.3, ctx.currentTime);
|
|
noteGain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + durationMs / 1000);
|
|
osc.connect(noteGain);
|
|
noteGain.connect(ctx.destination);
|
|
osc.start(ctx.currentTime);
|
|
osc.stop(ctx.currentTime + durationMs / 1000 + 0.05);
|
|
activeOscillators[note] = osc;
|
|
return osc;
|
|
},
|
|
|
|
stopAll: function () {
|
|
Object.values(activeOscillators).forEach(osc => {
|
|
try { osc.stop(); } catch (e) { }
|
|
});
|
|
Object.keys(activeOscillators).forEach(k => delete activeOscillators[k]);
|
|
},
|
|
|
|
// Save user SoundFont to IndexedDB via window.SonicStorage
|
|
saveToIndexedDB: async function (name, arrayBuffer) {
|
|
if (window.SonicStorage && window.SonicStorage.saveToIndexedDB) {
|
|
await window.SonicStorage.saveToIndexedDB('soundfont_' + name, arrayBuffer);
|
|
}
|
|
},
|
|
|
|
// Load user SoundFont from IndexedDB
|
|
loadFromIndexedDB: async function (name) {
|
|
if (window.SonicStorage && window.SonicStorage.loadFromIndexedDB) {
|
|
return await window.SonicStorage.loadFromIndexedDB('soundfont_' + name);
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
window.SonicSF = SonicSF;
|
|
})();
|