コンテンツにスキップ

テンポ変更

テンポの変更やバッファリング状態の監視パターンを紹介します。

使用モジュール: play, stretcher

テンポ変更デモ

WSOLA ベースの pitch 保持 time-stretch 機能のデモです。テンポスライダーでリアルタイムに再生速度を変更できます。

Sound Source

Pitch 保持の time-stretch を体験するには、長めの音源を使用してください。

または

チャンクバッファリングデモ

WSOLA stretcher エンジンのチャンクバッファリングの仕組みを観察するデモです。各チャンクの処理状態やイベントログをリアルタイムに確認できます。

Sound Source

チャンク分割の仕組みを確認するには、長めの音源を使用してください。

または

コード例

1. Pitch 保存テンポ変更

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 playback
playback.setPlaybackRate(1.2); // 1.2x speed, pitch maintained
// To change pitch along with tempo, set preservePitch: false
const playback2 = waa.play(buffer, {
playbackRate: 1.5,
preservePitch: false,
});

WSOLA アルゴリズムにより、ピッチを変えずにテンポを変更できます。preservePitch: true(デフォルト)で WSOLA ベースの time-stretch が有効になります。

関数 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 playback
playback.setPlaybackRate(1.2);
// To change pitch along with tempo, set preservePitch: false
const playback2 = play(ctx, buffer, {
playbackRate: 1.5,
preservePitch: false,
});

2. Buffering 状態の監視

WSOLA 処理は非同期で行われるため、テンポ変更時に buffering が発生する可能性があります。

const playback = waa.play(buffer, { playbackRate: 0.8 });
// Monitor buffering start
playback.on("buffering", ({ reason }) => {
console.log(`Buffering... (reason: ${reason})`);
// Show loading UI
});
// Monitor buffering completion
playback.on("buffered", ({ stallDuration }) => {
console.log(`Buffering complete (${stallDuration.toFixed(0)}ms)`);
// Hide loading UI
});
// Check stretcher state via snapshot
const 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)}%`);
}

関連 API