
在 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 序列控制的惯用且可靠路径。










