// 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; })();