Skip to content

リアルタイムボイスチェンジャー

RealtimeVoiceChanger は、libsonare のプリセット駆動のライブ音声チェーンです。マイク入力、リアルタイムモニタリング、または音声を繰り返しブロックで処理し、呼び出し間で DSP 状態を保ちたい場面で使います。

半音値とフォルマント係数で 1 回だけオフライン変換するなら voiceChange(...) を使います。ライブ処理やチャンク処理のプリセットチェーンが必要なら、このページを読んでください。

ブロック・サンプルレート・DSP 状態

リアルタイム音声は、小さなブロック(サンプルのまとまり)を次々に処理します。ブラウザでは AudioWorklet が 128 サンプルの固定レンダークォンタムを 1 回ずつ渡します。サンプルレート(例: 48000)は 1 秒が何サンプルでできているかです。DSP 状態は、エフェクトがブロック間で持ち越すメモリ(リバーブの残響、コンプレッサーの現在のゲインなど)です。だからこそボイスチェンジャーは 1 回限りの関数ではなく、再利用するオブジェクトになっています。

なぜ関数ではなくクラスなのか

ライブ音声処理には「前のブロックから続く状態」があります。ゲート、コンプレッサー、リバーブ、滑らかなピッチ/フォルマント変化は、前後のブロックをまたいで動く必要があります。RealtimeVoiceChanger はその状態を保持するため、ブロックごとに作り直すと音が不自然になり、準備コストも増えます。

このページで身につくこと

このページを読むと、次のことを判断・実装できるようになります。

  • 単純なオフライン voiceChange(...) ではなく RealtimeVoiceChanger を選ぶべき場面を判断できる。
  • ブロック処理のためにクラスを準備し、WASM ハンドルを正しく解放できる。
  • 組み込みプリセット JSON を確認し、ユーザー作成プリセットを受け入れる前に検証できる。
  • ブラウザ、Python、Node ネイティブ、CLI のどの入口を使うべきか選べる。

いつ使うか

作りたいもの使う入口入力出力
ブラウザでマイク音声をライブ加工するRealtimeVoiceChangerAudioWorklet などから届く短い Float32Array ブロック同じ長さの加工済みブロック
ブラウザで 1 つの録音クリップを半音値とフォルマント値だけで変換するvoiceChange(...)デコード済みモノラル Float32Array 全体加工済み Float32Array
Python でファイルや配列全体をプリセットチェーンに通すvoice_change_realtime(...)モノラル配列と sample_rate加工済み配列
Node ネイティブでサーバー/デスクトップ側の一括処理を行うvoiceChangeRealtime(...)モノラル配列とサンプルレート加工済み配列
ターミナルで WAV/MP3 をプリセット加工して書き出すsonare voice-change --preset ...音声ファイル書き出しファイル
プリセット設定を UI で表示・保存・検証するrealtimeVoiceChangerPresetJson(...) と検証 APIプリセット ID または JSON設定 JSON と検証結果

実装の全体像

ブラウザのライブ処理では、最初に init()prepare(sampleRate, maxBlockSize, channels) を済ませ、その後は届いた音声ブロックごとに processMono(...) または processMonoInto(...) を呼びます。RealtimeVoiceChanger は内部状態を持つため、ブロックごとに作り直さず、ストリームが続く間は同じインスタンスを再利用します。

実装で決めること目安
sampleRateAudioContext.sampleRate や入力ファイルの実サンプルレートを使います。推測で 44100 / 48000 を固定しないでください。
maxBlockSizeprocessMono(...) に渡す最大ブロック長です。AudioWorklet の標準レンダークォンタムなら 128 が出発点です。
channelsこのページの例はモノラルなので 1 です。インターリーブ入力なら processInterleaved(...) を使います。
出力バッファ単純な実装は processMono(...)、AudioWorklet のホットパスでは事前確保した出力へ processMonoInto(...) または WASM ヒープバッファ経路を使います。
後始末Vue / React コンポーネントの unmount、Worklet 停止、録音停止時に delete() を 1 回だけ呼びます。

