Tone.js 完全停止所有播放声音
Tone.js completely stop all playing sounds
简而言之,我想使用 PolySynth
和 Sequence
来演奏几个音符。如果用户反复按下按钮,我希望停止正在播放的内容,然后重新开始。这很可能是因为信封的 decay/sustain.
问题:无论我尝试什么,我都无法完全cancel/silence之前演奏的音符,以防序列再次开始(再次单击按钮)。
我的合成器:
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()
}
我怎样才能在开始播放之前完全消除所有声音(如果有的话)?
简答
仔细考虑了一下,如果我理解正确的话,这实际上是有意为之的行为。
您在合成器(基本上是一个 AudioWorkletNode)上触发一个音符。所以一旦一个音符触发了合成器,这个音符就消失了。停止播放该音符的唯一方法是将合成器本身静音。
长答案
在你的评论中,你可能在概念上遗漏了一些东西,我认为你在正确的轨道上。
让我们思考一下如何用 MIDI 生成声音。
- 您正在将合成器(它采用 MIDI 音符并生成声音)连接到输出
- 您正在传输上安排一些 MIDI 音符
- 你开始运输
- 一旦传输达到音符的预定时间,该 MIDI 值就会发送到合成器。
- 由于 Synth 基本上是一个带有包络生成器的 AudioWorkletNode,因此 Synth 获取该 MIDI 音符并触发内部声音生成(通过包络)。因此,特定时间点的 MIDI 音符会触发特定长度的声音生成(这将是 ADS 部分)。即使在您的示例中 MIDI 音符持续时间仅为 1 毫秒,声音生成也会持续至少 1.001 秒(释放加上 1 毫秒 MIDI 持续时间)。让我们再分解一下:
- MIDI 音符在想象的传输时间线上有一个起点和一个终点。
- 开始触发包络的 ADS 部分。
- End 触发包络的 R 部分。
- 一旦您的 MIDI 音符触发包络,就会生成声音。
所以当你停止你的传输或序列本身时,那会做什么?
如果 MIDI 音符已经触发包络,则包络将接收 MIDI 结束触发器并触发释放包络。
所以你的合成器总是会有拖尾的声音,因为 MIDI 音符并不能决定你合成器的起点和终点,而是触发你的包络的一部分。所以实际上你的 Synth 创造了声音,它既不依赖于传输,也不可能。
希望以上解释对您有所帮助。如果我误解了你,我很乐意纠正。
使用Tone.Transport.pause()
如果您有一个 class 为 'pause-play' 的 play/pause 按钮,请参见下面的示例:
$( '.pause-play' ).on('click', function(e) {
if (Tone.Transport.state === "paused" ) {
Tone.Transport.start("+0.1") })
else {
Tone.Transport.pause() }
}
)
音符一直在播放,因为传输在音符被触发后和释放前停止。因此,一种解决方案是在您按下停止按钮时触发所有音符的释放。我在使用 tambien/piano(基于 Tonejs)时遇到了类似的问题。
Tone.Transport.toggle()
if (Tone.Transport.state === 'stopped') {
for (let i=9; i<97; i++) {
piano.keyUp({midi: i}, '+0')
}
}
简而言之,我想使用 PolySynth
和 Sequence
来演奏几个音符。如果用户反复按下按钮,我希望停止正在播放的内容,然后重新开始。这很可能是因为信封的 decay/sustain.
问题:无论我尝试什么,我都无法完全cancel/silence之前演奏的音符,以防序列再次开始(再次单击按钮)。
我的合成器:
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()
}
我怎样才能在开始播放之前完全消除所有声音(如果有的话)?
简答
仔细考虑了一下,如果我理解正确的话,这实际上是有意为之的行为。 您在合成器(基本上是一个 AudioWorkletNode)上触发一个音符。所以一旦一个音符触发了合成器,这个音符就消失了。停止播放该音符的唯一方法是将合成器本身静音。
长答案
在你的评论中,你可能在概念上遗漏了一些东西,我认为你在正确的轨道上。
让我们思考一下如何用 MIDI 生成声音。
- 您正在将合成器(它采用 MIDI 音符并生成声音)连接到输出
- 您正在传输上安排一些 MIDI 音符
- 你开始运输
- 一旦传输达到音符的预定时间,该 MIDI 值就会发送到合成器。
- 由于 Synth 基本上是一个带有包络生成器的 AudioWorkletNode,因此 Synth 获取该 MIDI 音符并触发内部声音生成(通过包络)。因此,特定时间点的 MIDI 音符会触发特定长度的声音生成(这将是 ADS 部分)。即使在您的示例中 MIDI 音符持续时间仅为 1 毫秒,声音生成也会持续至少 1.001 秒(释放加上 1 毫秒 MIDI 持续时间)。让我们再分解一下:
- MIDI 音符在想象的传输时间线上有一个起点和一个终点。
- 开始触发包络的 ADS 部分。
- End 触发包络的 R 部分。
- 一旦您的 MIDI 音符触发包络,就会生成声音。
所以当你停止你的传输或序列本身时,那会做什么? 如果 MIDI 音符已经触发包络,则包络将接收 MIDI 结束触发器并触发释放包络。
所以你的合成器总是会有拖尾的声音,因为 MIDI 音符并不能决定你合成器的起点和终点,而是触发你的包络的一部分。所以实际上你的 Synth 创造了声音,它既不依赖于传输,也不可能。
希望以上解释对您有所帮助。如果我误解了你,我很乐意纠正。
使用Tone.Transport.pause()
如果您有一个 class 为 'pause-play' 的 play/pause 按钮,请参见下面的示例:
$( '.pause-play' ).on('click', function(e) {
if (Tone.Transport.state === "paused" ) {
Tone.Transport.start("+0.1") })
else {
Tone.Transport.pause() }
}
)
音符一直在播放,因为传输在音符被触发后和释放前停止。因此,一种解决方案是在您按下停止按钮时触发所有音符的释放。我在使用 tambien/piano(基于 Tonejs)时遇到了类似的问题。
Tone.Transport.toggle()
if (Tone.Transport.state === 'stopped') {
for (let i=9; i<97; i++) {
piano.keyUp({midi: i}, '+0')
}
}