starraid/star-raid/js/audio.js

74 lines
4.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
/* ---------------- 音频:程序化合成 ---------------- */
const AU={ctx:null,master:null,mgain:null,simMute:false,
init(){ if(this.ctx) return;
try{ this.ctx=new (window.AudioContext||window.webkitAudioContext)();
this.master=this.ctx.createGain(); this.master.gain.value=0.5; this.master.connect(this.ctx.destination);
this.mgain=this.ctx.createGain(); this.mgain.gain.value=(SAVE.mute||!SAVE.music)?0:0.16; this.mgain.connect(this.master);
}catch(e){}
},
/* 音乐音量统一闸门:静音 或 音乐关 都归零SAVE.mute 管 SFX 由 tone/noise 自查) */
applyMute(){ if(this.mgain) this.mgain.gain.value=(SAVE.mute||!SAVE.music)?0:0.16; },
now(){return this.ctx?this.ctx.currentTime:0;},
env(g,t,a,d,v){g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(v,t+a);g.gain.exponentialRampToValueAtTime(0.001,t+a+d);},
tone(f,type,dur,vol=0.2,slide=0){ if(!this.ctx||SAVE.mute||this.simMute)return;
const t=this.now(),o=this.ctx.createOscillator(),g=this.ctx.createGain();
o.type=type;o.frequency.setValueAtTime(f,t);
if(slide) o.frequency.exponentialRampToValueAtTime(Math.max(20,f+slide),t+dur);
this.env(g,t,0.005,dur,vol); o.connect(g);g.connect(this.master); o.start(t);o.stop(t+dur+0.05); },
noise(dur,vol=0.25,hp=400){ if(!this.ctx||SAVE.mute||this.simMute)return;
const t=this.now(),len=Math.floor(this.ctx.sampleRate*dur),buf=this.ctx.createBuffer(1,len,this.ctx.sampleRate),d=buf.getChannelData(0);
for(let i=0;i<len;i++)d[i]=(Math.random()*2-1)*(1-i/len);
const s=this.ctx.createBufferSource();s.buffer=buf;
const f=this.ctx.createBiquadFilter();f.type="highpass";f.frequency.value=hp;
const g=this.ctx.createGain();this.env(g,t,0.003,dur,vol);
s.connect(f);f.connect(g);g.connect(this.master);s.start(t); }
};
let _shootT=0,_eshootT=0;
const SFX={
shoot(){const n=performance.now(); if(n-_shootT<45)return; _shootT=n; AU.tone(880,"square",0.05,0.05,-300);},
eshoot(){const n=performance.now();if(n-_eshootT<90)return;_eshootT=n;AU.tone(220,"square",0.06,0.03,-80);},
hit(){AU.noise(0.05,0.12,1200);},
boom(big){AU.noise(big?0.5:0.25,big?0.4:0.22,big?80:200); AU.tone(big?90:140,"sawtooth",big?0.4:0.2,0.18,-60);},
pick(){AU.tone(660,"sine",0.08,0.12,300);},
coin(){AU.tone(990,"square",0.05,0.08);AU.tone(1320,"square",0.08,0.08);},
level(){[523,659,784,1046].forEach((f,i)=>setTimeout(()=>AU.tone(f,"square",0.1,0.12),i*70));},
hurt(){AU.tone(160,"sawtooth",0.3,0.3,-100);AU.noise(0.3,0.3,150);},
bomb(){AU.noise(0.8,0.5,60);AU.tone(70,"sawtooth",0.7,0.3,-40);},
od(){[440,660,880,1320].forEach((f,i)=>setTimeout(()=>AU.tone(f,"sawtooth",0.09,0.1),i*50));},
graze(){AU.tone(1800,"sine",0.03,0.04);},
warn(){for(let i=0;i<4;i++)setTimeout(()=>{AU.tone(440,"square",0.15,0.15);setTimeout(()=>AU.tone(349,"square",0.15,0.15),180);},i*400);},
};
/* 简易芯片音序器 */
const MUS={step:0,next:0,bpm:138,playing:false,boss:false,
bass:[45,45,48,45,52,45,48,43], lead:[69,72,76,72,74,72,69,64],
tick(){ if(!AU.ctx||!this.playing||!SAVE.music)return;
const spb=60/(this.bpm*2);
while(this.next<AU.now()+0.12){
const s=this.step%8, t=this.next;
// 贝斯
this.mt(this.N(this.bass[s]+(this.boss?2:0)),"triangle",t,spb*0.9,0.5);
// 琶音
if(s%2===0||this.boss) this.mt(this.N(this.lead[s]+12),"square",t,spb*0.4,0.12);
// 鼓点
if(s%4===0) this.kick(t); this.hat(t+spb/2);
this.next+=spb; this.step++;
}
},
N(n){return 440*Math.pow(2,(n-69)/12);},
mt(f,type,t,d,v){const o=AU.ctx.createOscillator(),g=AU.ctx.createGain();
o.type=type;o.frequency.value=f;g.gain.setValueAtTime(v,t);g.gain.exponentialRampToValueAtTime(0.001,t+d);
o.connect(g);g.connect(AU.mgain);o.start(t);o.stop(t+d+0.02);},
kick(t){const o=AU.ctx.createOscillator(),g=AU.ctx.createGain();
o.frequency.setValueAtTime(150,t);o.frequency.exponentialRampToValueAtTime(40,t+0.1);
g.gain.setValueAtTime(0.6,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.12);
o.connect(g);g.connect(AU.mgain);o.start(t);o.stop(t+0.14);},
hat(t){const len=Math.floor(AU.ctx.sampleRate*0.03),b=AU.ctx.createBuffer(1,len,AU.ctx.sampleRate),d=b.getChannelData(0);
for(let i=0;i<len;i++)d[i]=(Math.random()*2-1)*(1-i/len);
const s=AU.ctx.createBufferSource();s.buffer=b;const g=AU.ctx.createGain();
g.gain.setValueAtTime(0.07,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.03);
s.connect(g);g.connect(AU.mgain);s.start(t);},
start(boss){this.playing=true;this.boss=!!boss;this.bpm=boss?152:138;if(AU.ctx)this.next=AU.now()+0.1;},
stop(){this.playing=false;}
};