「インターリーブ」とは、複数チャンネルを 1 つの配列に交互に並べたもの(L, R, L, R…)で、「モノラル」は 1 チャンネルです。前者には processInterleaved(...)、後者には processMono(...) を使います。

ライブ処理と一括レンダーを混同しない

RealtimeVoiceChanger は「短いブロックを順番に処理し、ゲートやコンプの状態を保つ」ためのクラスです。Python / CLI の例は同じプリセットチェーンをファイル全体へ適用する入口で、ブラウザの AudioWorklet コールバック内でそのまま使うコードではありません。

信号チェーン

リアルタイムチェーンは単なるピッチシフターではありません。組み込みプリセットは次の段を組み合わせます。

PARAM SWEEP · PITCH SHIFTIDLE
ピッチシフト — 倍音の櫛ごと動かす

ピッチシフトは長さを変えずに音程を移します。セミトーンをドラッグするとスペクトルが動き、すべての倍音がまとめてスケールし、基音マーカーが新しい音程を追います。このシフトはフォルマント保存ではないため、フォルマントの山も一緒に動きます — いわゆる「チップマンク」効果です。再生で確認できます。

音程
0 st

上のデモはリチューン段(ピッチシフト)だけを取り出して、その効果を単体で聞けるようにしたものです。実際のプリセットは、下の表にあるフォルマント・EQ・ダイナミクス・空間処理の各段をこれに重ねます。

役割
リチューンプリセットが指定する半音値だけピッチを上下させ(例: 声を高く/低く)、プリセットが有効にしている場合は持続音を最も近い音階音へ寄せる。チェーンの中のピッチシフト段
フォルマント音高とは独立に、声の大きさやキャラクターの印象を変える
EQダイナミクスや空間処理の前に音色を整える
ゲート小さな部屋ノイズやマイクノイズを抑える
コンプレッサーブロックをまたいでレベルを安定させる
ディエッサー刺さりやすい歯擦音を抑える
リバーブ空間を足す、または整える
リミッターチェーン末尾でピークを捕まえる

ボイスチェーンの用語をまとめて把握

  • リチューン — 歌った音そのものを高く/低くピッチで上下させます。音を動かさずに声のキャラクターを変えるフォルマントとは別物です。
  • フォルマント — 声を大きく/小さく、男性的/女性的に聞かせる共鳴です。これをずらすと、音程を変えずに声のキャラクターが変わります。
  • ゲート — 信号がしきい値を下回ると無音にし、フレーズの合間の小さなマイク/部屋ノイズを取り除きます。
  • コンプレッサー — 大きい部分と小さい部分を自動でならし、レベルを安定させます。
  • ディエッサー — 耳に刺さる「s」「sh」の音(歯擦音)を抑えます。
  • リミッター — チェーン末尾でピークがクリップしないよう止める安全装置です。

標準プリセット ID には neutral-monitorbright-idolsoft-whisperdeep-narratorrobot-mascotdark-villain があります。これらは出発点であり、ジャンルや話者属性のラベルとして固定的に扱うものではありません。

ブラウザ / WASM

typescript
import {
  init,
  RealtimeVoiceChanger,
  realtimeVoiceChangerPresetNames,
} from '@libraz/libsonare';

await init();

const changer = new RealtimeVoiceChanger('bright-idol');
changer.prepare(48000, 128, 1);

try {
  const out = changer.processMono(inputBlock);
  changer.setConfig('soft-whisper');
  console.log(realtimeVoiceChangerPresetNames(), changer.latencySamples(), out);
} finally {
  changer.delete();
}

AudioWorklet 形式のループでは、ブラウザ / WASM で説明している WASM ヒープ上のリアルタイムバッファを使います。レンダークォンタムごとに新しい出力配列を確保せずに済みます。

