/** * 简单的合成狗叫声 * 使用 Web Audio API,无需外部音频文件 */ let audioCtx = null; function getAudioContext() { if (!audioCtx) { const AC = window.AudioContext || window.webkitAudioContext; if (AC) { audioCtx = new AC(); } } return audioCtx; } export default function playBark() { const ctx = getAudioContext(); if (!ctx) return; // 如果 AudioContext 被暂停(某些浏览器/微信环境),先 resume if (ctx.state === 'suspended') { ctx.resume(); } const now = ctx.currentTime; const duration = 0.18; // 吠叫持续约 180ms // 1. 噪声源(吠叫的嘶哑感) const sampleRate = ctx.sampleRate; const bufferSize = sampleRate * duration; const noiseBuffer = ctx.createBuffer(1, bufferSize, sampleRate); const output = noiseBuffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { output[i] = Math.random() * 2 - 1; } const noise = ctx.createBufferSource(); noise.buffer = noiseBuffer; // 2. 带通滤波器,集中在 600-1200Hz 类似狗叫的频段 const bandpass = ctx.createBiquadFilter(); bandpass.type = 'bandpass'; bandpass.frequency.value = 900; bandpass.Q.value = 0.8; // 3. 低频振荡器(吠叫的胸腔共鸣感) const osc = ctx.createOscillator(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(180, now); osc.frequency.exponentialRampToValueAtTime(120, now + duration); const oscGain = ctx.createGain(); oscGain.gain.value = 0.25; // 4. 主音量包络:快速Attack,快速Decay const masterGain = ctx.createGain(); masterGain.gain.setValueAtTime(0, now); masterGain.gain.linearRampToValueAtTime(0.6, now + 0.02); masterGain.gain.exponentialRampToValueAtTime(0.01, now + duration); // 连接 noise.connect(bandpass); bandpass.connect(masterGain); osc.connect(oscGain); oscGain.connect(masterGain); masterGain.connect(ctx.destination); // 播放 noise.start(now); noise.stop(now + duration); osc.start(now); osc.stop(now + duration); } export function playWinSound() { const ctx = getAudioContext(); if (!ctx) return; if (ctx.state === 'suspended') ctx.resume(); const now = ctx.currentTime; const notes = [523.25, 659.25, 783.99, 1046.50]; // C E G C' notes.forEach((freq, i) => { const osc = ctx.createOscillator(); osc.type = 'sine'; osc.frequency.value = freq; const gain = ctx.createGain(); gain.gain.setValueAtTime(0, now + i * 0.1); gain.gain.linearRampToValueAtTime(0.2, now + i * 0.1 + 0.02); gain.gain.exponentialRampToValueAtTime(0.01, now + i * 0.1 + 0.25); osc.connect(gain); gain.connect(ctx.destination); osc.start(now + i * 0.1); osc.stop(now + i * 0.1 + 0.3); }); }