CLI だけでミックスからマスタリングまで
ステムが 4 本あって、DAW は開いていない。このページはその状態から 1 本のシェルスクリプトで完成したマスターまで到達し、そのうえで各ステップが何を決めたのか、その判断が妥当かをどう見分けるのかを解説します。
この作業は「コマンドを 1 つ実行すれば終わる」ものではありません。計測・提案・レンダリング・マスタリングという 4 つの段階の連鎖であり、その途中に人間のチェックポイントが入ります。ミックスは機械が最後まで代われない部分だからです。
このページで身につくこと
このページを読むと、次のことができるようになります。
- ステム一式からミキサーシーンを作り、その中の数値ひとつひとつについてアシスタントが述べる理由を読める。
- そのシーンを素の JSON として編集し、ステレオのミックスダウンへレンダリングできる。
- ミックスダウンを指定した配信ターゲット向けにマスタリングし、処理前後のレポートを読める。
- 画面に出ている複数のラウドネス値のうち、どれを信じるべきかを判断できる。
全体のスクリプト
以下がこの作業の全体です。まず実行し、そのうえで各段階の説明を読んでください。
#!/usr/bin/env bash
set -euo pipefail
# 1. ステムからミックスを提案させる。ここでは計測のみで、音声は処理されない。
sonare suggest-mix \
--input drums=stems/drums.wav \
--input bass=stems/bass.wav \
--input gtr=stems/gtr.wav \
--input vox=stems/vox.wav \
--sample-rate 48000 \
--tempo-bpm auto \
--scene-out scene.json \
--json > suggestion.json
# 2. 理由を読み、scene.json を手で直す。ここがチェックポイント。
python3 -c 'import json;[print(l) for l in json.load(open("suggestion.json"))["explanation"]]'
# 3. シーンをステレオのミックスダウンへレンダリングする。
sonare mix --scene scene.json \
--input drums=stems/drums.wav \
--input bass=stems/bass.wav \
--input gtr=stems/gtr.wav \
--input vox=stems/vox.wav \
-o mix.wav --json
# 4. マスタリングに進む前に、破損がないか確認する。
sonare repair mix.wav --detect --json > defects.json
# 5. 配信ターゲット向けにマスタリングし、レポートを残す。
sonare mastering mix.wav \
--assistant --preset edm --explain \
--target-platform streaming \
-o master.wav --report report.json --jsonステップ 1 — ミックスを提案させる
suggest-mix は各ステムを計測し、さらにステム同士のあいだで起きていることを計測して、ミキサーシーンを書き出します。音声そのものには一切触れません。--scene-out が出すのはレンダリング結果ではなくドキュメントです。オプション全体と背後のルールは ミキシングアシスタント にあります。
sonare suggest-mix \
--input drums=stems/drums.wav --input bass=stems/bass.wav \
--input gtr=stems/gtr.wav --input vox=stems/vox.wav \
--sample-rate 48000 --tempo-bpm auto \
--scene-out scene.json --json出力のうち重要なのは 3 か所で、それぞれ答えている問いが違います。
explanation は、各数値がなぜその値なのかを示します。判断 1 件につき 1 行が、その判断を下したルール自身から出力されます。
staged vox with -7.2 dB of input trim towards the -18.0 LUFS target
balanced vox at +2.7 dB relative to its staged level as a vocal part,
scaled down from +5.0 dB by a 0.53 classification confidence
compressed vox at 3.3:1 above -22.2 dB, with the 5.3 ms attack and 200.0 ms
release its 5.2 dB crest factor and 1.00 sustain ratio call for
sent vox to the plate reverb at -10.5 dB as a vocal part
pulled the master bus down to leave the summed mix its headroomシーンより先にここを読んでください。納得できない判断があるなら、たいていはその手前の分類に納得できていないのであって、各行はその分類を明示しています。scaled down from +5.0 dB by a 0.53 classification confidence は、そのステムがボーカルだという確信が半分しかなかったとアシスタント自身が言っている行です。
tracks は、何を計測したかを示します。ステムごとに integratedLufs、crestFactorDb、sustainRatio、spectralCentroidHz、bandOccupancy、channelCount、そして判定された分類(source、sourceConfidence)が並びます。これが説明文の根拠です。
mix は、ステム同士のあいだで起きていることを示します。bandDominance は帯域ごとにマスクする側とされる側の組を並べ、crowdedBands は各帯域の混雑度を数値化し、monoRisks はモノラルで崩れる素材を指摘します。シーンはこれらに直接手を打ちません。問題がフェーダーで解けるのかアレンジの話なのかを、読み手が判断するための材料です。
--tempo-bpm auto の意味
提案されるシーンのディレイタイムはテンポに合わせて決まります。auto は最初の --input からテンポを検出します。フラグ自体を省くとトランスポートのフォールバックテンポが使われますが、それが曲のテンポと一致することはまずありません。
ステップ 2 — シーンを編集する
scene.json は素のドキュメントで、フィールド単位の仕様は ミキシングシーン JSON にあります。開いて、納得できないところを直して、保存する。これが 2 段階設計の狙いです。ステムからミックスダウンまでをこのファイルを見せずに一気に進めるコマンドは、意図的に用意していません。
上の実行で得られたシーンにはストリップが 6 本あります。4 本のステムに加えて、aux バスから供給される reverbReturn と delayReturn です。各インサートはパラメータを JSON 文字列として持ちます。
{
"id": "vox",
"faderDb": 2.652244806289673,
"inputTrimDb": -7.215085029602051,
"inserts": [
{ "processor": "dynamics.compressor", "slot": "pre",
"params": "{\"attackMs\":5.26,\"ratio\":3.34,\"thresholdDb\":-22.2,\"autoMakeup\":true}" },
{ "processor": "dynamics.vocalRider", "slot": "pre",
"params": "{\"targetDb\":-18,\"maxBoostDb\":3,\"maxCutDb\":3}" }
],
"sends": [
{ "id": "vox-to-reverbBus", "destinationBusId": "reverbBus", "sendDb": -10.5, "timing": "post" },
{ "id": "vox-to-delayBus", "destinationBusId": "delayBus", "sendDb": -14, "timing": "post" }
]
}sendDb・faderDb・スレッショルドの変更は単なるテキスト編集です。プロセッサを外すのは inserts からオブジェクトを 1 つ消すことです。ミキサーは読み込み時にドキュメントを検証するため、記述ミスは「妙なミックス」ではなくロードエラーとして表面化します。
ストリップに書き込む faderDb は、シーンが読み込まれたあとにミキサーがライブの操作子として公開するフェーダーそのものです。下の各レーンはエンジン内の 3 本のストリップで、フェーダーやミュートを動かすとそのレーンの出力だけが変わります。scene.json で音を聴かずに行っている編集はこれで、ステップ 3 のレンダリングで初めて耳で確かめられます。
ステップ 3 — レンダリングする
sonare mix --scene scene.json \
--input drums=stems/drums.wav --input bass=stems/bass.wav \
--input gtr=stems/gtr.wav --input vox=stems/vox.wav \
-o mix.wav --json{"strip_count": 6, "sample_rate": 48000, "block_size": 512,
"rendered_samples": 494549, "output": "mix.wav"}この出力には、知らないと驚く点が 3 つあります。
--input の ID はストリップ名です。drums=stems/drums.wav は id が drums のストリップへ供給します。どの --input でも指定されなかったストリップには無音が入ります。2 本のリターンストリップにはそれが正しい挙動ですが、名前を打ち間違えたときもまったく同じことが起きます。strip_count は供給した本数ではなくシーンのストリップ総数なので、--input の数にリターンの数を足した値と突き合わせてください。
レンダリング結果はステムより長くなります。ここでのステムは 6 秒ですが、rendered_samples は 494549 フレーム、つまり 10.3 秒です。伸びた分は、プレートリバーブとステレオディレイが最後の音の後に鳴り残っている時間です。エフェクトリターンを外した同じシーンをレンダリングすると、ちょうど 288000 フレーム、入力と 1 サンプルも違わない長さが返ってきます。
ステレオは保たれます。モノラルのステムは左右両方に載り、ステレオのステムは自分の 2 チャンネルを保ち、出力は常にステレオペアです。3 チャンネル以上の入力は警告付きでダウンミックスされます。
ステップ 4 — マスタリング前にミックスダウンを点検する
マスタリングは、小さな破損を大きくします。先に探しておきます。
sonare repair mix.wav --detect --json--detect は計測と報告だけを行い、何も書き出しません。レンダリングのたびに気軽に走らせられます。返ってくるのはクリップ連続の長さとファイル中の割合、クリックとクラックルの件数、ノイズフロア、そして商用電源周波数の倍音列が信号に乗っているかどうかです。見つかったものは、マスターで直すよりステムで直すほうが安く済みます。整音側の手順は 録音素材をまとめて整音する にあります。
--explain と --detect は併用できません
--detect では修復ステージが 1 つも走らないため、説明すべき選択が存在しません。両方を渡すと使い方のエラーになります。各ステージが選ばれる理由を見たい場合は --detect を外してください。
ステップ 5 — 配信ターゲット向けにマスタリングする
sonare mastering mix.wav \
--assistant --preset edm --explain \
--target-platform streaming \
-o master.wav --report report.json --json{"mode": "assistant",
"input_lufs": -19.61, "output_lufs": -14.11, "applied_gain_db": 7.53,
"stages": ["eq.tilt", "dynamics.compressor", "saturation.exciter",
"stereo.imager", "loudness.optimize"],
"explanation": ["base preset: edm",
"target loudness and ceiling applied from AssistantConfig"],
"latency_samples": 0}--assistant はミックスダウンをプロファイリングし、--preset で指定したプリセットを出発点にします。省略時は streaming を使います。音源からジャンルを推測することはありません。--explain は、ベースプリセットとリペアやスピーチ固有の判断を出力します。--target-platform は streaming、youtube、broadcast、podcast、audiobook、cinema、club、cd を受け付けます。broadcast、podcast、club、cd は、対応する値を明示していない場合に固有のラウドネスと天井を適用します。それ以外の名前は受け付けますが、現在の値を変更しません。プロセッサ自体は マスタリングプロセッサ に、アシスタントの入出力の仕様は マスタリングアシスタント にまとまっています。
--target-platform は --assistant を要求します
プラットフォームターゲットはアシスタントへの入力であって、全体設定ではありません。プリセット実行に渡すと終了コード 3 でその旨を表示して止まります。固定プリセットでマスタリングする場合は、--preset <name> とそのプリセットの組み込みターゲットを使います。別のターゲットや天井を指定する場合は、--assistant --preset <name> と --target-lufs/--ceiling-db を組み合わせるか、mastering-suggest でチェーンを書き出して --chain-config でレンダリングします。
素材の役割が決まっている場合は、ベースプリセットを明示してください。sonare mastering-presets --json が 30 種すべてを列挙します。pop や jpop から speech、fieldRecording、さらに vinyl や shellac78 まで揃っています。--assistant --preset <name> に同じ名前を渡せます。vinyl などのリストア用プリセットはマスタリング用のラウドネスターゲットを持たないため、アシスタントでは拒否されます。
ステップ 6 — どの数値を信じるか
--report は、チェーン自身が取った処理前後の計測値を書き出します。
{
"before": { "integrated_lufs": -19.61, "true_peak_dbtp": -6.13, "loudness_range": 6.22 },
"after": { "integrated_lufs": -14.11, "true_peak_dbtp": -0.95, "loudness_range": 6.50 },
"applied_gain_db": 7.53,
"max_gain_reduction_db": -2.30,
"loudness_target_limited": false,
"band_energy_delta_db": [ /* 32 バンド */ ]
}レポートで目標と処理結果を確認します。loudness_target_limited は、要求した目標に実際に到達したかを示します。false は設定した制約の範囲で目標に到達したことを意味しますが、リミッターが動いていないことやマスターが完成したことは示しません。max_gain_reduction_db は実際の最大ゲインリダクションを示します。処理後のラウドネスと True Peak を読み、試聴してください。true の場合は設定したゲインと天井の制約下で目標に到達していないため、ミックス、目標、または天井を見直します。
レポートの数値はステレオの値です。別途 lufs で測った値は違います
Python CLI では mastering がステレオペアを最後まで保つ一方、lufs をはじめとする計測系コマンドは先にモノラルへダウンミックスし、標準エラー出力に警告を出します。そのため同じファイルに対して両者の値が食い違います。
sonare lufs master.wav --json
# warning: 2-channel input is downmixed to mono by this CLI command
# {"integrated_lufs": -17.57, ...} ← モノラルに畳んだ値
# report.json の after.integrated_lufs = -14.11 ← ステレオのマスターこの差はどちらかの誤りではありません。ITU-R BS.1770 はチャンネルのパワーを合算するため、2 チャンネルのプログラムは同じ素材をモノラルへ畳んだものより約 3 dB 高く出ます。さらにサイド成分がダウンミックスで失われる分が上乗せされます。モノラルファイルなら両者は完全に一致します。
ステレオの納品物では、ラウドネスと True Peak をマスタリングレポートから取ってください。lufs を使うのは納品物がモノラルのときです。ステレオのまま測りたい場合は Python API のステレオ用エントリポイントを使います。
スクリプトとして回す
ここで使ったコマンドはすべて --json を受け付け、失敗はすべて規定の終了コードで返ります。使い方の誤りは 2、不正なパラメータは 3、ファイルが見つからなければ 4、出力先に書けなければ 12 です。したがってスクリプト冒頭の set -euo pipefail だけで、実際に壊れた段階で連鎖を止められます。レンダリングされていないファイルをマスタリングしてしまう事故は起きません。
最後のステップをコミットごとの合否判定にする方法は 納品前チェックを CI で回す に続きます。
次に読むもの
- ミックスは近いが音色バランスが狙いどおりでない — リファレンス曲に寄せる。
- ステム自体が荒い — 録音素材をまとめて整音する。
- スクリプトではなくアプリに組み込みたい — ミキシングエンジン と マスタリングアシスタント が同じパイプラインを API で示しています。