120 lines
3.0 KiB
JavaScript
120 lines
3.0 KiB
JavaScript
/**
|
|
* Cosmic Garden - Audio System
|
|
* Web Audio API-based sound generation (no external assets needed)
|
|
*/
|
|
|
|
const Audio = {
|
|
ctx: null,
|
|
enabled: true,
|
|
|
|
/**
|
|
* Initialize audio context (must be called after user interaction)
|
|
*/
|
|
init() {
|
|
try {
|
|
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
this.ctx = new AudioContext();
|
|
console.log('🔊 Audio system initialized');
|
|
} catch (e) {
|
|
console.warn('Web Audio API not supported');
|
|
this.enabled = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resume audio context (for browsers that suspend it)
|
|
*/
|
|
resume() {
|
|
if (this.ctx && this.ctx.state === 'suspended') {
|
|
this.ctx.resume();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Create a simple tone
|
|
*/
|
|
playTone(frequency, duration, type = 'sine', volume = 0.1) {
|
|
if (!this.enabled || !this.ctx) return;
|
|
|
|
const osc = this.ctx.createOscillator();
|
|
const gain = this.ctx.createGain();
|
|
|
|
osc.type = type;
|
|
osc.frequency.setValueAtTime(frequency, this.ctx.currentTime);
|
|
|
|
gain.gain.setValueAtTime(volume, this.ctx.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + duration);
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.ctx.destination);
|
|
|
|
osc.start(this.ctx.currentTime);
|
|
osc.stop(this.ctx.currentTime + duration);
|
|
},
|
|
|
|
/**
|
|
* Play a rising arpeggio (for clicks)
|
|
*/
|
|
playClick() {
|
|
if (!this.enabled) return;
|
|
this.resume();
|
|
|
|
const now = this.ctx.currentTime;
|
|
const notes = [523.25, 659.25, 783.99]; // C major chord
|
|
|
|
notes.forEach((freq, i) => {
|
|
setTimeout(() => {
|
|
this.playTone(freq, 0.1, 'sine', 0.08);
|
|
}, i * 30);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Play a purchase sound (descending chime)
|
|
*/
|
|
playPurchase() {
|
|
if (!this.enabled) return;
|
|
this.resume();
|
|
|
|
const notes = [783.99, 659.25, 523.25]; // G - E - C
|
|
|
|
notes.forEach((freq, i) => {
|
|
setTimeout(() => {
|
|
this.playTone(freq, 0.15, 'triangle', 0.1);
|
|
}, i * 50);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Play an achievement unlock fanfare
|
|
*/
|
|
playAchievement() {
|
|
if (!this.enabled) return;
|
|
this.resume();
|
|
|
|
const notes = [523.25, 659.25, 783.99, 1046.50]; // C - E - G - C (high)
|
|
|
|
notes.forEach((freq, i) => {
|
|
setTimeout(() => {
|
|
this.playTone(freq, 0.2, 'square', 0.06);
|
|
}, i * 80);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Play a notification sound
|
|
*/
|
|
playNotification() {
|
|
if (!this.enabled) return;
|
|
this.resume();
|
|
|
|
this.playTone(880, 0.1, 'sine', 0.08);
|
|
setTimeout(() => {
|
|
this.playTone(1100, 0.15, 'sine', 0.08);
|
|
}, 100);
|
|
}
|
|
};
|
|
|
|
// Make globally accessible
|
|
window.Audio = Audio;
|