feat(integrations): add retro doom web engine and agy proxy nginx config

This commit is contained in:
Antigravity committed 2026-09-22 09:25:23 +00:00
1 parent b8db5a123d
commit 50eadb4b5e
15 files changed
+6397

No files matched your search

+728
View File
@@ -0,0 +1,728 @@
/**
* DOOM 3D - Retro Sound Synthesizer & Music Sequencer (Web Audio API)
* Generates authentic 90s DOOM-inspired sound effects and the iconic E1M1 metal riff.
*/
class SoundSystem {
constructor() {
this.ctx = null;
this.masterGain = null;
this.sfxGain = null;
this.musicGain = null;
this.initialized = false;
this.sfxVolume = 0.8;
this.musicVolume = 0.5;
this.masterVolume = 1.0;
this.musicPlaying = false;
this.musicStep = 0;
this.musicTimer = null;
this.musicBpm = 135;
}
init() {
if (this.initialized) return;
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
this.ctx = new AudioContext();
this.masterGain = this.ctx.createGain();
this.masterGain.gain.value = this.masterVolume;
this.masterGain.connect(this.ctx.destination);
this.sfxGain = this.ctx.createGain();
this.sfxGain.gain.value = this.sfxVolume;
this.sfxGain.connect(this.masterGain);
this.musicGain = this.ctx.createGain();
this.musicGain.gain.value = this.musicVolume;
this.musicGain.connect(this.masterGain);
this.initialized = true;
this.startMusic();
} catch (e) {
console.warn('Web Audio API not supported or blocked:', e);
}
}
resume() {
if (this.ctx && this.ctx.state === 'suspended') {
this.ctx.resume();
}
if (!this.initialized) {
this.init();
}
}
setMasterVolume(val) {
this.masterVolume = Math.max(0, Math.min(1, val));
if (this.masterGain) this.masterGain.gain.setValueAtTime(this.masterVolume, this.ctx.currentTime);
}
setSfxVolume(val) {
this.sfxVolume = Math.max(0, Math.min(1, val));
if (this.sfxGain) this.sfxGain.gain.setValueAtTime(this.sfxVolume, this.ctx.currentTime);
}
setMusicVolume(val) {
this.musicVolume = Math.max(0, Math.min(1, val));
if (this.musicGain) this.musicGain.gain.setValueAtTime(this.musicVolume, this.ctx.currentTime);
}
// Helper: Create White Noise buffer
createNoiseBuffer(duration = 1) {
if (!this.ctx) return null;
const bufferSize = this.ctx.sampleRate * duration;
const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
return buffer;
}
// Distortion curve for heavy metal tone
makeDistortionCurve(amount = 50) {
const k = typeof amount === 'number' ? amount : 50;
const n_samples = 44100;
const curve = new Float32Array(n_samples);
const deg = Math.PI / 180;
for (let i = 0; i < n_samples; ++i) {
const x = (i * 2) / n_samples - 1;
curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
}
return curve;
}
// ----------------------------------------------------
// WEAPON SOUND EFFECTS
// ----------------------------------------------------
playFist() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Swoosh
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(120, t);
osc.frequency.exponentialRampToValueAtTime(40, t + 0.15);
gain.gain.setValueAtTime(0.6, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.15);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.16);
// Impact punch
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.12);
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(800, t);
filter.frequency.exponentialRampToValueAtTime(100, t + 0.12);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(0.8, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.12);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playPistol() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Punch osc
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(320, t);
osc.frequency.exponentialRampToValueAtTime(45, t + 0.18);
oGain.gain.setValueAtTime(0.7, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.18);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.19);
// Noise crack
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.2);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(1800, t);
filter.frequency.exponentialRampToValueAtTime(300, t + 0.2);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(0.9, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.2);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playShotgun() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Low boom
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(200, t);
osc.frequency.exponentialRampToValueAtTime(30, t + 0.35);
oGain.gain.setValueAtTime(1.0, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.35);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.36);
// Heavy blast noise
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.35);
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(2800, t);
filter.frequency.exponentialRampToValueAtTime(400, t + 0.35);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(1.2, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.35);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
// Pump action slide (clack-clack) after 0.35s
setTimeout(() => {
if (!this.ctx) return;
const t2 = this.ctx.currentTime;
const p1 = this.ctx.createOscillator();
const pGain = this.ctx.createGain();
p1.type = 'square';
p1.frequency.setValueAtTime(400, t2);
p1.frequency.setValueAtTime(650, t2 + 0.06);
pGain.gain.setValueAtTime(0.3, t2);
pGain.gain.exponentialRampToValueAtTime(0.01, t2 + 0.15);
p1.connect(pGain);
pGain.connect(this.sfxGain);
p1.start(t2);
p1.stop(t2 + 0.16);
}, 320);
}
playChaingun() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(260 + Math.random() * 40, t);
osc.frequency.exponentialRampToValueAtTime(50, t + 0.12);
oGain.gain.setValueAtTime(0.65, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.12);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.13);
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.12);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(2200, t);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(0.7, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.12);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playRocketLaunch() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Swoosh tone
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(100, t);
osc.frequency.exponentialRampToValueAtTime(450, t + 0.25);
osc.frequency.exponentialRampToValueAtTime(120, t + 0.5);
oGain.gain.setValueAtTime(0.7, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.5);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.51);
// Rocket booster hiss
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.4);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(800, t);
filter.frequency.linearRampToValueAtTime(1600, t + 0.4);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(0.8, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.4);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playExplosion(volumeMultiplier = 1.0) {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Sub bass boom
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(160, t);
osc.frequency.exponentialRampToValueAtTime(25, t + 0.7);
oGain.gain.setValueAtTime(1.2 * volumeMultiplier, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.7);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.71);
// Crumbling explosion noise
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.8);
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(1400, t);
filter.frequency.exponentialRampToValueAtTime(120, t + 0.8);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(1.4 * volumeMultiplier, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.8);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playPlasma() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(950, t);
osc.frequency.exponentialRampToValueAtTime(180, t + 0.16);
oGain.gain.setValueAtTime(0.7, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.16);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.17);
const osc2 = this.ctx.createOscillator();
const oGain2 = this.ctx.createGain();
osc2.type = 'sine';
osc2.frequency.setValueAtTime(1400, t);
osc2.frequency.exponentialRampToValueAtTime(300, t + 0.14);
oGain2.gain.setValueAtTime(0.5, t);
oGain2.gain.exponentialRampToValueAtTime(0.01, t + 0.14);
osc2.connect(oGain2);
oGain2.connect(this.sfxGain);
osc2.start(t);
osc2.stop(t + 0.15);
}
playBFGCharge() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(100, t);
osc.frequency.exponentialRampToValueAtTime(1200, t + 0.8);
gain.gain.setValueAtTime(0.1, t);
gain.gain.linearRampToValueAtTime(0.9, t + 0.8);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.82);
}
playBFGFire() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
// Big apocalyptic energy discharge
const osc = this.ctx.createOscillator();
const oGain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(800, t);
osc.frequency.exponentialRampToValueAtTime(60, t + 0.9);
oGain.gain.setValueAtTime(1.3, t);
oGain.gain.exponentialRampToValueAtTime(0.01, t + 0.9);
osc.connect(oGain);
oGain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.92);
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(1.0);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(3000, t);
filter.frequency.exponentialRampToValueAtTime(200, t + 1.0);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(1.5, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 1.0);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
// ----------------------------------------------------
// MONSTER & WORLD SOUND EFFECTS
// ----------------------------------------------------
playEnemyAlert(type = 'zombie') {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
if (type === 'pinky' || type === 'caco') {
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(140, t);
osc.frequency.linearRampToValueAtTime(220, t + 0.15);
osc.frequency.exponentialRampToValueAtTime(60, t + 0.4);
gain.gain.setValueAtTime(0.7, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.4);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.42);
} else {
// Zombie / Imp grunt
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(220, t);
osc.frequency.exponentialRampToValueAtTime(90, t + 0.28);
gain.gain.setValueAtTime(0.6, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.28);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.3);
}
}
playEnemyPain() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(180, t);
osc.frequency.exponentialRampToValueAtTime(70, t + 0.18);
gain.gain.setValueAtTime(0.6, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.18);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.19);
}
playEnemyDeath() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(130, t);
osc.frequency.exponentialRampToValueAtTime(30, t + 0.45);
gain.gain.setValueAtTime(0.8, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.45);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.46);
// Gurgle
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.35);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(600, t);
const nGain = this.ctx.createGain();
nGain.gain.setValueAtTime(0.7, t);
nGain.gain.exponentialRampToValueAtTime(0.01, t + 0.35);
noise.connect(filter);
filter.connect(nGain);
nGain.connect(this.sfxGain);
noise.start(t);
}
playFireball() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.3);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(1200, t);
filter.frequency.exponentialRampToValueAtTime(300, t + 0.3);
const gain = this.ctx.createGain();
gain.gain.setValueAtTime(0.8, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.3);
noise.connect(filter);
filter.connect(gain);
gain.connect(this.sfxGain);
noise.start(t);
}
playItemPickup(type = 'ammo') {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sine';
if (type === 'health' || type === 'armor') {
osc.frequency.setValueAtTime(440, t);
osc.frequency.setValueAtTime(554.37, t + 0.06);
osc.frequency.setValueAtTime(659.25, t + 0.12);
gain.gain.setValueAtTime(0.5, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.25);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.26);
} else if (type === 'weapon' || type === 'key') {
// Fanfare chime
osc.frequency.setValueAtTime(330, t);
osc.frequency.setValueAtTime(440, t + 0.08);
osc.frequency.setValueAtTime(660, t + 0.16);
osc.frequency.setValueAtTime(880, t + 0.24);
gain.gain.setValueAtTime(0.7, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.45);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.46);
} else {
// Ammo / click
osc.frequency.setValueAtTime(600, t);
osc.frequency.setValueAtTime(900, t + 0.05);
gain.gain.setValueAtTime(0.4, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.12);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.13);
}
}
playDoor(open = true) {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
const startFreq = open ? 90 : 150;
const endFreq = open ? 160 : 70;
osc.frequency.setValueAtTime(startFreq, t);
osc.frequency.linearRampToValueAtTime(endFreq, t + 0.6);
gain.gain.setValueAtTime(0.4, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.6);
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(450, t);
osc.connect(filter);
filter.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.62);
}
playPlayerPain() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(200, t);
osc.frequency.exponentialRampToValueAtTime(80, t + 0.22);
gain.gain.setValueAtTime(0.9, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.22);
osc.connect(gain);
gain.connect(this.sfxGain);
osc.start(t);
osc.stop(t + 0.23);
}
playSlimeHurt() {
if (!this.ctx) return;
const t = this.ctx.currentTime;
const noise = this.ctx.createBufferSource();
noise.buffer = this.createNoiseBuffer(0.2);
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(3200, t);
filter.Q.setValueAtTime(3, t);
const gain = this.ctx.createGain();
gain.gain.setValueAtTime(0.5, t);
gain.gain.exponentialRampToValueAtTime(0.01, t + 0.2);
noise.connect(filter);
filter.connect(gain);
gain.connect(this.sfxGain);
noise.start(t);
}
// ----------------------------------------------------
// E1M1 RETRO SYNTHESIZER SOUNDTRACK
// ----------------------------------------------------
startMusic() {
if (this.musicPlaying) return;
this.musicPlaying = true;
this.musicStep = 0;
// E1M1 Notes in Hz (E Minor riff)
const E1 = 41.20;
const E2 = 82.41;
const G2 = 98.00;
const A2 = 110.00;
const Bb2 = 116.54;
const B2 = 123.47;
const D3 = 146.83;
const E3 = 164.81;
// 32-step bass riff pattern for classic E1M1
// E E E3 E E D3 E E | C3 E E Bb2 E E B2 C3
const riffNotes = [
E2, E2, E3, E2, E2, D3, E2, E2,
E3, E2, E2, D3, E2, E2, Bb2, B2,
E2, E2, E3, E2, E2, D3, E2, E2,
E3, E2, E2, G2, E2, A2, Bb2, B2
];
const stepDuration = (60 / this.musicBpm) / 4; // 16th notes
const scheduleStep = () => {
if (!this.musicPlaying || !this.ctx) return;
const now = this.ctx.currentTime;
const note = riffNotes[this.musicStep % riffNotes.length];
const isHeavyBeat = (this.musicStep % 4 === 0);
const isSnareBeat = (this.musicStep % 8 === 4);
// Synth Distortion Bass/Guitar
const osc = this.ctx.createOscillator();
const dist = this.ctx.createWaveShaper();
const filter = this.ctx.createBiquadFilter();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(note, now);
dist.curve = this.makeDistortionCurve(30);
dist.oversample = '4x';
filter.type = 'lowpass';
filter.frequency.setValueAtTime(1400, now);
filter.frequency.exponentialRampToValueAtTime(350, now + stepDuration * 0.9);
gain.gain.setValueAtTime(0.35, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + stepDuration * 0.95);
osc.connect(dist);
dist.connect(filter);
filter.connect(gain);
gain.connect(this.musicGain);
osc.start(now);
osc.stop(now + stepDuration);
// Kick Drum on quarter notes
if (isHeavyBeat) {
const kick = this.ctx.createOscillator();
const kGain = this.ctx.createGain();
kick.type = 'sine';
kick.frequency.setValueAtTime(140, now);
kick.frequency.exponentialRampToValueAtTime(35, now + 0.12);
kGain.gain.setValueAtTime(0.7, now);
kGain.gain.exponentialRampToValueAtTime(0.01, now + 0.12);
kick.connect(kGain);
kGain.connect(this.musicGain);
kick.start(now);
kick.stop(now + 0.13);
}
// Snare Drum on 2 and 4
if (isSnareBeat) {
const snare = this.ctx.createBufferSource();
snare.buffer = this.createNoiseBuffer(0.15);
const sFilter = this.ctx.createBiquadFilter();
sFilter.type = 'highpass';
sFilter.frequency.setValueAtTime(1000, now);
const sGain = this.ctx.createGain();
sGain.gain.setValueAtTime(0.45, now);
sGain.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
snare.connect(sFilter);
sFilter.connect(sGain);
sGain.connect(this.musicGain);
snare.start(now);
}
// Closed Hi-hat every 8th note
if (this.musicStep % 2 === 0) {
const hat = this.ctx.createBufferSource();
hat.buffer = this.createNoiseBuffer(0.04);
const hFilter = this.ctx.createBiquadFilter();
hFilter.type = 'highpass';
hFilter.frequency.setValueAtTime(7000, now);
const hGain = this.ctx.createGain();
hGain.gain.setValueAtTime(0.15, now);
hGain.gain.exponentialRampToValueAtTime(0.01, now + 0.04);
hat.connect(hFilter);
hFilter.connect(hGain);
hGain.connect(this.musicGain);
hat.start(now);
}
this.musicStep++;
this.musicTimer = setTimeout(scheduleStep, stepDuration * 1000);
};
scheduleStep();
}
stopMusic() {
this.musicPlaying = false;
if (this.musicTimer) {
clearTimeout(this.musicTimer);
this.musicTimer = null;
}
}
toggleMusic() {
if (this.musicPlaying) {
this.stopMusic();
return false;
} else {
this.startMusic();
return true;
}
}
}
window.SoundSystem = SoundSystem;