138 lines
4.5 KiB
JavaScript
138 lines
4.5 KiB
JavaScript
|
|
/* 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 };
|
||
|
|
});
|