节奏变更
介绍节奏变更和缓冲状态监控模式。
使用模块: play, stretcher
节奏变更演示
基于 WSOLA 的保持音高时间拉伸功能演示。使用节奏滑块实时调整播放速度。
音源
使用较长的音源来体验保持音高的时间拉伸。
或
波形
0:00.0 0:00.0
节奏变更
拉伸引擎 缓冲中...
分块缓冲演示
用于观察 WSOLA 拉伸引擎分块缓冲机制的演示。实时监控分块处理状态和事件。
音源
使用较长的音源来观察分块行为。
或
波形
0:00.0 0:00.0
分块缓冲
分块状态
待处理
已排队
转换中
就绪
已释放
播放位置
拉伸引擎 缓冲中...
事件日志
代码示例
1. 保持音高的节奏变更
由于 preservePitch 默认为 true,因此在正常的 play() 调用中会自动保持音高。如需常规速度变更(音高也会改变),请明确设置 preservePitch: false。
import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();await waa.ensureRunning();const buffer = await waa.load("/audio/track.mp3");
// Change tempo while preserving pitch (default behavior)const playback = waa.play(buffer, { playbackRate: 0.8 });
// Change tempo during playbackplayback.setPlaybackRate(1.2); // 1.2x speed, pitch maintained
// To change pitch along with tempo, set preservePitch: falseconst playback2 = waa.play(buffer, { playbackRate: 1.5, preservePitch: false,});WSOLA 算法允许在不改变音高的情况下改变节奏。设置 preservePitch: true(默认)将启用基于 WSOLA 的时间拉伸。
函数 API 版
import { createContext, ensureRunning } from "waa-play/context";import { loadBuffer } from "waa-play/buffer";import { play } from "waa-play/play";
const ctx = createContext();await ensureRunning(ctx);const buffer = await loadBuffer(ctx, "/audio/track.mp3");
// Change tempo while preserving pitch (default behavior)const playback = play(ctx, buffer, { playbackRate: 0.8 });
// Change tempo during playbackplayback.setPlaybackRate(1.2);
// To change pitch along with tempo, set preservePitch: falseconst playback2 = play(ctx, buffer, { playbackRate: 1.5, preservePitch: false,});2. 监控缓冲状态
由于 WSOLA 处理是异步的,因此在改变节奏时可能会发生缓冲。
const playback = waa.play(buffer, { playbackRate: 0.8 });
// Monitor buffering startplayback.on("buffering", ({ reason }) => { console.log(`Buffering... (reason: ${reason})`); // Show loading UI});
// Monitor buffering completionplayback.on("buffered", ({ stallDuration }) => { console.log(`Buffering complete (${stallDuration.toFixed(0)}ms)`); // Hide loading UI});
// Check stretcher state via snapshotconst snapshot = waa.getSnapshot(playback);if (snapshot.stretcher) { console.log(`Tempo: ${snapshot.stretcher.tempo}`); console.log(`Buffer health: ${snapshot.stretcher.bufferHealth}`); console.log(`Converting: ${snapshot.stretcher.converting}`); console.log(`Conversion progress: ${(snapshot.stretcher.conversionProgress * 100).toFixed(0)}%`);}buffering 事件中的 reason 为 "initial" | "seek" | "tempo-change" | "underrun" 之一。您可以通过 getSnapshot() 中的 stretcher 字段获取详细状态信息。
函数 API 版
import { getSnapshot } from "waa-play/adapters";
const playback = play(ctx, buffer, { playbackRate: 0.8 });
playback.on("buffering", ({ reason }) => { console.log(`Buffering... (reason: ${reason})`);});
playback.on("buffered", ({ stallDuration }) => { console.log(`Buffering complete (${stallDuration.toFixed(0)}ms)`);});
const snapshot = getSnapshot(playback);if (snapshot.stretcher) { console.log(`Tempo: ${snapshot.stretcher.tempo}`); console.log(`Buffer health: ${snapshot.stretcher.bufferHealth}`); console.log(`Converting: ${snapshot.stretcher.converting}`); console.log(`Conversion progress: ${(snapshot.stretcher.conversionProgress * 100).toFixed(0)}%`);}