removed alerts
This commit is contained in:
323
js/ui/audio.js
Normal file
323
js/ui/audio.js
Normal file
@@ -0,0 +1,323 @@
|
||||
// Audio Manager using Web Audio API
|
||||
const audioManager = {
|
||||
audioContext: null,
|
||||
muted: false,
|
||||
sounds: {},
|
||||
lowTimeThreshold: 10, // Seconds threshold for low time warning
|
||||
lastTickTime: 0, // Track when we started continuous ticking
|
||||
tickFadeoutTime: 3, // Seconds after which tick sound fades out
|
||||
|
||||
// Initialize the audio context
|
||||
init() {
|
||||
try {
|
||||
// Create AudioContext (with fallback for older browsers)
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
// Check for saved mute preference
|
||||
const savedMute = localStorage.getItem('gameTimerMuted');
|
||||
this.muted = savedMute === 'true';
|
||||
|
||||
// Create all the sounds
|
||||
this.createSounds();
|
||||
|
||||
console.log('Web Audio API initialized successfully');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Web Audio API initialization failed:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Create all the sound generators
|
||||
createSounds() {
|
||||
// Game sounds
|
||||
this.sounds.tick = this.createTickSound();
|
||||
this.sounds.lowTime = this.createLowTimeSound();
|
||||
this.sounds.timeUp = this.createTimeUpSound();
|
||||
this.sounds.gameStart = this.createGameStartSound();
|
||||
this.sounds.gamePause = this.createGamePauseSound();
|
||||
this.sounds.gameResume = this.createGameResumeSound();
|
||||
this.sounds.gameOver = this.createGameOverSound();
|
||||
this.sounds.playerSwitch = this.createPlayerSwitchSound();
|
||||
|
||||
// UI sounds
|
||||
this.sounds.buttonClick = this.createButtonClickSound();
|
||||
this.sounds.modalOpen = this.createModalOpenSound();
|
||||
this.sounds.modalClose = this.createModalCloseSound();
|
||||
this.sounds.playerAdded = this.createPlayerAddedSound();
|
||||
this.sounds.playerEdited = this.createPlayerEditedSound();
|
||||
this.sounds.playerDeleted = this.createPlayerDeletedSound();
|
||||
},
|
||||
|
||||
// Helper function to create an oscillator
|
||||
createOscillator(type, frequency, startTime, duration, gain = 1.0, ramp = false) {
|
||||
if (this.audioContext === null) this.init();
|
||||
|
||||
const oscillator = this.audioContext.createOscillator();
|
||||
const gainNode = this.audioContext.createGain();
|
||||
|
||||
oscillator.type = type;
|
||||
oscillator.frequency.value = frequency;
|
||||
gainNode.gain.value = gain;
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(this.audioContext.destination);
|
||||
|
||||
oscillator.start(startTime);
|
||||
|
||||
if (ramp) {
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.001, startTime + duration);
|
||||
}
|
||||
|
||||
oscillator.stop(startTime + duration);
|
||||
|
||||
return { oscillator, gainNode };
|
||||
},
|
||||
|
||||
// Sound creators
|
||||
createTickSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
const currentTime = Date.now() / 1000;
|
||||
|
||||
// Initialize lastTickTime if it's not set
|
||||
if (this.lastTickTime === 0) {
|
||||
this.lastTickTime = currentTime;
|
||||
}
|
||||
|
||||
// Calculate how long we've been ticking continuously
|
||||
const tickDuration = currentTime - this.lastTickTime;
|
||||
|
||||
// Determine volume based on duration
|
||||
let volume = 0.1; // Default/initial volume
|
||||
|
||||
if (tickDuration <= this.tickFadeoutTime) {
|
||||
// Linear fade from 0.1 to 0 over tickFadeoutTime seconds
|
||||
volume = 0.1 * (1 - (tickDuration / this.tickFadeoutTime));
|
||||
} else {
|
||||
// After tickFadeoutTime, don't play any sound
|
||||
return; // Exit without playing sound
|
||||
}
|
||||
|
||||
// Only play if volume is significant
|
||||
if (volume > 0.001) {
|
||||
this.createOscillator('sine', 800, now, 0.03, volume);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
createLowTimeSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
// Low time warning is always audible
|
||||
this.createOscillator('triangle', 660, now, 0.1, 0.2);
|
||||
// Reset tick fade timer on low time warning
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createTimeUpSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// First note
|
||||
this.createOscillator('sawtooth', 440, now, 0.2, 0.3);
|
||||
|
||||
// Second note (lower)
|
||||
this.createOscillator('sawtooth', 220, now + 0.25, 0.3, 0.4);
|
||||
|
||||
// Reset tick fade timer
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createGameStartSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Rising sequence
|
||||
this.createOscillator('sine', 440, now, 0.1, 0.3);
|
||||
this.createOscillator('sine', 554, now + 0.1, 0.1, 0.3);
|
||||
this.createOscillator('sine', 659, now + 0.2, 0.3, 0.3, true);
|
||||
|
||||
// Reset tick fade timer
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createGamePauseSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Two notes pause sound
|
||||
this.createOscillator('sine', 659, now, 0.1, 0.3);
|
||||
this.createOscillator('sine', 523, now + 0.15, 0.2, 0.3, true);
|
||||
|
||||
// Reset tick fade timer
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createGameResumeSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Rising sequence (opposite of pause)
|
||||
this.createOscillator('sine', 523, now, 0.1, 0.3);
|
||||
this.createOscillator('sine', 659, now + 0.15, 0.2, 0.3, true);
|
||||
|
||||
// Reset tick fade timer
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createGameOverSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Fanfare
|
||||
this.createOscillator('square', 440, now, 0.1, 0.3);
|
||||
this.createOscillator('square', 554, now + 0.1, 0.1, 0.3);
|
||||
this.createOscillator('square', 659, now + 0.2, 0.1, 0.3);
|
||||
this.createOscillator('square', 880, now + 0.3, 0.4, 0.3, true);
|
||||
|
||||
// Reset tick fade timer
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createPlayerSwitchSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
this.createOscillator('sine', 1200, now, 0.05, 0.2);
|
||||
|
||||
// Reset tick fade timer on player switch
|
||||
this.lastTickTime = 0;
|
||||
};
|
||||
},
|
||||
|
||||
createButtonClickSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
this.createOscillator('sine', 700, now, 0.04, 0.1);
|
||||
};
|
||||
},
|
||||
|
||||
createModalOpenSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Ascending sound
|
||||
this.createOscillator('sine', 400, now, 0.1, 0.2);
|
||||
this.createOscillator('sine', 600, now + 0.1, 0.1, 0.2);
|
||||
};
|
||||
},
|
||||
|
||||
createModalCloseSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Descending sound
|
||||
this.createOscillator('sine', 600, now, 0.1, 0.2);
|
||||
this.createOscillator('sine', 400, now + 0.1, 0.1, 0.2);
|
||||
};
|
||||
},
|
||||
|
||||
createPlayerAddedSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Positive ascending notes
|
||||
this.createOscillator('sine', 440, now, 0.1, 0.2);
|
||||
this.createOscillator('sine', 523, now + 0.1, 0.1, 0.2);
|
||||
this.createOscillator('sine', 659, now + 0.2, 0.2, 0.2, true);
|
||||
};
|
||||
},
|
||||
|
||||
createPlayerEditedSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Two note confirmation
|
||||
this.createOscillator('sine', 440, now, 0.1, 0.2);
|
||||
this.createOscillator('sine', 523, now + 0.15, 0.15, 0.2);
|
||||
};
|
||||
},
|
||||
|
||||
createPlayerDeletedSound() {
|
||||
return () => {
|
||||
const now = this.audioContext.currentTime;
|
||||
|
||||
// Descending notes
|
||||
this.createOscillator('sine', 659, now, 0.1, 0.2);
|
||||
this.createOscillator('sine', 523, now + 0.1, 0.1, 0.2);
|
||||
this.createOscillator('sine', 392, now + 0.2, 0.2, 0.2, true);
|
||||
};
|
||||
},
|
||||
|
||||
// Play a sound if not muted
|
||||
play(soundName) {
|
||||
if (this.muted || !this.sounds[soundName]) return;
|
||||
|
||||
// Resume audio context if it's suspended (needed for newer browsers)
|
||||
if (this.audioContext.state === 'suspended') {
|
||||
this.audioContext.resume();
|
||||
}
|
||||
|
||||
this.sounds[soundName]();
|
||||
},
|
||||
|
||||
// Toggle mute state
|
||||
toggleMute() {
|
||||
this.muted = !this.muted;
|
||||
localStorage.setItem('gameTimerMuted', this.muted);
|
||||
return this.muted;
|
||||
},
|
||||
|
||||
// Play timer sounds based on remaining time
|
||||
playTimerSound(remainingSeconds) {
|
||||
if (remainingSeconds <= 0) {
|
||||
// Reset tick fade timer when timer stops
|
||||
this.lastTickTime = 0;
|
||||
return; // Don't play sounds for zero time
|
||||
}
|
||||
|
||||
if (remainingSeconds <= this.lowTimeThreshold) {
|
||||
// Play low time warning sound (this resets the tick fade timer)
|
||||
this.play('lowTime');
|
||||
} else if (remainingSeconds % 1 === 0) {
|
||||
// Normal tick sound on every second
|
||||
this.play('tick');
|
||||
}
|
||||
},
|
||||
|
||||
// Play timer expired sound
|
||||
playTimerExpired() {
|
||||
this.play('timeUp');
|
||||
},
|
||||
|
||||
// Stop all sounds and reset the tick fading
|
||||
stopAllSounds() {
|
||||
// Reset tick fade timer when stopping sounds
|
||||
this.lastTickTime = 0;
|
||||
},
|
||||
|
||||
// Stop timer-specific sounds (tick, low time warning)
|
||||
stopTimerSounds() {
|
||||
// Reset tick fade timer when stopping timer sounds
|
||||
this.lastTickTime = 0;
|
||||
// In this implementation, sounds are so short-lived that
|
||||
// they don't need to be explicitly stopped, just fade prevention is enough
|
||||
},
|
||||
|
||||
// Reset the tick fading (call this when timer is paused or player changes)
|
||||
resetTickFade() {
|
||||
this.lastTickTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize audio on module load
|
||||
audioManager.init();
|
||||
|
||||
// Export the audio manager
|
||||
export default audioManager;
|
||||
Reference in New Issue
Block a user