stack-overflow-game / src /utils /SoundGenerator.js
broadfield-dev's picture
Upload 51 files
78475cb verified
/**
* 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);
}
}
}