wangwang/js/barkSound.js

99 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 简单的合成狗叫声
* 使用 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);
});
}