class AudioSystem { constructor() { this.audioContext = null; this.sounds = {}; this.volume = 0.7; this.enabled = true; this.initialized = false; this.initializeAudio(); } async initializeAudio() { try { this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); this.generateSounds(); this.initialized = true; } catch (error) { console.warn('Audio initialization failed:', error); this.enabled = false; } } generateSounds() { this.sounds = { puckHit: this.createPuckHitSound(), stick: this.createStickSound(), goal: this.createGoalSound(), save: this.createSaveSound(), whistle: this.createWhistleSound(), crowd: this.createCrowdSound(), skate: this.createSkateSound(), board: this.createBoardSound() }; } createPuckHitSound() { return this.createPercussiveSound(150, 0.1, 'sawtooth'); } createStickSound() { return this.createPercussiveSound(200, 0.05, 'square'); } createGoalSound() { return this.createMelodicSound([400, 500, 600], 0.8, 'sine'); } createSaveSound() { return this.createPercussiveSound(100, 0.3, 'triangle'); } createWhistleSound() { return this.createMelodicSound([800, 900], 0.5, 'sine'); } createCrowdSound() { return this.createNoiseSound(0.2, 2.0); } createSkateSound() { return this.createNoiseSound(0.1, 0.2); } createBoardSound() { return this.createPercussiveSound(80, 0.2, 'triangle'); } createPercussiveSound(frequency, duration, waveType = 'sine') { return () => { if (!this.enabled || !this.audioContext) return; const oscillator = this.audioContext.createOscillator(); const gainNode = this.audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(this.audioContext.destination); oscillator.type = waveType; oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime); oscillator.frequency.exponentialRampToValueAtTime( frequency * 0.5, this.audioContext.currentTime + duration ); gainNode.gain.setValueAtTime(this.volume * 0.3, this.audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime( 0.001, this.audioContext.currentTime + duration ); oscillator.start(this.audioContext.currentTime); oscillator.stop(this.audioContext.currentTime + duration); }; } createMelodicSound(frequencies, duration, waveType = 'sine') { return () => { if (!this.enabled || !this.audioContext) return; frequencies.forEach((freq, index) => { const delay = index * 0.1; const oscillator = this.audioContext.createOscillator(); const gainNode = this.audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(this.audioContext.destination); oscillator.type = waveType; oscillator.frequency.setValueAtTime(freq, this.audioContext.currentTime + delay); gainNode.gain.setValueAtTime(0, this.audioContext.currentTime + delay); gainNode.gain.linearRampToValueAtTime( this.volume * 0.2, this.audioContext.currentTime + delay + 0.05 ); gainNode.gain.exponentialRampToValueAtTime( 0.001, this.audioContext.currentTime + delay + duration ); oscillator.start(this.audioContext.currentTime + delay); oscillator.stop(this.audioContext.currentTime + delay + duration); }); }; } createNoiseSound(volume, duration) { return () => { if (!this.enabled || !this.audioContext) return; const bufferSize = this.audioContext.sampleRate * duration; const buffer = this.audioContext.createBuffer(1, bufferSize, this.audioContext.sampleRate); const data = buffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { data[i] = (Math.random() * 2 - 1) * volume * this.volume; } const source = this.audioContext.createBufferSource(); const gainNode = this.audioContext.createGain(); const filter = this.audioContext.createBiquadFilter(); source.buffer = buffer; source.connect(filter); filter.connect(gainNode); gainNode.connect(this.audioContext.destination); filter.type = 'lowpass'; filter.frequency.setValueAtTime(2000, this.audioContext.currentTime); gainNode.gain.setValueAtTime(1, this.audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration); source.start(this.audioContext.currentTime); }; } playSound(soundName, volume = 1) { if (!this.enabled || !this.sounds[soundName]) return; try { if (this.audioContext.state === 'suspended') { this.audioContext.resume(); } this.sounds[soundName](); } catch (error) { console.warn('Failed to play sound:', soundName, error); } } onPuckHit(velocity) { const intensity = Math.min(1, velocity / 500); if (intensity > 0.1) { this.playSound('puckHit', intensity); } } onStickContact() { this.playSound('stick'); } onGoal(team) { this.playSound('goal'); setTimeout(() => this.playSound('crowd'), 500); } onSave() { this.playSound('save'); } onPenalty() { this.playSound('whistle'); } onPeriodEnd() { this.playSound('whistle'); } onBoardCollision(velocity) { const intensity = Math.min(1, velocity / 300); if (intensity > 0.2) { this.playSound('board', intensity); } } onPlayerMovement(speed) { if (speed > 100 && Math.random() < 0.01) { this.playSound('skate', 0.3); } } setVolume(volume) { this.volume = Math.max(0, Math.min(1, volume)); } setEnabled(enabled) { this.enabled = enabled; } toggle() { this.enabled = !this.enabled; return this.enabled; } resume() { if (this.audioContext && this.audioContext.state === 'suspended') { return this.audioContext.resume(); } } suspend() { if (this.audioContext && this.audioContext.state === 'running') { return this.audioContext.suspend(); } } getState() { return { enabled: this.enabled, volume: this.volume, initialized: this.initialized, contextState: this.audioContext ? this.audioContext.state : 'not-initialized' }; } }