Skip to content

ミキシングエンジン ​

ミキシングは、別々の複数トラック(ボーカル、ベース、ドラム、音楽ベッドなど)の音量バランスを取り、1 つのステレオ出力へまとめる工程です。

libsonare には、マスタリングプロセッサと同じ C++ DSP コアの上に、リアルタイム処理を意識したミキシング/ルーティングエンジンが備わっています。DAW ホストを組み込まずに、自分のアプリの中に小さなミキサーを作れます。

ストリップ、バス、センド、フェーダー、パン、オートメーション という言葉に馴染みがなければ、先に ミキシングの基礎 を読んでください。本ページはこれらの意味を理解している前提で、libsonare がそれをどう実装しているかと何を呼べば目的を達成できるかに集中します。

パイプライン内でのミキシングの位置

工程役割
解析トラックが「何か」を調べる
編集1 トラックのタイミングやピッチを直す
ミキシング複数トラックをステレオバスへまとめる
マスタリング仕上がったステレオミックスを配信向けに磨く

ミキシングは「ステム(セッションから書き出した楽器ごとのトラックやサブミックス)のフォルダ」を「1 曲」に変える工程です。通常はミックスしてから、その結果をマスタリングします。

最初に押さえる 3 語

ストリップは 1 トラック分の処理列、バスは複数トラックをまとめる行き先、センドは元の音を残したまま別のバスへ分岐する送り量です。この 3 つが分かると、フェーダー、パン、リバーブ送り、メーターの位置関係を追いやすくなります。

インサートとセンド ​

これらはストリップがプロセッサを使う 2 つの方法で、それぞれ用途が異なります。

インサートセンド
信号経路直列(全信号が通過する)並列(コピーをバスへ送る)
典型用途トラック自身へのコンプ・EQ・ディエッサー複数トラックが共有するリバーブ/ディレイ
ウェット/ドライプロセッサ自身の mix で制御ドライはストリップに残り、送ったコピーだけ処理される
置き場所プリフェーダーかポストフェーダーのスロットプリ/ポストフェーダーのどちらから取るかを選べる

1 本のボーカルだけにかけたいリバーブはインサートで構いません。ボーカル・スネア・ギターで共有したいリバーブは、AUX バスへのセンドとし、そのリターンにリバーブを 1 つ置きます。リバーブは 1 インスタンス、ソースは複数で、空間は一貫します。

デシベル、ゲイン、合算

フェーダー、トリム、センドは dB で指定します。g dB のゲインはサンプル値を 10^(g/20) 倍します。

dBリニアゲイン意味
0 dB1.0変化なし
−3 dB0.707パワーが半分
−6 dB0.5振幅が半分
−12 dB0.25振幅が 4 分の 1
−∞ dB0無音

同じレベルの信号を 2 つ足したときの結果は、2 つがどれだけ似ているかで変わります。まったく同じ(コヒーレントな)2 つの信号は振幅が足されるので +6 dB、無関係な 2 つの信号はパワーだけが足されるので約 +3 dB になります。この差が、後述するパンローの違いにつながります。dBFS、ヘッドルーム、レイテンシについては オーディオの基礎 を参照してください。

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

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

  • ステムの一括レンダリングには mixStereo、状態を持つルーティングには Mixer.fromSceneJson を選べる。
  • トリム、極性、ディレイ、EQ、インサート、フェーダー、パン、幅、センド、メーターを通る信号の順番を追える。
  • プリフェーダー/ポストフェーダーセンドの違いを理解し、典型的なルーティングミスを避けられる。
  • リアルタイム制約を壊さずにオートメーションをスケジュールし、メーターを読める。
  • 稼働中のミキサーにストリップを追加し、オフラインレンダリングの前にスムーザーを整定して、最初のブロックを設定値どおりに開始できる。
  • どの時点でフィールド別リファレンスの ミキシングシーン JSON へ進むべきか判断できる。

正しい入口を選ぶ ​

一括レンダリング、独自のブロック処理、再生と書き出しの同期という用途に合わせて選びます。

状況使う API理由
少数のステレオステムを 1 ファイルにまとめたいmixStereo(...)1 回の呼び出し、配列だけ、後始末も不要
再生と書き出しを備えたブラウザミキサーを作るSonareEngine操作値をリアルタイム再生とオフライン処理に反映
センド・バス・インサート・オートメーション・メーター・保存可能なプロジェクトが必要Mixer.fromSceneJson(...)ミキサー状態を保持し、シリアライズできる
ブラウザの AudioWorklet や音声コールバック内で使うWASM Mixer.createRealtimeBuffer()バッファを再利用し、ブロックごとに確保しない

シーンを使うことは決まっていても中身がまだ決まっていない場合は、オプションの ミキシングアシスタント が複数トラックを計測してシーンを提案します。トリム・フェーダー・パン・インサート・センドのそれぞれに、判断の理由が添えられます。提案するだけなので、その結果をミキサーへ読み込むのは呼び出し側が明示的に行う手順のままです。

1 つのエンジン、すべての実行環境

同じミキサーエンジンが WASM/JS、Node ネイティブ、Python、C ABI、CLI から使えます。名前は各言語の慣習に従います(mixStereo ↔ mix_stereo、fromSceneJson ↔ from_scene_json)が、ルーティンググラフ・シーン JSON・DSP は同一です。ただし 2 つの CLI は例外で、しかも互いに異なります。Python CLI の mix はシーンを一括でレンダリングします(ストリップごとに --input の WAV を 1 つ渡す)。一方ネイティブ CLI の mix-strip は --scene を持たない単一ファイル・単一ストリップのプロセッサです。ブロック単位で駆動できる永続的なマルチストリップの Mixer はライブラリの機能として使えます(WASM/JS、Node、Python、C ABI、C++)。どちらの CLI にも永続的な Mixer はありません。実行環境ごとの一覧は バインディング対応表 を参照してください。

チャンネルストリップを信号順にたどる ​

チャンネルストリップは 1 本のトラックレーンです。ストリップが音声を処理する順序を理解することが、各コントロールの効果を予測する鍵になります。たとえば、なぜポストフェーダーセンドはフェーダーの後に続き、プリフェーダーセンドはそうならないのか、が分かります。

ENGINE · LANE MIXERIDLE
エンジンのレーンミキサー — 再生エンジン内のフェーダーとミュート

