You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ToneJS:如何实现键盘触发多音高同时播放

嘿,这个问题我熟!要实现多音同时播放,核心问题就是原来的单个Tone.Synth一次只能输出一个音轨的声音,咱们换成ToneJS专门的复音合成器Tone.PolySynth就能轻松搞定,同时还要加个状态记录来避免重复触发。我给你改好代码,再一步步讲清楚逻辑:

核心修改思路

  • 改用PolySynth处理复音:Tone.PolySynth内部会自动管理多个Synth实例,支持同时播放多个不同音符,不用咱们手动创建和维护多个Synth对象。
  • 记录激活的音符状态:新增一个activeNotes对象,用来跟踪当前按住的按键对应的音符,避免同一个按键按住时多次触发keydown事件导致重复发声。
  • 完善按键事件逻辑:在keydown时检查按键是否合法且未激活,再触发音符;keyup时找到对应音符并停止播放。

修改后的完整代码

// 键盘到音高的映射不变
const keyToPitch = { 
  "z":"C3", "s":"C#3", "x":"D3", "d":"D#3", 
  "c":"E3", "v":"F3", "g":"F#3", "b":"G3", 
  "h":"G#3", "n":"A3", "j":"A#3", "m":"B3", ",":"C4" 
};

// 替换单个Synth为PolySynth,指定内部使用的合成器类型和参数
const polySynth = new Tone.PolySynth(Tone.Synth).toMaster();
// 设置振荡器类型,和原来一致
polySynth.set({ oscillator: { type: "sawtooth" } });

// 记录当前激活(按住)的音符,避免重复触发
const activeNotes = {};

function onKeyDown(e) {
  const pitch = keyToPitch[e.key.toLowerCase()];
  // 检查按键是否在映射里,且该音符未被激活
  if (pitch && !activeNotes[pitch]) {
    // 触发音符播放,第二个参数可以设置音量(可选)
    polySynth.triggerAttack(pitch, Tone.now(), 0.5);
    activeNotes[pitch] = true;
  }
}

function onKeyUp(e) {
  const pitch = keyToPitch[e.key.toLowerCase()];
  if (pitch && activeNotes[pitch]) {
    // 停止该音符播放
    polySynth.triggerRelease(pitch, Tone.now());
    delete activeNotes[pitch];
  }
}

// 绑定键盘事件
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);

额外说明

  • 如果想调整复音的最大数量(比如最多同时播放8个音),可以在创建PolySynth时指定:new Tone.PolySynth({ maxPolyphony: 8 }, Tone.Synth)
  • 你也可以给PolySynth设置其他参数,比如滤波器、包络等,和单个Synth的设置方式类似,用polySynth.set()方法即可。

内容的提问来源于stack exchange,提问作者drenl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:35:24