简而言之,按下按钮后,我想使用
PolySynth
和 Sequence
弹奏一些音符。如果用户反复按下按钮,我希望停止正在播放的内容,然后重新开始。
问题:无论我尝试什么,我都无法完全取消/静音之前播放的音符,以防序列再次启动(再次单击按钮)。这很可能是因为包络的衰减/维持。
我的合成器:
import { PolySynth } from 'tone'
const synth = new PolySynth(Synth, {
oscillator: {
type: 'sine4',
volume: -6,
},
envelope: {
attack: 0.01,
decay: 0.5,
sustain: 0.1,
release: 1,
},
}).toDestination()
synth.maxPolyphony = 4 // max notes playing at a time, not sure if necessary
我的顺序:
import { Sequence } from 'tone'
// Play the 2 notes individually then play them together
const notes = [
{ note: 'C4', duration: '8n' },
{ note: 'G4', duration: '8n' },
{ note: ['C4', 'G4'], duration: '4n' }
]
// The sequence that should play the notes after one another
const sequence = new Sequence({
subdivision: '8n',
loop: false,
events: notes,
callback: (time, note) => synth.triggerAttackRelease(note.note, note.duration, time),
})
我的玩法是,这是一个事件处理程序:
import { start, Transport } from 'tone'
// Event handler simply attached to a button's onClick
function onButtonClicked() {
// Call whatever this start is, doc says it can only happen in an event handler
start()
// Try everything to kill current sound
Transport.cancel()
Transport.stop()
// Start it again
Transport.start()
sequence.start()
}
如何在开始播放之前完全消除所有声音(如果有的话)?
仔细思考一下,如果我理解正确的话,这实际上是有意的行为。 您正在 Synth(基本上是一个 AudioWorkletNode)上触发一个音符。因此,一旦音符触发合成器,该音符就会消失。阻止该音符播放的唯一方法是将合成器本身静音。
在您所说的评论中,您可能在概念上遗漏了一些东西,我认为您的方向是正确的。
让我们考虑一下如何用 MIDI 生成声音。
那么当你停止运输或序列本身时,会发生什么? 如果 MIDI 音符已触发包络,则包络将接收 MIDI 结束触发器并触发释放包络。
因此,您的合成器总会有拖尾声音,因为 MIDI 音符并不能确定您的合成器的起点和终点,而是触发您的包络的部分内容。所以实际上你的合成器创造了声音,它既不依赖于传输,也不可能依赖于传输。
希望这个解释对您有所帮助。如果我误解了你,我很乐意纠正。
音符会继续播放,因为走带在音符触发之后、释放之前停止。因此,解决方案是在按下停止按钮时触发所有音符的释放。我在使用 tambien/piano(基于 Tonejs)时遇到了类似的问题。
Tone.Transport.toggle()
if (Tone.Transport.state === 'stopped') {
for (let i=9; i<97; i++) {
piano.keyUp({midi: i}, '+0')
}
}
该解决方案可能很难在较长的序列中实现,但在您的情况下它应该可行。我挑战过类似的问题并且成功了。
polySynth 的问题是你只能添加演奏的音符。但使用普通合成器,可以通过用空音符覆盖播放的音符来“消除声音”。
// It would create continuously playing note.
synth = synth || new Tone.Synth().toMaster();
synth.triggerAttackRelease(noteToPlay);
// It would mute the sound.
synth = synth || new Tone.Synth().toMaster();
synth.triggerAttackRelease();
可以同时播放许多单个合成器,因此您可以手动创建复音合成器。
synth1 = synth1 || new Tone.Synth().toMaster();
synth1.triggerAttackRelease(noteToPlay);
synth2 = synth2 || new Tone.Synth().toMaster();
synth2.triggerAttackRelease(noteToPlay2);
synth3 = synth3 || new Tone.Synth().toMaster();
synth3.triggerAttackRelease(noteToPlay3);
构建序列会更复杂,但是您可以通过在播放函数的开头添加“soundKiller”来使播放序列静音。重要的是:不要声明序列,然后通过调用“Transport”来播放它,而只需在事件处理程序中播放音符(因为 - 正如另一个人已经写的 - 调用它后无法停止传输)。
只需调用 PolySynth 对象上的releaseAll()方法即可:
polySynth.releaseAll()
这将停止播放所有当前触发的音符。
使用Tone.Transport.pause()
如果您有“暂停-播放”类别的播放/暂停按钮,请参阅下面的示例:
$( '.pause-play' ).on('click', function(e) {
if (Tone.Transport.state === "paused" ) {
Tone.Transport.start("+0.1") })
else {
Tone.Transport.pause() }
}
)