3 つの MIDI クリップがリアルタイムエンジンでループします。各トラックはレーンを 1 つ占有し、専用のチャンネルストリップを持ち、レーンの合計はエンジンの True Peak リミッターを載せたマスターストリップへ送られます。フェーダーはストリップのセッターを、ミュートは setSoloMute を呼びます。下の各バンドは各レーンをソロにしてマスターチェーンを通した個別の試聴結果で、操作のたびに renderOffline で描き直されます。

リードのフェーダー
0 dB
ベースのフェーダー
3 dB
ドラムのフェーダー
-8 dB
リードをミュート
ベースをミュート
ドラムをミュート

libsonare は各ブロックを次の順序で処理します。

チャンネルストリップの信号経路
プリフェーダーセンドストリップ入力入力トリム極性反転チャンネルディレイEQ(プリ)プリインサートプリフェーダータップフェーダー + VCAパンステレオ幅ポストインサートポストフェーダータップバス / マスターへポストセンド+ メーター
信号の順序: トリム → 極性 → ディレイ → EQ(プリ)→ プリフェーダーインサート → フェーダー → パン → 幅 → ポストフェーダーインサート → バス。2 つのタップはメインの経路を変えずに、信号のコピーをセンド/メーターへ分岐させます。

上から順に読むと次のとおりです。

  1. 入力トリム:何よりも先の純粋なゲイン段で、作業しやすいレベルを整えるために使います(ゲインステージング参照)。フェーダーとは別物で、トリムは「入口でレベルを整える」、フェーダーは「他トラックとのバランスを取る」ためのものです。
  2. 極性反転:左/右チャンネルの符号を反転します。別トラックと位相が逆に録れたトラックの補正に使います。
  3. チャンネルディレイ:ストリップごとのサンプル遅延です。このストリップだけを遅らせます。エンジンのディレイ補償の対象には含まれません。
  4. EQ:内蔵のパラメトリック EQ で、シーンの eq フィールドで設定します。常にプリフェーダーで動くので、フェーダーは EQ 後の信号を扱います(C++ の ChannelStrip は set_eq_position でポストフェーダーへ移せますが、シーン JSON とバインディングにはその操作がありません)。
  5. プリフェーダーインサート:フェーダーより前で動く、指定したプロセッサ(コンプレッサー、ディエッサーなど)。
  6. プリフェーダータップ:プリフェーダーセンドとプリフェーダーメーターが読み取る地点です。フェーダーより前なので、フェーダーを動かしてもプリフェーダーセンドのレベルは変わりません。
  7. フェーダー(+ VCA オフセット):メインのレベル制御。VCA グループ(複数ストリップを一括で調整する 1 本のフェーダー)のオフセットがここで加算されるため、1 つのグループフェーダーで多数のストリップを、音声を再ルーティングせずに一括調整できます。
  8. パン:ストリップのパンモードとパンローに従ってステレオフィールド内に配置します。
  9. ステレオ幅:サイド信号を狭めたり広げたりします(モノラル互換性参照)。
  10. ポストフェーダーインサート:ポストフェーダーのレベルに反応すべきプロセッサ。
  11. ポストフェーダータップ + ゴニオメーター:ポストフェーダーセンド、出力メーター、ゴニオメーター履歴バッファへ信号を渡します。

プリフェーダーとポストフェーダーは見た目だけの違いではない

ポストフェーダーセンドはフェーダーに追従します。フェーダーを下げると、そのトラックから送ったリバーブも一緒に下がり、ドライ信号との比率が保たれます。プリフェーダーセンドはフェーダーから独立しており、ヘッドホン/キューミックスや完全ウェットなエフェクトリターンに向きます。ここを誤るのが最も多いルーティングミスです。

MIXER · SENDSIDLE
プリフェーダー送りとポストフェーダー送り — フェーダーを動かす

1 つのチャンネルが 2 つの送りとメイン出力に分かれます。チャンネルフェーダーを下げると、ポストフェーダー送りとメイン出力はそれに従います。フェーダーの後ろで分岐しているからです。プリフェーダー送りはフェーダーの前で分岐しているので、そのまま残ります。再生すると、ドライ音(ポストフェーダー)は消えていくのに、プリフェーダー送り(リバーブ/AUXのリターンの代役)はフェーダーを下げきっても鳴り続けます。プリフェーダーに送ったボーカルのリバーブが、ボーカルを下げても消えないのはこのためです。よくある送りの落とし穴です。

チャンネルフェーダー
0 dB
コードとの対応

上記の順序は ChannelStrip::process_segment そのものです。

text
input_trim -> 極性 -> alignment_delay -> eq(pre) -> プリインサート
  -> [pre タップ] -> fader(+VCA) -> panner -> width
  -> ポストインサート -> [ゴニオメーター + post タップ]

pre/post タップは事前確保したスクラッチバッファなので、センドが音声スレッドで確保することはありません。スケジュールされた各オートメーションイベント(フェーダー、パン、幅、センド、インサート)はこの同じセグメントループ内のサンプル位置で適用されます。オートメーションがサンプル精度になるのはこのためです。

一括ミックス: mixStereo ​

「ステムが数本あるので 1 ステレオファイルにしたい」なら、mixStereo だけで完結します。トラックごとの左/右チャンネルと、設定値の並列配列を受け取り、レンダリング済みマスターとトラックごとのメーターを返します。

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

await init();

const mix = mixStereo([vocalL, musicL], [vocalR, musicR], sampleRate, {
  inputTrimDb: [3, 0],     // 小さいボーカルをフェーダー前に持ち上げる
  faderDb: [-3, -12],      // バランス: ボーカルを前に、音楽を後ろに
  pan: [0, -0.2],          // 音楽をやや左へ
  width: [1, 0.9],         // モノラル安全のため音楽を少し狭める
  // panMode / muted は任意。これらもトラックごとの配列を受け付ける
});

// mix.left, mix.right  -> Float32Array マスター
// mix.meters[i]         -> トラックごとの MixMeterSnapshot(「メーター」参照)
python
import libsonare as sonare

mix = sonare.mix_stereo(
    [(vocal_l, vocal_r), (music_l, music_r)],
    sample_rate=48000,
    input_trim_db=[3, 0],   # 小さいボーカルをフェーダー前に持ち上げる
    fader_db=[-3, -12],     # バランス: ボーカルを前に、音楽を後ろに
    pan=[0, -0.2],          # 音楽をやや左へ
    width=[1, 0.9],         # モノラル安全のため音楽を少し狭める
)

