// 音频管理系统 class AudioManager { constructor() { this.audioContext = null; this.sounds = new Map(); this.isEnabled = true; this.volume = 0.7; this.isInitialized = false; this.hasShownInteractionHint = false; // 音效配置 this.soundConfig = { potActivate: { frequency: 440, duration: 0.5, type: 'magic' }, cooking: { frequency: 220, duration: 0.3, type: 'bubble' }, foodPop: { frequency: 660, duration: 0.2, type: 'pop' }, animalAppear: { frequency: 880, duration: 0.4, type: 'chirp' }, girlAppear: { frequency: 1320, duration: 0.6, type: 'sparkle' }, celebration: { frequency: 1760, duration: 1.0, type: 'fanfare' }, stopCooking: { frequency: 330, duration: 0.4, type: 'whoosh' } }; this.init(); } async init() { try { // 创建音频上下文(暂停状态) this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); // 注册用户交互事件来启动音频上下文 this.setupUserInteractionHandlers(); console.log('音频系统已准备,等待用户交互启动'); } catch (error) { console.warn('音频系统初始化失败:', error); this.isEnabled = false; } } setupUserInteractionHandlers() { const startAudio = async () => { if (this.audioContext && this.audioContext.state === 'suspended') { try { await this.audioContext.resume(); // 音频上下文启动后,预生成音效 if (!this.isInitialized) { this.preGenerateSounds(); this.isInitialized = true; console.log('音频系统初始化完成'); } // 隐藏音频激活提示 this.hideAudioActivationHint(); } catch (error) { console.warn('启动音频上下文失败:', error); } } }; // 监听各种用户交互事件 const events = ['click', 'touchstart', 'keydown']; const handler = () => { startAudio(); // 移除事件监听器,只需要启动一次 events.forEach(event => { document.removeEventListener(event, handler); }); }; events.forEach(event => { document.addEventListener(event, handler, { once: true }); }); } showAudioActivationHint() { const hint = document.getElementById('audioActivationHint'); if (hint) { hint.style.display = 'block'; console.log('显示音频激活提示'); } } hideAudioActivationHint() { const hint = document.getElementById('audioActivationHint'); if (hint) { hint.style.display = 'none'; console.log('隐藏音频激活提示'); } } async ensureAudioContext() { if (!this.audioContext) return false; if (this.audioContext.state === 'suspended') { try { await this.audioContext.resume(); // 如果音频上下文刚刚启动,且还未初始化,则初始化音效 if (!this.isInitialized) { this.preGenerateSounds(); this.isInitialized = true; console.log('音频系统延迟初始化完成'); } // 隐藏音频激活提示 this.hideAudioActivationHint(); } catch (error) { console.warn('无法恢复音频上下文:', error); return false; } } return this.audioContext.state === 'running'; } preGenerateSounds() { // 预生成所有音效的音频数据 Object.keys(this.soundConfig).forEach(soundName => { this.generateSound(soundName); }); } generateSound(soundName) { const config = this.soundConfig[soundName]; if (!config) return null; const sampleRate = this.audioContext.sampleRate; const duration = config.duration; const length = sampleRate * duration; const buffer = this.audioContext.createBuffer(1, length, sampleRate); const data = buffer.getChannelData(0); // 根据音效类型生成不同的波形 switch (config.type) { case 'magic': this.generateMagicSound(data, config, sampleRate); break; case 'bubble': this.generateBubbleSound(data, config, sampleRate); break; case 'pop': this.generatePopSound(data, config, sampleRate); break; case 'chirp': this.generateChirpSound(data, config, sampleRate); break; case 'sparkle': this.generateSparkleSound(data, config, sampleRate); break; case 'fanfare': this.generateFanfareSound(data, config, sampleRate); break; case 'whoosh': this.generateWhooshSound(data, config, sampleRate); break; default: this.generateSimpleBeep(data, config, sampleRate); } this.sounds.set(soundName, buffer); return buffer; } generateMagicSound(data, config, sampleRate) { // 魔法激活音效 - 上升的音调带有颤音 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 上升的频率 const freq = baseFreq * (1 + progress * 0.5); // 颤音效果 const vibrato = 1 + 0.1 * Math.sin(2 * Math.PI * 6 * t); // 主音调 const wave = Math.sin(2 * Math.PI * freq * vibrato * t); // 包络(淡入淡出) const envelope = Math.sin(Math.PI * progress) * Math.exp(-progress * 2); data[i] = wave * envelope * 0.3; } } generateBubbleSound(data, config, sampleRate) { // 烹饪冒泡音效 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 随机频率变化模拟冒泡 const randomFactor = 1 + 0.3 * (Math.random() - 0.5); const freq = baseFreq * randomFactor; // 多个正弦波叠加 const wave1 = Math.sin(2 * Math.PI * freq * t); const wave2 = Math.sin(2 * Math.PI * freq * 1.5 * t) * 0.5; const wave3 = Math.sin(2 * Math.PI * freq * 2 * t) * 0.25; const wave = wave1 + wave2 + wave3; // 快速衰减 const envelope = Math.exp(-progress * 8); data[i] = wave * envelope * 0.2; } } generatePopSound(data, config, sampleRate) { // 食物弹出音效 - 短促的爆破声 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 快速下降的频率 const freq = baseFreq * (1 - progress * 0.8); // 噪音成分 const noise = (Math.random() - 0.5) * 0.3; // 主音调 const wave = Math.sin(2 * Math.PI * freq * t) + noise; // 非常快的衰减 const envelope = Math.exp(-progress * 15); data[i] = wave * envelope * 0.4; } } generateChirpSound(data, config, sampleRate) { // 动物出现音效 - 鸟叫声 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 频率变化模拟鸟叫 const freqMod = Math.sin(2 * Math.PI * 8 * t) * 0.2; const freq = baseFreq * (1 + freqMod); const wave = Math.sin(2 * Math.PI * freq * t); // 自然的包络 const envelope = Math.sin(Math.PI * progress) * Math.exp(-progress * 3); data[i] = wave * envelope * 0.25; } } generateSparkleSound(data, config, sampleRate) { // 小女孩出现音效 - 闪亮的铃声 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 多个谐波创造铃声效果 const wave1 = Math.sin(2 * Math.PI * baseFreq * t); const wave2 = Math.sin(2 * Math.PI * baseFreq * 2 * t) * 0.5; const wave3 = Math.sin(2 * Math.PI * baseFreq * 3 * t) * 0.25; const wave4 = Math.sin(2 * Math.PI * baseFreq * 4 * t) * 0.125; const wave = wave1 + wave2 + wave3 + wave4; // 缓慢衰减 const envelope = Math.exp(-progress * 1.5); data[i] = wave * envelope * 0.2; } } generateFanfareSound(data, config, sampleRate) { // 庆祝音效 - 号角声 const baseFreq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; // 三和弦 const freq1 = baseFreq; const freq2 = baseFreq * 1.25; // 大三度 const freq3 = baseFreq * 1.5; // 完全五度 const wave1 = Math.sin(2 * Math.PI * freq1 * t); const wave2 = Math.sin(2 * Math.PI * freq2 * t) * 0.8; const wave3 = Math.sin(2 * Math.PI * freq3 * t) * 0.6; const wave = wave1 + wave2 + wave3; // 持续的包络 const envelope = Math.sin(Math.PI * progress) * 0.8; data[i] = wave * envelope * 0.15; } } generateWhooshSound(data, config, sampleRate) { // 停止音效 - 风声 const length = data.length; for (let i = 0; i < length; i++) { const progress = i / length; // 白噪音 const noise = (Math.random() - 0.5) * 2; // 低通滤波效果(简化) const cutoff = 1000 * (1 - progress); const filtered = noise * Math.exp(-progress * 3); // 快速衰减 const envelope = Math.exp(-progress * 5); data[i] = filtered * envelope * 0.3; } } generateSimpleBeep(data, config, sampleRate) { // 简单的蜂鸣声 const freq = config.frequency; const length = data.length; for (let i = 0; i < length; i++) { const t = i / sampleRate; const progress = i / length; const wave = Math.sin(2 * Math.PI * freq * t); const envelope = Math.exp(-progress * 3); data[i] = wave * envelope * 0.3; } } async playSound(soundName, volume = 1.0) { if (!this.isEnabled) return; const canPlay = await this.ensureAudioContext(); if (!canPlay) { // 如果是第一次尝试播放,显示视觉提示 if (!this.hasShownInteractionHint) { this.showAudioActivationHint(); this.hasShownInteractionHint = true; } return; } if (!this.isInitialized) { console.warn('音频系统尚未初始化'); return; } const buffer = this.sounds.get(soundName); if (!buffer) { console.warn(`音效 ${soundName} 不存在`); return; } try { const source = this.audioContext.createBufferSource(); const gainNode = this.audioContext.createGain(); source.buffer = buffer; source.connect(gainNode); gainNode.connect(this.audioContext.destination); // 设置音量 gainNode.gain.value = this.volume * volume; source.start(); // 清理资源 source.onended = () => { source.disconnect(); gainNode.disconnect(); }; } catch (error) { console.warn(`播放音效 ${soundName} 失败:`, error); } } // 播放特定音效的便捷方法 playPotActivate() { this.playSound('potActivate'); } playCooking() { this.playSound('cooking', 0.5); } playFoodPop() { this.playSound('foodPop', 0.8); } playAnimalAppear() { this.playSound('animalAppear', 0.6); } playGirlAppear() { this.playSound('girlAppear', 0.7); } playCelebration() { this.playSound('celebration', 0.9); } playStopCooking() { this.playSound('stopCooking', 0.6); } // 播放随机音调的食物弹出声 playRandomFoodPop() { const randomPitch = 0.8 + Math.random() * 0.4; // 0.8 到 1.2 this.playSound('foodPop', randomPitch); } // 设置音量 setVolume(volume) { this.volume = Math.max(0, Math.min(1, volume)); } // 启用/禁用音效 setEnabled(enabled) { this.isEnabled = enabled; localStorage.setItem('magicPotAudioEnabled', enabled.toString()); } // 获取音效状态 getStatus() { return { enabled: this.isEnabled, initialized: this.isInitialized, volume: this.volume, soundCount: this.sounds.size, contextState: this.audioContext ? this.audioContext.state : 'none' }; } // 测试所有音效 async testAllSounds() { const soundNames = Object.keys(this.soundConfig); for (let i = 0; i < soundNames.length; i++) { const soundName = soundNames[i]; console.log(`测试音效: ${soundName}`); await this.playSound(soundName); // 等待一段时间再播放下一个 await new Promise(resolve => setTimeout(resolve, 800)); } } } // 创建全局音频管理器实例 window.audioManager = new AudioManager();