11 lines
1.8 KiB
JavaScript
11 lines
1.8 KiB
JavaScript
// Small offline Web Audio cues: no network or audio-file dependency.
|
|
export class CasualAudio {
|
|
constructor(host,storage,key){this.host=host;this.storage=storage;this.key=key;this.context=null;this.lastEvent=null;this.lastAt={};try{this.enabled=storage.getItem(key)!=='off';}catch{this.enabled=true;}}
|
|
unlock(){if(!this.enabled)return false;try{const Audio=this.host.AudioContext||this.host.webkitAudioContext;if(!Audio)return false;if(!this.context)this.context=new Audio();if(this.context.state==='suspended')this.context.resume()?.catch(()=>{});return true;}catch{return false;}}
|
|
toggle(){this.enabled=!this.enabled;try{this.storage.setItem(this.key,this.enabled?'on':'off');}catch{}if(this.enabled)this.unlock();return this.enabled;}
|
|
play(kind,delay=0,step=1){if(!this.unlock())return;const ctx=this.context,now=ctx.currentTime;const gap=kind==='error'?.18:0;if(this.lastAt[kind]!==undefined&&now-this.lastAt[kind]<gap)return;this.lastAt[kind]=now;this.lastEvent=kind;
|
|
const cues={move:[[523*Math.pow(2,(Math.max(1,step)-1)/12),.07,0]],correct:[[659,.09,0],[880,.14,.075]],error:[[330,.10,0],[247,.15,.09]],undo:[[587,.07,0],[440,.10,.055]],restart:[[523,.075,0],[392,.09,.065],[523,.11,.14]],complete:[[523,.12,0],[659,.12,.10],[784,.14,.20],[1046,.25,.32]]};
|
|
for(const [frequency,duration,offset]of cues[kind]||[]){try{const start=now+delay+offset,osc=ctx.createOscillator(),gain=ctx.createGain();osc.type='sine';osc.frequency.setValueAtTime(frequency,start);gain.gain.setValueAtTime(.0001,start);gain.gain.linearRampToValueAtTime(kind==='error'?.055:.075,start+.008);gain.gain.exponentialRampToValueAtTime(.0001,start+duration);osc.connect(gain);gain.connect(ctx.destination);osc.onended=()=>{osc.disconnect();gain.disconnect();};osc.start(start);osc.stop(start+duration+.015);}catch{}}
|
|
}
|
|
}
|