# mix.left, mix.right -> マスターサンプル
# mix.meters[i]        -> トラックごとの MixMeterSnapshot(「メーター」参照)
bash
# ネイティブ CLI の mix-strip は 1 ファイルを 1 つのチャンネルストリップで処理する。
# 複数ステムをトラックごとの設定で 1 回にまとめるのはバインディング専用(上記
# mixStereo)。CLI のもう 1 つの経路は Python CLI のシーンベース `mix`。
sonare-cli mix-strip vocal.wav \
  --input-trim-db 3 \
  --fader-db -3 \
  --pan 0.2 \
  --pan-mode stereopan \
  --width 1.1 \
  -o out.wav

スカラーかトラックごとか

JavaScript の mixStereo オプションは、単一値(全トラックに適用)か配列(トラックごと)のどちらも受け付けます。faderDb: -3 は全体を 3 dB 下げ、faderDb: [-3, -12] は各トラックを個別に設定します。

Python の mix_stereo(...) では、fader_db、pan、width、muted、input_trim_db はトラックごとの sequence を渡します。pan_mode は全トラック共通の 1 値またはトラックごとの sequence のどちらも使えます。

mixStereo はオフラインのユーティリティ、バッチスクリプト、ファイル出力までの最短経路に使います。リバーブセンド・サブグループ・オートメーション・保存可能なプロジェクトが必要になった時点で、シーンベースの Mixer へ移行してください。

シーンベースのミックス: Mixer ​

シーンは、ミキサー全体(ストリップ、そのインサートとセンド、バス、VCA グループ、それらの接続)を記述したプレーンな JSON です。Mixer.fromSceneJson(...) はその記述をルーティンググラフへコンパイルし、ブロック単位で処理できるようにします。

typescript
import { init, Mixer, mixingScenePresetJson, mixingScenePresetNames } from '@libraz/libsonare';

await init();

mixingScenePresetNames();                                  // ['vocalReverbSend', 'drumBusSubgroup', 'commentaryDucking']
const sceneJson = mixingScenePresetJson('vocalReverbSend'); // 編集可能な出発点

const mixer = Mixer.fromSceneJson(sceneJson, sampleRate, /* blockSize */ 512);
try {
  // ストリップごとのステレオ音声を 1 ブロック渡し、ルーティング済みステレオマスターを得る
  // ストリップ 1(vocal-verb-return)は vocal-verb AUX バスから供給されるので、自身の入力は無音のままにする
  const silence = new Float32Array(512);
  const out = mixer.processStereo(
    [vocalBlockL, silence],
    [vocalBlockR, silence],
  );

  const vocalMeter = mixer.stripMeter(0, 'postFader');     // 「メーター」参照
} finally {
  mixer.delete();   // WASM ハンドルは GC されないので必ず解放する
}
python
import numpy as np
import libsonare as sonare

sonare.mixing_scene_preset_names()                          # ['vocalReverbSend', 'drumBusSubgroup', 'commentaryDucking']
scene_json = sonare.mixing_scene_preset_json("vocalReverbSend")

with sonare.Mixer.from_scene_json(scene_json, sample_rate=48000, block_size=512) as mixer:
    # ストリップ 1(vocal-verb-return)は vocal-verb AUX バスから供給される。自身の入力は無音のまま
    silence = np.zeros(512, dtype=np.float32)
    block = mixer.process_stereo(
        [vocal_block_l, silence],
        [vocal_block_r, silence],
    )
    vocal_meter = mixer.strip_meter(0, tap="postFader")     # 「メーター」参照
bash
# Python CLI の `mix` はシーンを一括でレンダリングする。シーンを読み込み、ストリップ
# ごとに --input の WAV を 1 つ渡し、マスターを書き出す。ブロック単位でパラメーターを
# 変えながら回せる永続的な Mixer はバインディング専用(WASM/JS・Node・Python)。
# ネイティブ CLI に --scene はなく、その `mix-strip` は 1 ストリップ専用。
sonare mixing-presets                            # 組み込みシーンプリセット名の一覧
sonare mixing-preset --preset vocalReverbSend    # 1 つのシーンの JSON を標準出力へ出力
# --input は ID=WAV でストリップを指定する(省略時はファイルの基本名)。どの入力にも指定されないストリップは無音になる
sonare mix --scene scene.json --input vocal=vocal.wav -o master.wav

ミキサーは必ず解放する

Mixer はすべての embind オブジェクトと同様、JavaScript の GC では回収できない WASM ヒープハンドルを保持します。finally ブロックで mixer.delete()(エイリアスの destroy() も可。どちらの名前も WASM / Node 両バインディングで使えます)を呼んでください。ハンドルをリークすると、長時間のセッションで WASM メモリが徐々に枯渇します。

シーンスキーマの全体、各フィールド、注釈つきのプリセット JSON は ミキシングシーン JSON にあります。3 つの組み込みプリセット(vocalReverbSend、drumBusSubgroup、commentaryDucking)はブラックボックスではありません。読み込んで編集し、toSceneJson()(Python では to_scene_json())で再シリアライズすれば、実例で形式を学べます。

シーン読み込み後は sceneWarnings() を確認する

シーンを読み込むと、リーダーが消費しなかったキーがすべて非致命的な警告として報告されます。strips[0].faderDB のような綴り間違いのフィールドや、どのプロセッサも読まないインサートの param が対象で、mixer.sceneWarnings()(Python は mixer.scene_warnings())で読み出せます。シーン自体は読み込まれ、該当キーは単に効果を持ちません。fromSceneJson(...) の直後に警告を読むのが、エラーにならないまま効いていないつまみを見つける最も手軽な方法です。インサートが受け付けるキーは masteringInsertParamNames(name) で列挙できます。

バス、ロール、ルーティンググラフ ​

バスは共有の行き先です。ストリップはバスへ、バスは別のバスへ接続し、1 つのバスが master になります。

ストリップ、バス、センド、マスター
プリインサートフェーダーポストインサートプリインサートフェーダーポストインサートプリインサートフェーダーポストインサートインサートインサートインサートストリップ 1ストリップ 2ストリップ 3サブミックスバスAUX バスマスタープリフェーダーセンドポストフェーダーセンド出力プリフェーダーセンドはフェーダーに影響されず、ポストフェーダーセンドは追従する。元の経路はどちらでも続く。

