Spaces:
Sleeping
Sleeping
File size: 5,754 Bytes
78475cb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | /**
* Generate simple sound effects using Web Audio API
*/
export default class SoundGenerator {
/**
* Play a simple beep sound
* @param {number} frequency - Frequency in Hz
* @param {number} duration - Duration in seconds
* @param {number} volume - Volume (0-1)
*/
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();
// Create oscillator
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = frequency;
oscillator.type = 'square'; // Retro square wave sound
// Envelope
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) {
// Noisy 8-bit explosion
this.playNoisyExplosion();
// Add bonus sounds for multiple lines
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) {
// Tetris! Extra exciting
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();
// Create white noise
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;
// Filter the noise
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() {
// Exciting sound for 4-line clear
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() {
// Ascending tone
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() {
// Descending tone
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();
// Create white noise for woosh effect
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;
// High-pass filter for airy woosh sound
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);
}
}
}
|