← レッスン一覧に戻る レッスン 23 / 28
上級 api
オーディオ処理
はじめに
オーディオ処理はWasmに最適です — 低レイテンシでリアルタイムの計算が必要です。Rust/WasmはJavaScriptよりも高速にオーディオバッファを処理し、Web Audio APIが再生を担当します。
アーキテクチャ
┌──────────────────┐ ┌──────────────────┐
│ Web Audio API │ buffer │ Rust/Wasm │
│ (AudioContext) │────────▶│ (オーディオ処理) │
│ │◀────────│ │
│ スピーカー │ buffer │ DSP、フィルター、 │
│ │ │ 合成 │
└──────────────────┘ └──────────────────┘セットアップ
[dependencies]
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3"
features = [
"AudioContext",
"AudioBuffer",
"AudioBufferSourceNode",
"GainNode",
"AudioDestinationNode",
]生成したオーディオの再生
import init, { generate_sine } from './pkg/audio_wasm.js';
await init();
const ctx = new AudioContext();
const samples = generate_sine(ctx.sampleRate, 440, 1.0); // A4音、1秒
// Wasmで生成したデータからAudioBufferを作成
const buffer = ctx.createBuffer(1, samples.length, ctx.sampleRate);
buffer.copyToChannel(samples, 0);
// 再生
const source = ctx.createBufferSource();
source.buffer = buffer;
source.connect(ctx.destination);
source.start();AudioWorkletによるリアルタイム処理
低レイテンシのオーディオ処理にはAudioWorkletを使います:
// audio-processor.js(オーディオスレッドで実行)
class WasmProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.wasmReady = false;
this.port.onmessage = async (e) => {
if (e.data.type === 'init') {
// オーディオスレッドでWasmモジュールを読み込む
const { default: init, low_pass_filter } = await import('./pkg/audio_wasm.js');
await init();
this.filter = low_pass_filter;
this.wasmReady = true;
}
};
}
process(inputs, outputs) {
if (!this.wasmReady || !inputs[0].length) return true;
const input = inputs[0][0];
const output = outputs[0][0];
output.set(input);
// Rust/Wasmで処理
this.filter(output, 1000, sampleRate);
return true;
}
}
registerProcessor('wasm-processor', WasmProcessor);よく使うオーディオDSP関数
ゲイン(音量)
#[wasm_bindgen]
pub fn apply_gain(samples: &mut [f32], gain: f32) {
for sample in samples.iter_mut() {
*sample *= gain;
*sample = sample.clamp(-1.0, 1.0);
}
}ディストーション
#[wasm_bindgen]
pub fn distortion(samples: &mut [f32], amount: f32) {
for sample in samples.iter_mut() {
let x = *sample * amount;
*sample = x / (1.0 + x.abs());
}
}リバーブ(シンプルなディレイ)
#[wasm_bindgen]
pub fn delay(samples: &mut [f32], delay_samples: usize, feedback: f32) {
let len = samples.len();
for i in delay_samples..len {
samples[i] += samples[i - delay_samples] * feedback;
samples[i] = samples[i].clamp(-1.0, 1.0);
}
}パフォーマンス: なぜWasmでオーディオ処理?
| 処理 | JavaScript | Rust/Wasm |
|---|---|---|
| FFT(1024サンプル) | ~0.5ms | ~0.08ms |
| ローパスフィルター(44100サンプル) | ~2ms | ~0.3ms |
| 波形生成(1秒) | ~5ms | ~0.8ms |
オーディオ処理はバッファあたり約5ms以内に完了する必要があります(44.1kHz、256サンプルブロック時)。JavaScriptはこの期限に間に合わないことがあり、オーディオのグリッチが発生します。Rust/Wasmなら安定して間に合います。
試してみよう
スターターコードはサイン波を生成し、ローパスフィルターを適用し、音量を測定します。実際のプロジェクトでは、これらの関数はマイクや音声ファイルからのライブオーディオバッファを処理します。
試してみる
チャプタークイズ
すべての問題に正解してレッスンを完了しましょう