各ストリップ本来の経路は、プリフェーダーインサート、フェーダー、ポストフェーダーインサートを通ってからバスへ届きます。各レーンにある 2 つのタップ点は、プリフェーダーまたはポストフェーダーのセンドが信号を複製して AUX バスへ送り出す位置で、各バスは合算後の信号に対して自前のインサートチェーンを 1 回だけ走らせます。各バスは role を持ちます。

ロール意味
master最終ステレオ出力。すべての信号が最終的にここへ届く。
auxセンドの並列行き先。通常はエフェクトリターン(リバーブ、ディレイ)。
submix(そのほかの任意の文字列)マスターの前にまとめて処理するストリップ群(「ドラムバス」)。文字列はラベルにすぎません(下記参照)。

グラフが特別に解釈するロール文字列は master だけです。aux はロールを指定しなかったバスに付く既定値にすぎません。それ以外のロール文字列(submix、subgroup、group など)はマスター以外の一般的なバスとして扱われます。"mastre" と綴り間違えると普通のバスになり、マスターは id が master のバスか暗黙のバスになります。組み込みの drumBusSubgroup プリセットはドラムバスに subgroup というラベルを付けているため、シーンを出力すると "role": "submix" ではなく "role": "subgroup" が表示されることがあります。

バスはインサートチェーンを 1 ブロックにつき 1 回、そこへ入るすべての信号の合計に対して走らせます。そのためグループバスのコンプレッサーやサチュレーションは、各ソースに個別にかかるのではなく、グループ全体にかかります。クリップ音声とホストしている楽器の両方がバスへ入るリアルタイムエンジンでも同じ規則が保たれます。バスのインサート列は合算後の信号を 1 度だけ通る を参照してください。

接続はグラフを形成します。Mixer.fromSceneJson はミキサー構築時にそのグラフをビルドしてコンパイルするため、返されたミキサーはすぐに処理できます。構築後は mixer.addBus(id, role?)(role の既定は 'aux')と mixer.removeBus(id) でバスを追加・削除でき、mixer.busCount() で現在の本数を取得できます。mixer.setOutputBus(sourceId, busId)(Python は set_output_bus、C は sonare_mixer_set_output_bus)は、シーン JSON を書き直さずに、構築後のストリップやバスをサブミックスへルーティングします。シーン側で定義されたストリップ間のエッジを含め、ソースのメイン接続を置き換えます。ストリップのインデックスではなく id を取り、未知の id、バスではない宛先、ソースとしてのマスター、自己ルーティング、接続やセンドをたどるサイクルを閉じる変更は、id を示すコードつきの InvalidParameter エラーで拒否し、トポロジーは変わりません。addBus で追加したサブミックスは、そちらもマスターへルーティングして初めてマスターに届きます。トポロジーを変更するとグラフは再コンパイル待ちになり、次の processStereo 呼び出しで遅延再コンパイルされます(compile() を呼べば即時に再コンパイルできます)。

トポロジー変更をした後は、次のタイミングが重要なブロックの前に compile() を呼んでください。トポロジー変更とは、たとえば次のような変更です。

  • ストリップの追加
  • バスの追加/削除
  • センドの追加/削除
  • setOutputBus によるストリップやバスの再ルーティング

パラメータ変更、オートメーション変更、および VCA グループの変更(追加・削除・ゲイン調整)には再コンパイルは不要です。VCA グループはメンバーストリップへのライブなゲインオフセット調整のみで、ルーティンググラフの一部ではありません。

strip の指定方法はランタイムで異なります。

ランタイムstrip の指定方法
WASMmixer 制御メソッドは数値のストリップインデックスを使います。シーン ID から指定したい場合は、先に stripById(id) でインデックスを取得します。
Node ネイティブ / Python多くの制御メソッドで、数値インデックスとストリップ ID 文字列の両方を受け取ります。

ストリップの追加: addStrip ​

どのバインディングでも Mixer はシーンから構築します。空のミキサーを作るコンストラクタはないので、構築後にミキサーへストリップを増やす手段が mixer.addStrip(id, metering?)(Python は mixer.add_strip(strip_id, ...))です。セッション途中で増えたトラックや、雛形として読み込んだシーンをコードで埋めていく場面で使います。バインディングレベルのシグネチャは JS API ページにあります。

呼び出し側が渡すのは id で、未使用でなければなりません(重複は例外になります)。戻り値はありません。ストリップはミキサーが所有し、以後はインデックス(末尾に追加されるので stripCount() - 1)か stripById(id) で指定します。それ以外はストリップの既定値で始まります。トリムとフェーダーは 0 dB、パンは中央、幅は 1、インサートもセンドもなく、出力は master へ直結です。submix バスへ送りたい場合は、追加後に mixer.setOutputBus('room-mic', 'drum-bus')(Python は set_output_bus)でルーティングし、compile() を呼びます。メンバー一覧にその id をすでに挙げている VCA グループは、ストリップが追加された時点でそれをメンバーに含めます。

ここでしか決められないのがメーター設定です。ストリップのメーターバッファは構築時にサイズが決まるからです。既定では両方のメーターをフル構成(LUFS + 4 倍オーバーサンプリングの True Peak、48 kHz で 1 ストリップあたり約 1.4 MB)で持ちます。{ lufs: false } は各メーターを約 83 KB に縮め、{ enabled: false } はスナップショットを読まないストリップ向けに両方を落とします(約 145 KB)。このオブジェクトはシーンの strips[].metering と同じ名前で、truePeakOversample が 0〜16 の外なら例外です。メーターは要求値を、2 なら 2 倍、8〜16 なら 8 倍、それ以外は 4 倍に解決するので、0 はライブラリ既定の 4 倍になります。

typescript
mixer.addStrip('room-mic', { lufs: false });
const roomMic = mixer.stripCount() - 1;      // 末尾に追加される。stripById('room-mic') でも同じインデックス
mixer.setFaderDb(roomMic, -8);
mixer.setPan(roomMic, 0.4, 'stereoPan');
mixer.compile();                             // ストリップの追加はトポロジー変更
mixer.settle(roomMic);                       // 次の節を参照

processStereo はストリップ順に、ストリップ 1 本につきステレオ 1 組を受け取るので、追加したストリップの分だけ末尾の入力が 1 組増えます。渡した組より後ろのストリップには無音が入り、stripCount() より多い組を渡すと、両方の数を示す引数エラー(WASM と Node は RangeError、Python は SonareValueError)で拒否されます。

オフラインレンダリング前の整定: settle ​

