/* ISLE SLINGERS — audio.js * All sound is synthesized with WebAudio oscillators/noise buffers. * No external audio assets, fully original. */ (function (root, factory) { var api = factory(); if (typeof module !== 'undefined' && module.exports) module.exports = api; else root.ISLE_AUDIO = api; })(typeof globalThis !== 'undefined' ? globalThis : this, function () { 'use strict'; function AudioEngine() { this.ctx = null; this.master = null; this.muted = false; } AudioEngine.prototype.init = function () { if (this.ctx || typeof window === 'undefined') return; var AC = window.AudioContext || window.webkitAudioContext; if (!AC) return; this.ctx = new AC(); this.master = this.ctx.createGain(); this.master.gain.value = 0.5; this.master.connect(this.ctx.destination); }; AudioEngine.prototype.resume = function () { if (this.ctx && this.ctx.state === 'suspended') this.ctx.resume(); }; AudioEngine.prototype.setMuted = function (m) { this.muted = m; if (this.master) this.master.gain.value = m ? 0 : 0.5; }; AudioEngine.prototype.noise = function (dur, filterFreq, gainVal) { if (!this.ctx || this.muted) return; var sr = this.ctx.sampleRate; var buf = this.ctx.createBuffer(1, Math.max(1, (sr * dur) | 0), sr); var data = buf.getChannelData(0); for (var i = 0; i < data.length; i++) data[i] = Math.random() * 2 - 1; var src = this.ctx.createBufferSource(); src.buffer = buf; var filt = this.ctx.createBiquadFilter(); filt.type = 'lowpass'; filt.frequency.value = filterFreq; var g = this.ctx.createGain(); var t0 = this.ctx.currentTime; g.gain.setValueAtTime(gainVal, t0); g.gain.exponentialRampToValueAtTime(0.001, t0 + dur); src.connect(filt); filt.connect(g); g.connect(this.master); src.start(t0); }; AudioEngine.prototype.tone = function (freq, dur, type, gainVal, slideTo) { if (!this.ctx || this.muted) return; var t0 = this.ctx.currentTime; var o = this.ctx.createOscillator(); o.type = type || 'sine'; o.frequency.setValueAtTime(freq, t0); if (slideTo) o.frequency.exponentialRampToValueAtTime(Math.max(30, slideTo), t0 + dur); var g = this.ctx.createGain(); g.gain.setValueAtTime(gainVal || 0.2, t0); g.gain.exponentialRampToValueAtTime(0.001, t0 + dur); o.connect(g); g.connect(this.master); o.start(t0); o.stop(t0 + dur + 0.02); }; AudioEngine.prototype.play = function (ev) { if (!this.ctx || this.muted) return; var s = ev.strength || 0; switch (ev.type) { case 'launch': this.noise(0.25, 900, 0.25); this.tone(300, 0.22, 'triangle', 0.15, 90); break; case 'hit': case 'block_impact': this.noise(0.16, ev.mat === 'ice' ? 3200 : 1200, 0.3); this.tone(ev.mat === 'stone' ? 70 : 110, 0.12, 'sine', 0.3, 45); break; case 'break': if (ev.mat === 'wood') { this.noise(0.3, 1600, 0.4); this.tone(140, 0.18, 'triangle', 0.3, 60); } else if (ev.mat === 'ice') { this.noise(0.35, 5000, 0.45); this.tone(2200, 0.15, 'sine', 0.15, 3200); } else { this.noise(0.4, 500, 0.5); this.tone(60, 0.3, 'sine', 0.45, 38); } break; case 'crack': this.noise(0.1, 2000, 0.2); break; case 'pop': this.tone(520, 0.16, 'sine', 0.35, 160); this.noise(0.12, 2500, 0.2); break; case 'crush': this.noise(0.2, 700, 0.3); break; case 'bird_ground': this.noise(0.2, 800, 0.25); break; case 'ability_fury': this.noise(0.3, 2600, 0.35); this.tone(700, 0.25, 'sawtooth', 0.12, 1600); break; case 'ability_dive': this.tone(900, 0.4, 'sawtooth', 0.14, 220); break; case 'ability_quake': this.tone(60, 0.55, 'sine', 0.6, 30); this.noise(0.5, 400, 0.5); break; case 'bird_gone': this.tone(360, 0.2, 'sine', 0.12, 200); break; case 'level_won': { var self = this; [523, 659, 784, 1047].forEach(function (f, i) { setTimeout(function () { self.tone(f, 0.3, 'triangle', 0.3); }, i * 140); }); break; } case 'level_lost': this.tone(300, 0.5, 'sine', 0.3, 120); this.tone(200, 0.7, 'sine', 0.25, 80); break; case 'star': this.tone(880, 0.25, 'triangle', 0.3, 1320); break; case 'ui': this.tone(600, 0.06, 'sine', 0.12); break; } }; return { AudioEngine: AudioEngine }; });