Загрузить файлы в «js»
This commit is contained in:
212
js/sound.js
Normal file
212
js/sound.js
Normal file
@@ -0,0 +1,212 @@
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
AUDIO SYSTEM - Web Audio API Sound Effects
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
|
||||
const AudioSystem = {
|
||||
ctx: null,
|
||||
masterVolume: 0.3,
|
||||
sounds: {},
|
||||
muted: false,
|
||||
|
||||
init() {
|
||||
try {
|
||||
this.ctx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.createSounds();
|
||||
} catch (e) {
|
||||
console.log('Audio not supported');
|
||||
}
|
||||
},
|
||||
|
||||
createSounds() {
|
||||
// Jump sound - rising pitch
|
||||
this.sounds.jump = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(200, this.ctx.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(600, this.ctx.currentTime + 0.1);
|
||||
gain.gain.setValueAtTime(this.masterVolume * 0.5, this.ctx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.15);
|
||||
osc.start();
|
||||
osc.stop(this.ctx.currentTime + 0.15);
|
||||
};
|
||||
|
||||
// Double jump sound - higher pitch
|
||||
this.sounds.doubleJump = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(400, this.ctx.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(800, this.ctx.currentTime + 0.1);
|
||||
gain.gain.setValueAtTime(this.masterVolume * 0.4, this.ctx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.12);
|
||||
osc.start();
|
||||
osc.stop(this.ctx.currentTime + 0.12);
|
||||
};
|
||||
|
||||
// Coin collect - bright chime
|
||||
this.sounds.coin = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const frequencies = [880, 1100, 1320];
|
||||
frequencies.forEach((freq, i) => {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime + i * 0.05);
|
||||
gain.gain.setValueAtTime(0, this.ctx.currentTime);
|
||||
gain.gain.linearRampToValueAtTime(this.masterVolume * 0.3, this.ctx.currentTime + i * 0.05);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.2 + i * 0.05);
|
||||
osc.start(this.ctx.currentTime + i * 0.05);
|
||||
osc.stop(this.ctx.currentTime + 0.25 + i * 0.05);
|
||||
});
|
||||
};
|
||||
|
||||
// Damage/hurt - low thud
|
||||
this.sounds.hurt = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sawtooth';
|
||||
osc.frequency.setValueAtTime(150, this.ctx.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(50, this.ctx.currentTime + 0.2);
|
||||
gain.gain.setValueAtTime(this.masterVolume * 0.4, this.ctx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.2);
|
||||
osc.start();
|
||||
osc.stop(this.ctx.currentTime + 0.2);
|
||||
};
|
||||
|
||||
// Power-up collected - magical chime
|
||||
this.sounds.powerUp = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const frequencies = [523, 659, 784, 1047];
|
||||
frequencies.forEach((freq, i) => {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'triangle';
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime + i * 0.08);
|
||||
gain.gain.setValueAtTime(0, this.ctx.currentTime);
|
||||
gain.gain.linearRampToValueAtTime(this.masterVolume * 0.25, this.ctx.currentTime + i * 0.08);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.3 + i * 0.08);
|
||||
osc.start(this.ctx.currentTime + i * 0.08);
|
||||
osc.stop(this.ctx.currentTime + 0.4 + i * 0.08);
|
||||
});
|
||||
};
|
||||
|
||||
// Checkpoint - celebratory sound
|
||||
this.sounds.checkpoint = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const frequencies = [392, 523, 659, 784];
|
||||
frequencies.forEach((freq, i) => {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime + i * 0.1);
|
||||
gain.gain.setValueAtTime(0, this.ctx.currentTime);
|
||||
gain.gain.linearRampToValueAtTime(this.masterVolume * 0.3, this.ctx.currentTime + i * 0.1);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.3 + i * 0.1);
|
||||
osc.start(this.ctx.currentTime + i * 0.1);
|
||||
osc.stop(this.ctx.currentTime + 0.5 + i * 0.1);
|
||||
});
|
||||
};
|
||||
|
||||
// Level complete - victory fanfare
|
||||
this.sounds.levelComplete = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const notes = [523, 587, 659, 698, 784, 880, 988, 1047];
|
||||
notes.forEach((freq, i) => {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'square';
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime + i * 0.12);
|
||||
gain.gain.setValueAtTime(0, this.ctx.currentTime);
|
||||
gain.gain.linearRampToValueAtTime(this.masterVolume * 0.15, this.ctx.currentTime + i * 0.12 + 0.02);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.4 + i * 0.12);
|
||||
osc.start(this.ctx.currentTime + i * 0.12);
|
||||
osc.stop(this.ctx.currentTime + 0.5 + i * 0.12);
|
||||
});
|
||||
};
|
||||
|
||||
// Game over - sad descending
|
||||
this.sounds.gameOver = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const frequencies = [440, 415, 392, 370, 349, 330, 311, 294];
|
||||
frequencies.forEach((freq, i) => {
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sawtooth';
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime + i * 0.15);
|
||||
gain.gain.setValueAtTime(0, this.ctx.currentTime);
|
||||
gain.gain.linearRampToValueAtTime(this.masterVolume * 0.2, this.ctx.currentTime + i * 0.15);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.3 + i * 0.15);
|
||||
osc.start(this.ctx.currentTime + i * 0.15);
|
||||
osc.stop(this.ctx.currentTime + 0.4 + i * 0.15);
|
||||
});
|
||||
};
|
||||
|
||||
// Step/land sound
|
||||
this.sounds.land = () => {
|
||||
if (!this.ctx || this.muted) return;
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(80, this.ctx.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(40, this.ctx.currentTime + 0.05);
|
||||
gain.gain.setValueAtTime(this.masterVolume * 0.2, this.ctx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.05);
|
||||
osc.start();
|
||||
osc.stop(this.ctx.currentTime + 0.05);
|
||||
};
|
||||
},
|
||||
|
||||
play(soundName) {
|
||||
if (this.sounds[soundName]) {
|
||||
// Resume audio context if suspended (browser autoplay policy)
|
||||
if (this.ctx && this.ctx.state === 'suspended') {
|
||||
this.ctx.resume();
|
||||
}
|
||||
this.sounds[soundName]();
|
||||
}
|
||||
},
|
||||
|
||||
toggleMute() {
|
||||
this.muted = !this.muted;
|
||||
return this.muted;
|
||||
},
|
||||
|
||||
setVolume(vol) {
|
||||
this.masterVolume = Math.max(0, Math.min(1, vol));
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-initialize on first user interaction
|
||||
document.addEventListener('click', () => {
|
||||
if (!AudioSystem.ctx) {
|
||||
AudioSystem.init();
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
document.addEventListener('keydown', () => {
|
||||
if (!AudioSystem.ctx) {
|
||||
AudioSystem.init();
|
||||
}
|
||||
}, { once: true });
|
||||
Reference in New Issue
Block a user