入力トリム、フェーダー(VCA オフセット込み)、パン、幅はスムージングされています。新しい値へは時定数 5 ms で滑らかに近づく(5 ms で約 63%、約 25 ms でほぼ到達する)ので、ライブでフェーダーを動かしてもクリックノイズは出ません。このスムーザーはストリップ構築時に 0 dB・中央・幅 1 で初期化され、シーンの値はその後に適用されます。そのため fromSceneJson で作った直後のミキサーでも、最初のブロックは既定値から始まってシーンの値へ滑っていきます。フェーダーが −12 dB のストリップは 0 dB から下がり、左いっぱいのストリップは中央から外へ動き、出力冒頭の 20 ms ほどにそれが残ります。

mixer.settle(strip) はこの 4 段を設定済みの値へスナップし、次のブロックをその値ちょうどで開始させます。それ以外は何も消しません。キュー済みのオートメーション、メーター、インサート状態、シーン JSON はそのままです。スムーザーが向かう先はシーンがすでに記録している値だからです。

呼ぶタイミングはストリップごとに 1 回、トリム/フェーダー/パン/幅の最後の変更の後、冒頭を残すレンダリングの最初の processStereo の前です。オフラインバウンス、テストフィクスチャ、1 パスで書き出すファイルがこれにあたります。その後に値を変えるとグライドは現在値から再び始まるので、次のレンダリング前にまた整定してください。ライブセッションでは呼びません。フェーダー操作でクリックが出ないのは、このグライドがあるからです。

typescript
for (let i = 0; i < mixer.stripCount(); i++) mixer.settle(i);   // WASM はインデックス指定
const out = mixer.processStereo(lefts, rights);                  // 先頭サンプルから設定値どおり

Node と Python の settle はストリップ id でも指定できます。エンジンのプロジェクトバウンスは全ストリップを自動で整定しますが、ブロック単位で自分で駆動する Mixer は整定しません。

センドとバスの削除 ​

ストリップのセンドは追加順のインデックスで指定します。removeSend(strip, sendIndex) で 1 つ削除すると、それより後ろのセンドはすべてインデックスが 1 つ前へ詰まります。削除した位置より大きいインデックスをキャッシュしていた場合は 1 つずれるので、削除後はインデックスを取得し直してください(ルーティングされた結果を読む前に再コンパイルまたは処理を行い、グラフを再構築させます)。

バスを削除すると、そのバスを指していたルーティングも整理されます。宛先がそのバスだったセンドは、送り先が存在しなくなるため破棄されます。送り先のバスを削除した後は、そのストリップのセンドを確認してください。

どこにも届かないバス

明示的な submix または aux バスから master への経路がない場合でも、警告は出ません。グラフはそのままコンパイルされて動きますが、master へ届かないバスは出力に何も寄与しません。多くは意図的な行き止まりではなく、接続の付け忘れです。sceneWarnings() が報告するのは読まれなかったキーで、到達できないバスは対象外です。追加した各バスが実際に master まで配線されているかは自分で確認してください。

VCA グループ ​

VCA グループは、複数ストリップの音声を再ルーティングせずにレベルだけをまとめて調整する 1 本のフェーダーです。VCA は voltage-controlled amplifier(電圧制御アンプ)の略で、歴史的には 1 つの制御電圧で複数のコンソールチャンネルを一括して増減していました。ここではそれをソフトウェアで再現したもので、音声を再ルーティングせずに 1 本のフェーダーでグループをオフセットします。「drums」VCA を 2 dB 下げると、キック・スネア・オーバーヘッドがそれぞれのバスへ流れたまま 3 つとも 2 dB 下がります。

グループの gainDb は、各メンバーがすでに持つフェーダー値に対する差分として、各メンバーのフェーダー段(上記ステップ 7)に加算されます。差分だけが適用されるため、グループ内でストリップごとに設定したフェーダーの微調整は失われません。グループフェーダーは個別のバランスを上書きせずに、セット全体を一括で上下できます。

ストリップのグループとグループゲインは addVcaGroup(id, gainDb, members) で追加し、setVcaGroupGainDb(...) でゲインを調整し、setVcaGroupMembers(id, members) でグラフを再構築せずにメンバー構成を置き換え、removeVcaGroup(id) で削除します。このグループ定義(ゲインとメンバー構成)はシーン JSON を往復します。setVcaOffsetDb(...) による調整は、セッション中に変更できるストリップごとのライブなオフセットです。これはストリップの vcaOffsetDb として保存され、シーン JSON を往復します。

ソロとミュートのロジック ​

  • setMuted(strip, true) はストリップを無音にします。
  • setSoloed(strip, true) は他のすべてのストリップを暗黙ミュートします。ただし setSoloSafe(...) でソロセーフに指定したものは除きます。エフェクトリターンのストリップをソロセーフにしておくと、ボーカルをソロにしてもそのリバーブリターンが聞こえます。

ソロ・ミュート・ソロセーフは、グラフ再コンパイルなしで次のブロックから有効になります。

ミュートまたはソロで無音になったストリップは完全に無音になります。プリフェーダーセンドも一緒に無音になり、ゲートは閉じれば厳密にゲイン 0、開けば厳密にユニティに達します。ソロセーフのストリップは、他のストリップがソロの間も鳴り続けます。

ただし瞬時に切り替わるわけではありません。レーンのミュート/ソロのゲートは時定数 10 ms でスムージングされたゲインなので、ミュートの開閉でクリックノイズが出ません。比較として、パンのスムーザーは 5 ms です。パンの移動はゲートよりも操作へ密に追従する必要があるためです。どちらもエンジン側で固定された挙動で、バインドしたインサートパラメータのランプを制御する別のパラメータスムージング設定とは無関係です。

パンモードとパンロー ​

パンモードは位置を左右へ どうマッピングするか 、パンローは中央をサイドに対してどれだけの音量にするかです。

  • balance:すでにステレオの素材向け。像を動かすのではなく片側を下げます。
  • stereoPan:本来のパン。まずストリップを (L + R) / 2 でモノラルに畳み、そのモノラル信号をフィールド内に配置します。左右で異なる成分は失われ、逆相の成分は打ち消され、左チャンネルだけにあった音はパンローがかかる前に畳み込みで半分(−6 dB)になります。モノラルやほぼモノラルのソースに使い、本物のステレオ素材には balance か dualPan を使ってください。
  • dualPan:左右独立の位置(setDualPan(...) で設定)。広いステレオトラックを内側へ畳むなど。

