Ace编辑器运行Tone.js重复播放致音效叠加失真如何解决
问题根源
每次点击播放按钮触发go()函数时,会重复执行编辑器内的Tone.js代码,导致两个核心问题:
- 重复向
Tone.Transport注册调度回调,旧的回调没有被清除,多个回调同时触发音符播放 - 重复创建Synth等音频节点,多个节点同时输出信号造成音量叠加、声音失真
之前尝试的editor.setValue('')方法是操作Ace编辑器的文本内容,和已执行的JS上下文、Tone.js内部注册的事件完全无关,所以会误删编辑器内的代码,却解决不了声音叠加的问题。
修复方案
不需要修改编辑器内容,只需要在每次运行新代码、点击停止按钮时,重置Tone.js的运行上下文即可,核心操作包括:
- 停止Transport播放
- 清空Transport上所有已注册的调度事件
- 重置播放位置到起始点
- 做短暂静音避免清理时产生爆音
修改后的完整JS逻辑
const editor = ace.edit("editor"); editor.setTheme("ace/theme/monokai"); editor.session.setMode("ace/mode/javascript"); editor.setOptions({fontSize:"14pt"}); // 重置Tone运行上下文,不影响编辑器内的代码内容 const resetToneContext = () => { // 停止播放 Tone.Transport.stop(); // 清除所有通过scheduleRepeat、scheduleOnce注册的调度事件 Tone.Transport.cancel(); // 重置播放进度到开头 Tone.Transport.position = 0; // 短暂静音避免清理瞬间产生爆音 Tone.Master.mute = true; setTimeout(() => { Tone.Master.mute = false; }, 50); } const go = () => { // 每次运行用户代码前,先清理上一次的运行状态 resetToneContext(); const userCode = editor.getSession().getValue(); try { new Function(userCode)(); console.log(userCode); } catch (err) { console.error(err); } Tone.Transport.start(); } const stop = () => { resetToneContext(); }
效果说明
- 编辑器内的用户代码会完整保留,不会被清空
- 每次点击播放只会存在一组调度事件、不会重复叠加声音
- 点击停止按钮会立即终止播放,再次点击播放会从头开始运行
如果需要更严格的上下文隔离,可以进一步改造
new Function的执行作用域,避免用户代码声明的全局变量残留,但对于入门教程场景,上述逻辑已经完全可以解决声音叠加失真的问题。
内容的提问来源于stack exchange,提问作者Jeremy Faucher
相关产品推荐
相关产品推荐

