如何在 Tone.js 中准确获取序列播放进度与状态

11次阅读

如何在 Tone.js 中准确获取序列播放进度与状态

在 tone.js 中准确获取序列播放进度与状态,关键在于理解 `sequence.progress` 和 `state` 的设计约束:`progress` 仅在循环模式下有效,且返回 0–1 的归一化值;而 `state` 并非实时播放状态指示器,需配合 transport 或事件机制使用。

Tone.js 的 Sequence 是一个基于时间的回调调度器,它本身不维护独立的播放生命周期状态,而是依赖全局 Tone.Transport 进行时间推进。因此,直接访问 seq.state 或 seq.progress 往往得不到预期结果——正如你在示例中观察到的:progress 始终为 0,state 也未反映真实播放状态。

✅ 正确获取播放状态与进度的方法

1. 判断是否正在播放:监听 Transport 状态

// 启动 Transport(必须!Sequence 依赖它) Tone.Transport.start();  // 监听 Transport 状态变化(更可靠) Tone.Transport.on('start', () => console.log('Transport started → sequence likely playing')); Tone.Transport.on('stop', () => console.log('Transport stopped → sequence paused/stopped'));  // 实时检查(可在回调或定时器中使用) if (Tone.Transport.state === 'started') {   console.log('Sequence is actively progressing'); } else if (Tone.Transport.state === 'stopped') {   console.log('Playback is halted'); }

2. 获取归一化进度:启用 loop 并监听 tick

Sequence.progress 仅在 loop: true 时动态更新(内部基于当前 tick 与总 ticks 计算):

const seq = new Tone.Sequence(   (time, note) => {     synth.triggerAttackRelease(note.note, note.duration, time, 0.1);     // ✅ 此时 progress 才有意义(前提是 loop: true)     console.log(`Progress: ${seq.progress.toFixed(3)}, State: ${seq.state}`);   },   notesarray,   {     loop: true,        // ⚠️ 必须开启循环,progress 才会更新     subdivisions: 1    // 每个 step 对应一个 callback   } ).start(0);

? 注意:即使 loop: true,progress 仍表示当前循环内的相对位置(0=起点,1=循环结束前一刻),并非整个序列的绝对完成度。

3. 触发完成事件:使用 ended 回调(推荐)

若目标是“序列播放完毕后执行操作”,最健壮的方式是利用 Sequence 的内置 ended 事件(v14+ 支持):

seq.on('ended', () => {   console.log('✅ Sequence completed exactly once');   // 执行你的逻辑:重置 UI、加载下一组音符、触发动画等 });  // 若需单次播放(非循环),确保 loop: false(默认),并依赖 ended seq.loop = false; // 显式关闭循环

4. 自定义完成检测(兼容旧版本)

若使用较老版本 Tone.js(

let stepCount = 0; const totalSteps = notesarray.length;  const seq = new Tone.Sequence(   (time, note) => {     synth.triggerAttackRelease(note.note, note.duration, time, 0.1);     stepCount++;      if (stepCount >= totalSteps) {       console.log('? Manual completion detected');       seq.stop(); // 防止重复触发       // your on-completion logic here     }   },   notesarray ).start(0);

⚠️ 关键注意事项

  • seq.state 返回的是内部调度器状态(如 “started”/”stopped”),不等于用户感知的“播放中”;它可能因 Transport 暂停而不同步。
  • seq.progress 在 loop: false 下始终为 0 —— 这是设计使然,不是 bug。官方文档虽未明说,但源码逻辑明确要求循环上下文。
  • 所有 Sequence 实例共享 Tone.Transport,务必调用 Tone.Transport.start() 启动时钟,否则序列不会推进。
  • 使用 seq.stop() 后,需手动调用 Tone.Transport.stop() 避免残留调度。

综上,与其依赖易误解的 progress/state,不如采用事件驱动范式:用 ended 监听完成、用 Transport.state 判断全局播放态、用 loop: true + progress 实现循环内进度可视化。这才是 Tone.js 序列控制的惯用且可靠路径。

text=ZqhQzanResources