ストリップは setChannelDelaySamples(...) で自身の信号をサンプル単位で遅延させることもできます。パンに触れずに、近接マイクとルームマイクの時間合わせや、パラレル経路の補正を行うためのストリップ単位のアライメント調整です。

永続的な Mixer は、ストリップ内の他の段もライブで操作できます。いずれもグラフの再コンパイル不要のパラメータ変更で、次のブロックから有効になります。

  • setInputTrimDb(strip, db):信号フローの入力トリム段(mixStereo の inputTrimDb オプションと同じゲイン)。フェーダーとは別物です。
  • setWidth(strip, width):ステレオ幅段(mixStereo の width オプション)。0 = モノラル、1 = 原音、1 より大きい値で広げます。幅はパンの後に、パン済みの信号のミッド/サイド成分に対してかかります。そのためパンしたモノラルソースでは位置も動きます。左いっぱいのソースは幅 0 で中央の −6 dB になり、幅 2 では右チャンネルに半分のレベルの逆相成分が現れます。モノラルソースは幅を 1 のままにし、パンだけで配置してください。
  • setPolarityInvert(strip, invertLeft, invertRight):極性反転段。ストリップの左/右チャンネルの符号を反転します。

JavaScript API のパンローは const3dB、const4.5dB、const6dB、linear0dB です。Python では同じ値を enum/int、または const-3db、linear-0db のような正規化文字列で指定します。

ほとんどのパンローは中央を一定量だけ下げます。これにより、中央へパンした音が左右いっぱいへパンした音より大きくならないようにします。

パンロー中央の減衰用途
const3dB−3 dB多くの素材(既定)。定パワー
const4.5dB−4.5 dBコンソールでよく使われる中間的な選択
const6dB−6 dB定振幅。モノラル合算レベルを一定に保つ
linear0dB0 dB中央の補正を一切行わない

const3dB は定パワー則です(cos/sin、中央で各チャンネル 0.707)。パンしても総エネルギーが変わらないため、スピーカー再生ではこれが基準になります。一方でコヒーレントなモノラル合算は中央で 3 dB 上がります。const6dB は定振幅則です(中央で各チャンネル 0.5)。左右が加算されると左右いっぱいへパンしたときと同じ 1.0 になるため、モノラル合算が一定に保たれ、代わりにパワーが 3 dB 下がります。const4.5dB はこの 2 つの幾何平均です。linear0dB は補正を行わず、中央でも両チャンネルが等倍のままです。そのため中央の音はハードパンした音よりパワーで 3 dB、モノラル合算で 6 dB 大きくなります。後段ですでに独自のパン補正が入っている場合にだけ選んでください。

中央の減衰が出力に現れるのは stereoPan モードだけです

表の値はパンローが返す生のゲインです。既定の balance モードでは、パンナーが両チャンネルのゲインを大きいほう(近い側)で割るため、近い側は常に等倍になり、遠い側だけが下がります。つまりどのパンローを選んでも中央は 0 dB のままで、パンローは近い側と遠い側の比だけを決めます。中央の減衰そのものが必要なら、ストリップを stereoPan に切り替えてください。sourceLayout が mono のストリップは第 3 のケースです。balance ではどのパンローでも中央が等倍で、stereoPan では各出力チャンネルにパンローの中央ゲイン(4 つのパンローで 0.707 / 0.595 / 0.5 / 1.0)が掛かります。これは整定済みのストリップで測り直した値です。

MIXER · PAN LAWIDLE
パンロー:音源を動かすとレベルはどう変わるか

モノラル音源をミキサーのストリップ 1 本でパンした結果です。上段はストリップが左右にかけるゲイン、下段は合計パワーとモノラル合算(L + R)を、ハードパンを基準にした dB で示し、ほかのパンローも薄く重ねています。stereoPan では、const3dB はパワーを一定に保ち、モノラル合算はセンターで 3 dB 上がります。const6dB はモノラル合算を一定に保ち、パワーが 3 dB 下がります。balance に切り替えると、どのパンローでもセンターは左右とも等倍です。再生すると、ノイズが左から右へ移動します。

パンロー
パンモード

サラウンドとマルチチャンネル ​

ステレオより広いバス向けに、ストリップは SurroundPan 位置(setSurroundPan(strip, { azimuth, divergence, lfe })、Python は set_surround_pan(strip, azimuth=..., divergence=..., lfe=...))を保持します。azimuth(−180…180°、0 = 正面中央)・divergence(0 = 点音源、1 = 前方へ広がる)・lfe(0…1 の LFE=低域効果チャンネルのプレーンへの送り)が有効で、elevation と distance は予約です。位置はシーンに保存され JSON で往復しますが、単体の Mixer(processStereo)は依然ステレオのみをレンダリングします。同じサラウンドパンナーはリアルタイムエンジンのサラウンドグループバスと、対応する出力幅でのプロジェクトバウンスでも動くため、ここで位置を設定し、サラウンドミックスはそのどちらかでレンダリングしてください。

オートメーション ​

時間変化するコントロールはすべて、最初の processStereo 呼び出しからの絶対サンプル位置でスケジュールします。再コンパイルしてもこのクロックはリセットされず、キュー済みのオートメーションもそのまま残ります。利用できるレーンは次のとおりです。

typescript
mixer.scheduleFaderAutomation(stripIndex, sampleRate * 8,  -6, 's-curve');   // 8 秒でボーカルを下げる
mixer.schedulePanAutomation(stripIndex, sampleRate * 12,  0.3, 'linear');
mixer.scheduleWidthAutomation(stripIndex, sampleRate * 12, 1.2, 'linear');
mixer.scheduleSendAutomation(stripIndex, sendIndex, sampleRate * 16, -12, 'hold');
mixer.scheduleInsertAutomation(stripIndex, insertIndex, paramId, sampleRate * 4, value, 'exponential');

補間カーブはイベント間の動き方を決めます。

カーブ形用途
linear直線ランプ一般的なレベル/パンの移動。dB のフェーダーレーンでは、これだけで自然に聞こえるフェードになる
exponential値の大きさを対数軸上で補間周波数スイープやリニアゲインのパラメータ
s-curve入りと出の両方をなめらかにクリックのない滑らかな遷移
holdランプなしで跳ぶ段階的/即時の変化

dB レーンでの exponential

