You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用Web Audio API生成钢琴、长笛等乐器类音色?

如何使用Web Audio API生成钢琴、长笛等乐器类音色?

嘿,你现在用的正弦波是最纯净的「基础款」声音,但钢琴、长笛这类真实乐器的音色可复杂多啦——它们的差异主要来自泛音(谐波)结构音量/频率包络,还有不同的滤波特性。我来一步步帮你改造代码,做出接近真实乐器的声音👇

先搞懂核心:乐器音色的3个关键

  • 泛音(谐波):除了你设置的基频freq,真实乐器会同时发出多个比基频高的谐波(比如2倍、3倍基频),不同乐器的谐波比例不一样,音色就天差地别
  • ADSR包络:音量随时间的变化曲线,分四个阶段:
    • Attack:从静音到最大音量的时间(钢琴快,长笛慢)
    • Decay:从最大音量降到持续音量的时间
    • Sustain:持续阶段的音量比例(钢琴Sustain低,长笛Sustain高)
    • Release:松开按键后音量降到静音的时间
  • 滤波:模拟乐器发声后的高频衰减(比如钢琴的高频泛音会随时间慢慢消失)

1. 实现钢琴音色

钢琴的特点:起音极快,然后快速衰减到一个较低的持续音量,泛音非常丰富(基频+多倍谐波)。我们可以叠加多个振荡器模拟泛音,再用ADSR包络控制音量:

// 先确保你已经初始化了AudioContext
const ctx = new (window.AudioContext || window.webkitAudioContext)();

function playPianoTone(freq, duration, delay = 0) {
  const startTime = ctx.currentTime + delay;
  const endTime = startTime + duration;

  // 钢琴的泛音组合:基频为主,叠加2/3/4倍泛音(音量依次降低)
  const harmonics = [
    { freqRatio: 1, gain: 0.3 }, // 基频,音量最大
    { freqRatio: 2, gain: 0.15 }, // 2倍泛音
    { freqRatio: 3, gain: 0.08 }, // 3倍泛音
    { freqRatio: 4, gain: 0.03 }, // 4倍泛音
  ];

  // 创建主增益节点,统一控制整体ADSR包络
  const masterGain = ctx.createGain();
  masterGain.connect(ctx.destination);

  // 叠加每个泛音的振荡器
  harmonics.forEach(harmonic => {
    const osc = ctx.createOscillator();
    osc.type = "sine"; // 用正弦波叠加泛音,更接近钢琴的柔和质感
    osc.frequency.value = freq * harmonic.freqRatio;
    
    const oscGain = ctx.createGain();
    oscGain.gain.setValueAtTime(harmonic.gain, startTime);
    
    osc.connect(oscGain).connect(masterGain);
    osc.start(startTime);
    osc.stop(endTime);
  });

  // 钢琴的ADSR包络: Attack极短,快速衰减后保持低音量,释音平缓
  masterGain.gain.setValueAtTime(0, startTime);
  masterGain.gain.linearRampToValueAtTime(1, startTime + 0.01); // 0.01秒快速起音
  masterGain.gain.linearRampToValueAtTime(0.1, startTime + 0.01 + 0.2); // 0.2秒衰减到低音量
  masterGain.gain.setValueAtTime(0.1, endTime);
  masterGain.gain.linearRampToValueAtTime(0, endTime + 0.3); // 0.3秒释音
}

// 调用示例:弹一个440Hz的A音,持续1秒
playPianoTone(440, 1);

2. 实现长笛音色

长笛是木管乐器,特点是起音平缓(不会突然炸响),持续阶段音量稳定,泛音以奇次谐波为主(1、3、5倍频),音色更通透。我们可以用少量泛音+缓慢的ADSR包络,再加个带通滤波模拟管乐的共鸣:

function playFluteTone(freq, duration, delay = 0) {
  const startTime = ctx.currentTime + delay;
  const endTime = startTime + duration;

  // 长笛泛音:基频为主,叠加3/5倍奇次泛音
  const harmonics = [
    { freqRatio: 1, gain: 0.4 },
    { freqRatio: 3, gain: 0.12 },
    { freqRatio: 5, gain: 0.05 },
  ];

  const masterGain = ctx.createGain();
  masterGain.connect(ctx.destination);

  // 加一个带通滤波,模拟长笛的共鸣特性,让音色更集中
  const bandpassFilter = ctx.createBiquadFilter();
  bandpassFilter.type = "bandpass";
  bandpassFilter.frequency.value = freq;
  bandpassFilter.Q.value = 1; // Q值越小,滤波带宽越宽
  bandpassFilter.connect(masterGain);

  harmonics.forEach(harmonic => {
    const osc = ctx.createOscillator();
    osc.type = "sine";
    osc.frequency.value = freq * harmonic.freqRatio;
    
    const oscGain = ctx.createGain();
    oscGain.gain.setValueAtTime(harmonic.gain, startTime);
    
    osc.connect(oscGain).connect(bandpassFilter);
    osc.start(startTime);
    osc.stop(endTime);
  });

  // 长笛的ADSR包络:平缓起音,稳定持续,缓慢释音
  masterGain.gain.setValueAtTime(0, startTime);
  masterGain.gain.linearRampToValueAtTime(1, startTime + 0.3); // 0.3秒平缓起音
  masterGain.gain.linearRampToValueAtTime(0.8, startTime + 0.3 + 0.1); // 轻微衰减到稳定音量
  masterGain.gain.setValueAtTime(0.8, endTime);
  masterGain.gain.linearRampToValueAtTime(0, endTime + 0.5); // 0.5秒缓慢释音
}

// 调用示例:弹一个349Hz的F音,持续2秒
playFluteTone(349, 2);

进阶小技巧

  • 要是觉得叠加多个振荡器太麻烦,可以直接用osc.type = "sawtooth"或者"square",这些波形本身就包含丰富泛音,再用滤波和ADSR包络调整,也能快速模拟不同乐器
  • 钢琴的释音阶段可以加一点「延音」效果:在stop之后,让增益缓慢下降而不是立刻切断
  • 可以把ADSR包络封装成一个单独的函数,比如createADSRGain(ctx, startTime, endTime, attack, decay, sustainLevel, release),这样不同乐器可以复用参数

怎么样,你可以先试试这些代码,调整泛音的比例、ADSR的时间参数,就能调出更接近你想要的音色啦!有问题再喊我~

火山引擎 最新活动