import { JamesDSPNode } from './jamesdsp-node.js'; const presets = { Flat: {}, 'Bass boost': { 'bass.enabled': 1, 'bass.boost': 6, 'eq.enabled': 1, 'eq.band0': 3, 'eq.band1': 2, 'gain': -3 }, Vocal: { 'eq.enabled': 1, 'eq.band3': -2, 'eq.band5': 2, 'eq.band7': 3, 'eq.band9': 2, 'eq.band11': -1 }, 'Headphone crossfeed': { 'crossfeed.enabled': 1, 'crossfeed.mode': 1 }, Loudness: { 'limiter.threshold': -1, 'gain': 2 }, }; export class JamesDSPController { constructor() { this.context = null; this.node = null; this.source = null; this.element = null; this.sources = new WeakMap(); } async enable(element, settings) { const AC = window.AudioContext || window.webkitAudioContext; if (!AC) throw new Error('Web Audio is not supported in this browser'); if (!this.context) this.context = new AC(); if (![44100, 48000].includes(this.context.sampleRate)) throw new Error(`JamesDSP supports 44.1 or 48 kHz; this device uses ${this.context.sampleRate} Hz`); if (!this.node) { this.node = await JamesDSPNode.create(this.context); this.node.addEventListener('error', (event) => this.fault(event.detail)); } if (this.element !== element) { if (this.source) { this.source.disconnect(this.node); this.source.connect(this.context.destination); } this.source = this.sources.get(element) || this.context.createMediaElementSource(element); this.sources.set(element, this.source); this.node.connect(this.source); this.element = element; } await this.node.setBypass(false); await this.apply(settings); if (this.context.state === 'suspended') await this.context.resume(); } async apply(settings) { if (!this.node) return; const preset = presets[settings.preset] || presets.Flat; const params = { ...preset, ...(settings.params || {}) }; await this.node.setBypass(true); await this.node.applyPreset(params); await this.node.setBypass(false); } async update(name, value, settings) { settings.params ||= {}; settings.params[name] = value; await this.apply(settings); } async disable() { if (this.node) await this.node.setBypass(true).catch(() => {}); } fault(error) { console.warn('[JamesDSP] Processing failed; routing media dry:', error); try { this.source?.disconnect(this.node); this.source?.connect(this.context.destination); } catch {} this.node?.destroy(); this.node = null; this.source = null; this.element = null; this.onError?.(error || new Error('Audio processor failed')); } } export const JAMESDSP_PRESETS = Object.keys(presets);