フェーダーやセンドのレーンは dB の値を持ち、dB 上の直線はそれだけで振幅の指数的な変化です。exponential は dB の値をさらに曲げるため、形が方向によって変わります。−1 dB から −60 dB へ向かうと 4 分の 1 ごとの点で −2.8、−7.9、−22 dB(ゆっくり始まって速くなる)、−60 dB から −1 dB へ向かうと −21、−7.6、−2.7 dB(速く始まってゆっくりになる)になります。ちょうど 0 dB で始まるか終わる区間、または 0 dB をまたぐ区間は直線で補間されます。フェーダーのフェードには linear か s-curve を使ってください。

インサートパラメータのオートメーション

scheduleInsertAutomation はインサートを結合シーケンス [プリインサート…, ポストインサート…] 内のインデックスで指定し、paramId はプロセッサ固有です。声の下でコンプのスレッショルドを動かしてダッキングするのが定番ですが、サイドチェインダッキングは commentaryDucking プリセットが配線済みです。

メーター ​

各ストリップ(およびマスター)は豊富な MixMeterSnapshot を公開します。mixStereo の meters[] からレンダリング後に、または mixer.meterTap(strip, 'preFader' | 'postFader') でライブに読み取れます。stripMeter(...) は WASM/Python では便利な別名、Node ネイティブではポストフェーダーの簡易入口です。busMeter(busId) は、master を含むコンパイル済みバスの post-insert メーターを返します。

フィールド意味
peakDbL / peakDbR測定区間(下記参照)でのチャンネルごとのサンプルピーク
truePeakDbL / truePeakDbR / maxTruePeakDbサンプル間ピーク(ISP)を含む True Peak。ITU-R BS.1770-4 に沿った 4 倍オーバーサンプリングによる推定値で、DAC(デジタルアナログ変換器)が再構成するピークに近い値ですが、実際の再構成フィルターはこれよりわずかに高く出ることがあります
rmsDbL / rmsDbR測定区間でのチャンネルごとの平均(RMS)レベル
momentaryLufs / shortTermLufs / integratedLufs400 ms / 3 s / 全体(BS.1770 のゲート付き)の ラウドネス
correlation測定区間での −1…+1 の位相相関。+1 付近はモノラル安全、負は打ち消しの警告
monoCompatWidth / monoCompatPeak / monoCompatSideRms / likelyMonoCompatibleモノラル互換性の要約
gainReductionDbストリップ上のダイナミクスがどれだけ働いているか
seq変化検出用の単調増加スナップショットカウンター

測定区間と、UI 側でバリスティクスを付ける理由

ピーク、RMS、相関値は、1 回の呼び出しで処理した音声だけを対象にします。mixStereo ではレンダリング全体、Mixer やリアルタイムエンジンでは直前に処理した 1 ブロックです。48 kHz で 128 フレームのブロックなら 2.7 ms の区間になり、値はブロックごとに跳ねます。LUFS の各値は別で、400 ms と 3 s の窓がブロックをまたいでスライドするため、ブロックサイズに左右されません。

ハードウェア風のメーターは、決まったバリスティクス(針の動き方)で値をなめらかにします。VU メーターは約 300 ms で積分し、ピークプログラムメーター(PPM)は数ミリ秒で立ち上がってゆっくり戻り、ピークホールドの目印は最大値を 1〜2 秒保持します。libsonare はブロックごとの生の値を返し、バリスティクスは表示側に任せます。UI では、落ちていくピーク(たとえば毎秒 20 dB)と、約 1.5 秒の最大値ホールドを持たせるのが一般的です。

METERS · LOUDNESSIDLE
ラウドネス計測 — LUFS・True Peak・レンジ

バーは再生中の瞬時ラウドネスを追い、パネルは時間ごとのラウドネスです。インテグレーテッド LUFS は全体を表す一つの数値、True Peak はサンプル間も含むピークの推定値、LRA はラウドネスの動く幅を表します。ウィンドウを切り替えると、速い瞬時メーターと滑らかな短時間メーターを比べられます。どちらもウィンドウが埋まってから値を出すため、短時間の曲線はクリップの 3 秒後から始まります。

ウィンドウ

ゴニオメーターとは

ゴニオメーター(ベクトルスコープとも呼びます)は、左信号と右信号を点群として描く表示です。通常は 45° 回転させ、x = (L − R) / √2、y = (L + R) / √2 として描きます。この表示では、ほぼ垂直な線はモノラル、横に広がった塊は広いステレオ展開、横方向ににじむ表示はモノラルで打ち消し合いかねない位相の問題を警告します。correlation メーターを絵にしたものです。後述の readGoniometerLatest は回転前の { left, right } の組を返すので、回転は自分で行ってください。回転せずに描くと、モノラル信号は 45° の対角線になります。

True Peak 値はブロックより再構成フィルターの群遅延 1 つ分だけ遅れて読まれるため、ブロックサイズに依存しません。信号の最後のブロックのあとで mixer.flushMeters()(Python は flush_meters())を呼ぶと、その保留分を無音に対して読み切ってスナップショットに反映します。以降の processStereo は新しい信号として始まります。オフラインのエンジンレンダーとワンショットの mixStereo は、これを自動で行います。ブロックの最後に短いバーストを置くと違いが出ます。フラッシュ後は truePeakDbL の読みが上がりますが、サンプルピークは動きません。

LOUDNESS · TRUE PEAKIDLE
サンプル間ピーク — マスターが再生時にクリップする理由

ドットは正弦波のサンプル、曲線はその解析的な波形です。山の頂点がサンプル間に来ると、保存された値より高いピークが生じます。その差は周波数と位相で変わり、ナイキスト周波数に近づくほど単調に増えるわけではありません。解析的なピークと、オーバーサンプリングしたメーターの推定値を比較してください。サンプルピークを 0 dBFS に近づけると、サンプル間の超過が生じ、ヘッドルームの足りないコンバーターや後段の処理でクリップする可能性があります。

サンプルピーク
-0.3 dBFS
周波数(×ナイキスト)
0.4

ゴニオメーターは別の時間領域ビューです。readGoniometerLatest(strip, maxPoints) が直近の左/右サンプル対(古い→新しい)を返し、ステレオベクトルスコープを描画できます。コア側の読み取りは割り当てを行いません。ストリップが持つ事前確保リングから呼び出し側の領域へ点をコピーするため、UI からポーリングしても音声スレッド側の確保は増えません。WASM バインディングはその後、一時領域と、プレーンな JavaScript 配列および各要素の { left, right } オブジェクトを確保します。JS 呼び出し全体にはバインディング側の確保がありますが、戻り値は構造化複製できるプレーンデータなので、structuredClone や postMessage に安全に渡せます。

