| |
| |
| |
| export default class SoundGenerator { |
| |
| |
| |
| |
| |
| |
| static getAudioContext() { |
| if (!this.audioContext) { |
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| } |
| return this.audioContext; |
| } |
|
|
| static playBeep(frequency, duration, volume = 0.3) { |
| try { |
| const audioContext = this.getAudioContext(); |
|
|
| |
| const oscillator = audioContext.createOscillator(); |
| const gainNode = audioContext.createGain(); |
|
|
| oscillator.connect(gainNode); |
| gainNode.connect(audioContext.destination); |
|
|
| oscillator.frequency.value = frequency; |
| oscillator.type = 'square'; |
|
|
| |
| gainNode.gain.setValueAtTime(volume, audioContext.currentTime); |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration); |
|
|
| oscillator.start(audioContext.currentTime); |
| oscillator.stop(audioContext.currentTime + duration); |
| } catch (e) { |
| console.warn('Audio not available:', e); |
| } |
| } |
|
|
| static playMove() { |
| this.playBeep(200, 0.05, 0.2); |
| } |
|
|
| static playRotate() { |
| this.playBeep(300, 0.08, 0.25); |
| } |
|
|
| static playDrop() { |
| this.playBeep(150, 0.15, 0.3); |
| } |
|
|
| static playLineClear(lineCount = 1) { |
| |
| this.playNoisyExplosion(); |
|
|
| |
| if (lineCount === 2) { |
| setTimeout(() => this.playBeep(600, 0.15, 0.25), 100); |
| setTimeout(() => this.playBeep(800, 0.15, 0.25), 200); |
| } else if (lineCount === 3) { |
| setTimeout(() => this.playBeep(700, 0.12, 0.25), 100); |
| setTimeout(() => this.playBeep(900, 0.12, 0.25), 180); |
| setTimeout(() => this.playBeep(1100, 0.15, 0.25), 260); |
| } else if (lineCount >= 4) { |
| |
| setTimeout(() => this.playBeep(800, 0.1, 0.3), 100); |
| setTimeout(() => this.playBeep(1000, 0.1, 0.3), 180); |
| setTimeout(() => this.playBeep(1200, 0.1, 0.3), 260); |
| setTimeout(() => this.playBeep(1400, 0.2, 0.3), 340); |
| } |
| } |
|
|
| static playNoisyExplosion() { |
| try { |
| const audioContext = this.getAudioContext(); |
|
|
| |
| const bufferSize = audioContext.sampleRate * 0.5; |
| const noiseBuffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); |
| const noiseData = noiseBuffer.getChannelData(0); |
|
|
| for (let i = 0; i < bufferSize; i++) { |
| noiseData[i] = Math.random() * 2 - 1; |
| } |
|
|
| const noise = audioContext.createBufferSource(); |
| noise.buffer = noiseBuffer; |
|
|
| |
| const filter = audioContext.createBiquadFilter(); |
| filter.type = 'lowpass'; |
| filter.frequency.setValueAtTime(2000, audioContext.currentTime); |
| filter.frequency.exponentialRampToValueAtTime(80, audioContext.currentTime + 0.5); |
|
|
| const gainNode = audioContext.createGain(); |
| gainNode.gain.setValueAtTime(0.7, audioContext.currentTime); |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); |
|
|
| noise.connect(filter); |
| filter.connect(gainNode); |
| gainNode.connect(audioContext.destination); |
|
|
| noise.start(audioContext.currentTime); |
| noise.stop(audioContext.currentTime + 0.5); |
| } catch (e) { |
| console.warn('Audio not available:', e); |
| } |
| } |
|
|
| static playSoftDrop() { |
| this.playBeep(120, 0.04, 0.15); |
| } |
|
|
| static playTetris() { |
| |
| this.playBeep(500, 0.1, 0.35); |
| setTimeout(() => this.playBeep(600, 0.1, 0.35), 60); |
| setTimeout(() => this.playBeep(700, 0.1, 0.35), 120); |
| setTimeout(() => this.playBeep(800, 0.2, 0.35), 180); |
| } |
|
|
| static playLevelUp() { |
| |
| this.playBeep(400, 0.1, 0.3); |
| setTimeout(() => this.playBeep(500, 0.1, 0.3), 80); |
| setTimeout(() => this.playBeep(600, 0.1, 0.3), 160); |
| setTimeout(() => this.playBeep(700, 0.2, 0.3), 240); |
| } |
|
|
| static playGameOver() { |
| |
| this.playBeep(400, 0.15, 0.3); |
| setTimeout(() => this.playBeep(300, 0.15, 0.3), 120); |
| setTimeout(() => this.playBeep(200, 0.15, 0.3), 240); |
| setTimeout(() => this.playBeep(100, 0.3, 0.3), 360); |
| } |
|
|
| static playWoosh() { |
| try { |
| const audioContext = this.getAudioContext(); |
|
|
| |
| const bufferSize = audioContext.sampleRate * 0.4; |
| const noiseBuffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); |
| const noiseData = noiseBuffer.getChannelData(0); |
|
|
| for (let i = 0; i < bufferSize; i++) { |
| noiseData[i] = Math.random() * 2 - 1; |
| } |
|
|
| const noise = audioContext.createBufferSource(); |
| noise.buffer = noiseBuffer; |
|
|
| |
| const filter = audioContext.createBiquadFilter(); |
| filter.type = 'highpass'; |
| filter.frequency.setValueAtTime(800, audioContext.currentTime); |
| filter.frequency.exponentialRampToValueAtTime(200, audioContext.currentTime + 0.4); |
|
|
| const gainNode = audioContext.createGain(); |
| gainNode.gain.setValueAtTime(0.4, audioContext.currentTime); |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.4); |
|
|
| noise.connect(filter); |
| filter.connect(gainNode); |
| gainNode.connect(audioContext.destination); |
|
|
| noise.start(audioContext.currentTime); |
| noise.stop(audioContext.currentTime + 0.4); |
| } catch (e) { |
| console.warn('Audio not available:', e); |
| } |
| } |
| } |
|
|
|
|
|
|