如何在 Tone.js 中准确监控序列进度与状态

11次阅读

如何在 Tone.js 中准确监控序列进度与状态

tone.js 的 `sequence` 对象虽提供 `progress` 和 `state` 属性,但其行为有特定前提:`progress` 仅在启用循环(`loop: true`)时返回归一化进度值(0–1),否则恒为 0;`state` 可实时反映播放状态(”started” / “stopped”),但需确保在正确时机读取(如回调或事件监听中)。

在 Tone.js 中,Tone.Sequence 并非一个持续运行的“计时器式”对象,而是一个基于调度器(Tone.Transport)触发回调的节拍驱动序列器。它的 progress 和 state 属性的设计逻辑需结合其生命周期理解:

state 是可靠的实时状态指示器
seq.state 返回字符串 “started” 或 “stopped”,反映当前是否已调用 .start() 且未被 .stop() 或 .cancel() 中断。它不依赖循环设置,只要序列处于活跃调度中即为 “started”。但注意:在回调函数内直接 console.log(seq.state) 可能因执行时机过早(如 Transport 尚未真正启动)而偶现不一致,推荐通过事件监听获取更稳健的状态反馈:

seq.on('start', () => console.log('Sequence started')); seq.on('stop', () => console.log('Sequence stopped'));

progress 仅在 loop: true 时有意义
正如 Tone.js v11 文档遗留说明及当前源码行为所证实:seq.progress 是一个归一化浮点数(0.0 → 1.0),表示当前循环内的完成比例。若 loop: false(默认),该值始终为 0 —— 这并非 bug,而是设计使然,因为非循环序列没有“循环内进度”的概念。若需单次播放的进度追踪,应改用 Transport 时间戳计算:

const seq = new Tone.Sequence(   (time, note) => {     synth.triggerAttackRelease(note.note, note.duration, time, 0.1);      // ✅ 安全获取当前 Transport 进度(单次/循环均适用)     const transportPos = Tone.Transport.position;     const bars = Tone.Transport.timeSignature[0];     const totalBeats = bars * Tone.Transport.bpm.value / 60 * Tone.Transport.seconds;     console.log(`Transport position: ${transportPos}`);   },   notesarray,   {     loop: true, // ← 必须设为 true 才能激活 progress     subdivisions: 4   } ).start(0);  // 监听循环完成事件(推荐用于“触发完成逻辑”) seq.on('loop', () => {   console.log('One loop completed');   // ? 此处可安全触发你的完成事件 });  // 若需单次播放后执行操作,使用 .once() + Transport stop 监听: Tone.Transport.once('stop', () => {   console.log('Entire sequence finished (non-looping)'); });

⚠️ 关键注意事项

  • 不要依赖 seq.progress 判断单次序列是否结束;它不适用于 loop: false 场景。
  • Tone.Sequence 的 start(time) 中的 time 是 Transport 时间(如 “0:0:0″),而非绝对秒数,请确保 Tone.Transport.start() 已被调用(或启用 Tone.Transport.autoStart = true)。
  • 如需高精度节拍对齐,所有触发(如 synth.triggerAttackRelease)必须传入回调中的 time 参数,而非 1(你原代码中的硬编码 1 会导致音符全部挤在第 1 拍,失去节奏意义)。

? 总结:监控序列完成的推荐方案是——

  1. 循环序列:监听 ‘loop’ 事件;
  2. 单次序列:监听 Tone.Transport 的 ‘stop’ 事件(需手动调用 Transport.stop() 或设置 seq.loop = false 后让 Transport 自然停止);
  3. 避免在回调中轮询 seq.progress,改用 Transport 时间或事件驱动模型,既符合 Web Audio 的调度范式,也保障时序准确性。

text=ZqhQzanResources