リアルタイムと AudioWorklet ブリッジ ​

ミキサーコアは予測可能な音声コールバックのために作られています。デノーマル対策、ロックフリーなパラメータ変更、事前確保された状態、グラフ単位のプラグインディレイ補償を備えます。

ドライ音や外部処理した音声と位置を揃えるときは、mixer.latencySamples() でコンパイル済みグラフが報告するレイテンシを取得します。トポロジーが再コンパイル待ちの場合、この呼び出しは値を返す前にグラフをコンパイルします。

WASM パッケージの processStereo は、呼び出すたびに結果用の配列を新たに確保します。オフラインでは問題ありませんが、リアルタイムの音声コールバック内では禁じられます。そうしたタイトなループでは、代わりに次のいずれかの確保なし経路を使ってください。

  • processStereoInto(inL, inR, outL, outR):呼び出し側が所有する配列へ書き込みます。入力チャンネル数がストリップ数より少なくてもよく、残りのストリップは processStereo と同様に無音のままです。ストリップ数より多いと RangeError です。
  • createRealtimeBuffer():再利用可能な WASM ヒープ上の入出力ビューを返します。入力を埋め、process() を呼び、outLeft/outRight を読む、を繰り返します。ビューはミキサーが所有し、delete() 後は無効になります。

ホストがストリップ入力を送るのを止めても、先読み・リバーブ・ディレイの各プロセッサにはまだ音声が残っています。mixer.tailSamples() でコンパイル済みグラフ内の最大プロセッサテイル長(サンプル数)を読み取り、mixer.drainTailStereo(numSamples) でゼロ入力ブロックをレンダリングして、そのテイルをマスターへ吐き出させます。drainTailStereo は 1 ブロックをレンダリングするだけでループはしません。numSamples はミキサー構築時のブロックサイズが上限なので、tailSamples() 分のサンプルを取り切るまでブロックサイズ単位で繰り返してください。

typescript
let remaining = mixer.tailSamples();
while (remaining > 0) {
  const n = Math.min(remaining, blockSize);
  const tail = mixer.drainTailStereo(n);   // ゼロ入力ブロックを 1 つ
  exportBlock(tail.left, tail.right);
  remaining -= n;
}
デノーマル対策とは?

デノーマル(非正規化数)は、ゼロに極めて近い非常に小さな浮動小数点数で、多くの CPU では通常の値よりはるかに遅く処理されます。

音声コールバックでは、リバーブやディレイのテイルが減衰してサンプルがゼロに近づくときにこの範囲へ入り込み、処理時間が急に跳ね上がってドロップアウトを招くことがあります。デノーマル対策はこうした極小値をゼロに切り捨て、各ブロックの処理時間を予測可能に保ちます。

トポロジー変更はリアルタイム安全ではない

バス・センドの追加はグラフを再コンパイル待ちの状態にし、次の processStereo で再コンパイル(確保が起こりうる)します。構造変更はセットアップ中か重要でないブロックで行い、compile() を呼んでから、タイトなレンダリングループに入ってください。フェーダー/パン/センド/インサートの操作、オートメーション、および VCA グループの変更はループ内で問題ありません。

レイテンシとプラグインディレイ補償 (PDC) ​

先読み処理(たとえばドラムバス上のリミッター)はレイテンシを足します。グラフのある経路だけ遅延し別経路が遅延しないと、両者はマスターでずれて到達します。

エンジンは各経路のレイテンシを測定し、短い経路を長い経路に合わせて遅延させることで、マスターですべてを揃えます。これがプラグインディレイ補償です。ストリップごとの channelDelaySamples は意図的なタイミング調整です。そのストリップだけを遅らせ、報告されるレイテンシや PDC による他の経路の遅延には加算されません。

レシピ ​

数本のステムをバランスしてバウンス

最速のミックス。バスもオートメーションもなく、トリム、バランス、レンダリングだけです。

typescript
const mix = mixStereo(lefts, rights, sampleRate, {
  inputTrimDb: trims,   // 各ステムを妥当な作業レベルへ
  faderDb: balances,    // 互いのバランスを取る
  pan: positions,
});
exportWav(mix.left, mix.right, mix.sampleRate);
AUX センドで共有するボーカルリバーブ

リバーブ 1 つを、ポストフェーダーセンドで送り、専用ストリップで返します。vocalReverbSend プリセットの縮小版です。

typescript
const mixer = Mixer.fromSceneJson(mixingScenePresetJson('vocalReverbSend'), sampleRate, 512);
// strip 0 = "vocal"(EQ + コンプのインサート、"vocal-verb" AUX へのポストフェーダーセンド)
// strip 1 = "vocal-verb-return"(プレートリバーブのインサート、マスターへ返す)
mixer.setSendDb(0, 0, -10);          // リバーブを増やす
mixer.compile();                      // トポロジー編集後のみ必要

センドはポストフェーダーなので、ボーカルフェーダーを下げるオートメーションはリバーブも一緒に下げます。

パラレルコンプつきドラムサブグループ

キック/スネア/オーバーヘッドを 1 つの submix バスへ送り、パラレルコンプとテープでまとめ、VCA で一括調整します。drumBusSubgroup プリセットがその例です。

DYNAMICS · COMPRESSORIDLE
パラレルコンプレッション — コピーを潰し、パンチは残す

ドライ音と圧縮した音を混ぜます。圧縮音だけの場合に比べ、ドライ音を混ぜると元の立ち上がりが残ります。このデモはメイクアップゲインを加えないため、小さい音を持ち上げる処理ではなく、混合比によって出力レベルも変わります。入出力特性とゲインの変化を見ながら試聴してください。

スレッショルド
-28 dB
レシオ
8 :1
ブレンド
50 %
typescript
const mixer = Mixer.fromSceneJson(mixingScenePresetJson('drumBusSubgroup'), sampleRate, 512);
mixer.setVcaOffsetDb(/* ドラムメンバーのストリップ */ 0, -1.5);  // キット全体をライブで調整
ポッドキャスト/実況のダッキング

サイドチェインで音楽ベッドを話声の下に自動で下げます。commentaryDucking プリセットは host ストリップをキーにした dynamics.sidechainRouter をベッドに配置します。

typescript
const mixer = Mixer.fromSceneJson(mixingScenePresetJson('commentaryDucking'), sampleRate, 512);
// host + guest は "voices" として VCA グループ化。music-bed ストリップは host が話すたびにダッキングする

関連 ​