Tone.js完全停止所有播放声音

问题描述

简而言之,我想使用polySynthSequence来弹奏一些音符。如果用户反复按下按钮,我希望停止正在播放的内容,然后重新开始。这很可能是由于包络的衰减/维持。

问题:无论我尝试什么,如果重新开始音序(再次单击按钮),我都无法完全取消/静音先前演奏的音符。

我的合成器:

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',{ 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产生声音。

  1. 您正在将Synth(需要MIDI音符并生成声音)连接到输出
  2. 您要在传输器上安排一些MIDI音符
  3. 您开始运输
  4. 运输工具到达音符的预定时间后,该MIDI值将发送到Synth。
  5. 由于Synth本质上是带有信封生成器的AudioWorkletNode,因此Synth会获取MIDI音符并触发内部声音生成(通过包络)。因此,在特定时间点的MIDI音符会触发特定长度的声音生成(这将是ADS的一部分)。即使在您的示例中MIDI音符的持续时间只有1ms长,声音的产生也将持续至少1.001秒(释放加上1毫秒的MIDI持续时间)。让我们进一步分解一下:
    • MIDI音符在虚拟传输时间轴上有一个起点和终点。
    • 开始会触发信封的ADS部分。
    • End触发信封的R部分。
    • 一旦您的MIDI音符触发了包络,就会产生声音。

那么当您停止运输或序列本身时,该怎么办? 如果MIDI音符已经触发了信封,信封将收到MIDI结束触发器并触发释放包络。

因此,您的Synth总是会有尾声,因为MIDI音符不会确定您的Synth的起点和终点,但会触发您的信封的一部分。因此,实际上您的Synth创造了声音,并且与运输无关,也没有。

希望这种解释对您有所帮助。如果我误解了你,我很乐意纠正。

,

使用 Tone.Transport.pause()

如果您有一个“暂停播放”类的播放/暂停按钮,请参见下面的示例:

$( '.pause-play' ).on('click',function(e) {

    if (Tone.Transport.state === "paused" ) {  
          Tone.Transport.start("+0.1") })
    else {
          Tone.Transport.pause()  }
    }
)