调用Tone.js的triggerAttack/triggerRelease时程序崩溃问题求助
p5.js + Tone.js 合成器间歇性崩溃问题修复
问题描述
用p5.js搭配Tone.js开发合成器时,以特定速度重复点击鼠标会导致sketch间歇性崩溃。精简代码后,推测问题和Tone.js FM合成器的portamento(滑音)、triggerRelease的触发速度有关,报错显示音频参数传入了非有限值。
复现代码
let bass let nfrequency function setup () { createCanvas(windowWidth, windowHeight); strokeWeight(5) createSynth() } function draw() { background(0); nfrequency = 220 bass.setNote(nfrequency/4); } function mousePressed () { bass.triggerAttack(); } function mouseReleased () { bass.triggerRelease(); } function createSynth() { bass = new Tone.FMSynth({ oscillator: {type: "sine"}, portamento: 0.05 }); bass.connect(Tone.Master); }
报错信息
TypeError: Failed to execute 'exponentialRampToValueAtTime' on 'AudioParam': The provided float value is non-finite. at undefined:7:37363 TypeError: Failed to execute 'setValueAtTime' on 'AudioParam': The provided float value is non-finite. at undefined:7:39588
问题原因与修复方案
根本原因
- 高频重复调用setNote:draw函数以60fps频率反复执行
bass.setNote(nfrequency/4),而portamento激活时,Tone.js会持续生成音频参数的平滑过渡动画。快速点击触发attack/release时,频繁的setNote操作会和滑音的参数过渡逻辑冲突,计算出NaN或Infinity这类非有限值,触发浏览器音频API报错。 - 触发时序冲突:短时间内重复触发attack和release,会让合成器状态混乱,叠加滑音的参数计算后,更容易出现非法数值。
修复步骤
移除draw中的setNote调用:不需要每帧重复设置相同音符,把setNote移到合成器初始化阶段:
function createSynth() { bass = new Tone.FMSynth({ oscillator: {type: "sine"}, portamento: 0.05 }); bass.setNote(220/4); // 初始化时设置一次音符 bass.connect(Tone.Master); }同时删除draw函数内的
bass.setNote(nfrequency/4);代码。添加状态锁避免重复触发:防止快速点击时重复触发attack和release,避免合成器状态混乱:
let isPlaying = false; function mousePressed () { if (!isPlaying) { bass.triggerAttack(); isPlaying = true; } } function mouseReleased () { if (isPlaying) { bass.triggerRelease(); isPlaying = false; } }可选优化:如果需要动态修改音符,避免在合成器发声时频繁调用setNote,可在调用前判断合成器状态,确保仅在静音状态下修改,或等待滑音完成后再执行修改操作。
内容的提问来源于stack exchange,提问作者mikey
相关产品推荐
相关产品推荐