プリセット JSON

プリセット JSON は、音声チェーン設定を確認・保存・検証したいときに使います。

typescript
import {
  realtimeVoiceChangerPresetJson,
  validateRealtimeVoiceChangerPresetJson,
} from '@libraz/libsonare';

const json = realtimeVoiceChangerPresetJson('bright-idol');
const validation = validateRealtimeVoiceChangerPresetJson(json);

if (!validation.ok) {
  throw new Error(validation.error);
}

現在の組み込みプリセット JSON はスキーマバージョン 1 を使います。ネイティブ POD 設定の ABI は別物です。FFI やネイティブ境界をまたぐ場合は voiceChangerAbiVersion() で確認してください。

正規のプリセット ID や解決済みのフラットなネイティブ設定だけが必要なら、JSON を往復せず voiceCharacterPresetId(...)realtimeVoiceChangerPresetConfig(...) を使います。Python では同じネイティブ設定取得経路を realtime_voice_changer_preset_config(...) として公開しています。

Python

python
import libsonare as sonare

print(sonare.voice_character_preset_id(1))
preset_config = sonare.realtime_voice_changer_preset_config("bright-idol")

with sonare.RealtimeVoiceChanger(48000, preset="bright-idol", max_block_size=128) as changer:
    out = changer.process_mono(input_block)
    changer.set_config("soft-whisper")
    print(sonare.realtime_voice_changer_preset_names(), preset_config, changer.latency_samples())

processed = sonare.voice_change_realtime(vocal, sample_rate=48000, preset="soft-whisper")

コンテキストマネージャーを使うか、close() を呼んでネイティブハンドルを解放してください。

Node ネイティブ

typescript
import {
  RealtimeVoiceChanger,
  realtimeVoiceChangerPresetNames,
  voiceChangeRealtime,
} from '@libraz/libsonare-native';

const changer = new RealtimeVoiceChanger({
  sampleRate: 48000,
  maxBlockSize: 128,
  channels: 1,
  preset: 'bright-idol',
});

const blockOut = changer.processMono(inputBlock);
const rendered = voiceChangeRealtime(vocal, 48000, 'soft-whisper');
console.log(realtimeVoiceChangerPresetNames(), blockOut, rendered);

ネイティブのファイルデコード、サーバー側バッチ処理、デスクトップ統合が必要なら Node ネイティブを使います。マイクと UI がブラウザ内にある場合はブラウザ / WASM を使います。

CLI

sonare voice-change には 2 つのモードがあります。

モードオプション
単純なピッチ/フォルマント変換--pitch-semitones--formant-factor
リアルタイムプリセットチェーンでのレンダリング--preset--preset-json--preset-pack--set PATH=VALUE
bash
sonare voice-presets --json
sonare voice-change vocal.wav --preset soft-whisper -o rendered.wav

リアルタイムプリセット系のオプションを渡した場合、コマンドはプリセットチェーンを使い、単純なピッチ/フォルマント指定は参照しません。詳細なコマンド表は CLI リファレンス を参照してください。

実用上の注意

リアルタイム音声処理は状態を持ちます。同じ changer をブロック間で再利用し、ブロックサイズは prepare(...) した最大値以内に保ち、コンポーネントやストリーム停止時にはハンドルを解放してください。

大きなピッチ、フォルマント、空間処理の変更はサウンドデザインには有効ですが、自然さは下がります。自然なモニタリングではプリセット編集を控えめにし、latencySamples() でレイテンシも確認してください。

ここでいう「レイテンシ」とは

レイテンシは、音が入ってから加工後の音が出てくるまでの遅れで、チェーンが行う解析によって生じます。latencySamples() はこれをサンプル数で報告するので、サンプルレートで割れば秒になります。ライブモニタリングでは小さいほどよく、ピッチ/フォルマントの変化が大きいほどレイテンシは増えがちです。

関連ページ