Skip to content

ミキシングアシスタント ​

アシスタントは各トラックとトラック間の関係を計測し、ミキサーシーンを返します。シーンには入力トリム、フェーダー、パンと幅、補正 EQ、ダイナミクス、エフェクトバスとセンドが含まれ、各変更に理由が付きます。

提案はしますが、適用はしません

音声は処理も出力もされません。アシスタントはバッファを読み、パラメータを返すだけです。シーンは Mixer.fromSceneJson で明示的に適用します。統合した呼び出しはないため、適用前に呼び出し側で提案を確認・編集できます。

ストリップ・センド・バスがまだ馴染みのない語であれば、先に ミキシングの基礎 と ミキシングエンジン を読んでください。シーンのドキュメント自体は ミキシングシーン JSON にフィールド単位で規定されています。

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

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

  • 複数トラックに対してアシスタントを呼び出し、返ってくるシーン・トラックごとの計測値・説明文を読める。
  • 提案を実際のミックスへ、意図された 2 段階の手順として反映できる。
  • ある提案がどの計測に基づいているか、そしてアシスタントに知りようがなかったことは何かを言える。
  • アシスタントが何を「やらない」かを予測できる。とくに EQ の提案がなぜ少ないのかを理解できる。
  • 劣化した入力と拒否される入力を区別し、ビルドにアシスタントが含まれているかを判定できる。

よくある使い方 ​

トラックを計測し、理由を確認し、シーンを読み込みます。

typescript
import { suggestMixScene, Mixer } from '@libraz/libsonare-native';

const result = suggestMixScene({
  sampleRate,
  tracks: [
    { id: 'kick',   name: 'Kick',   left: kick },
    { id: 'bass',   name: 'Bass',   left: bass },
    { id: 'guitar', name: 'Gtr L',  left: guitarL, right: guitarR },
    { id: 'vocal',  name: 'Lead Vox', left: vocal },
  ],
  options: { targetTrackLufs: -18, suggestionStrength: 0.8 },
});

for (const line of result.explanation) console.log(line);

// この時点でまだ音声には何も起きていません。適用するのは次の行です。
const mixer = Mixer.fromSceneJson(JSON.stringify(result.scene), sampleRate);
python
import json

import libsonare as sonare

result = sonare.suggest_mix_scene(
    [
        sonare.MixTrackInput("kick", kick, name="Kick"),
        sonare.MixTrackInput("bass", bass, name="Bass"),
        sonare.MixTrackInput("guitar", guitar_l, guitar_r, name="Gtr L"),
        sonare.MixTrackInput("vocal", vocal, name="Lead Vox"),
    ],
    sample_rate=sample_rate,
    target_track_lufs=-18.0,
    suggestion_strength=0.8,
)

for line in result["explanation"]:
    print(line)

mixer = sonare.Mixer.from_scene_json(json.dumps(result["scene"]), sample_rate)
bash
sonare suggest-mix \
  --input kick=kick.wav --input bass=bass.wav \
  --input guitar=guitar.wav --input vocal=vocal.wav \
  --sample-rate 48000 \
  --params targetTrackLufs=-18,suggestionStrength=0.8 \
  --scene-out scene.json

トラック名で分類が変わることがあります

name はソース分類に使われます。計測で選ばれたクラスと一致する名前は確信度を上げます。別のクラスを示す名前は、計測結果がそのクラスと矛盾しないときに分類を変えます。計測で判定できるクラスは、そのクラスのルールも満たす必要があります。計測で区別できない keys、strings、lead、vocal、backing、fx は名前だけで指定できます。名前のない声、パッド、リード音は unknown のままです。名前に複数のクラスを示すヒント語があり、その間が空白・アンダースコア・ハイフン・ドットだけなら、最後のヒント語を採用します(Lead Vox は vocal、Synth Lead は lead)。複数のヒント語の間にほかの文字がある場合、名前はクラスを指定しません。キット全体を 1 トラックに録音した場合、ヒットの計測結果がキットのルールを満たせば drumKit に分類できます。名前だけではこのクラスを強制できません。

学習ではなくルールベース ​

ここには学習済みモデルも、統計的分類器も、学習されたパラメータもありません。ソース分類は計測特徴量 — 7 バンドの占有率から求めた対数周波数の重心、スペクトルのロールオフと平坦度、4 つにまとめたバンド占有率、低域・高域が優勢なヒットの割合、サステイン比、アタック密度、クレストファクター — に対する単層の決定表であり、その下流の判断はすべて、ソースで読める閾値を持ったルールです。行は上から順に試され、最初に一致した行が採用されます。境界をかろうじて越えただけの一致は、弱いラベルとして返すのではなく unknown として報告されます。

ルールの出発点となる数値はスタジオの慣習です。同じ量をプロの制作実務について測った査読付きの調査がある場合には、慣習をそのまま主張するのではなく、その調査と突き合わせてあります。リバーブのリターンレベル、リバーブのプリディレイ、リードボーカルの定位、広いパンの幅、コンプレッションレシオの周波数順は、いずれも P. Pestana, J. D. Reiss, Intelligent Audio Production Strategies Informed by Best Practices, AES 53rd International Conference on Semantic Audio, London, 2014 に由来します。

UI にとって何が重要か

ルールベースのアシスタントは、常に理由を言えます。explanation が生成された講評ではなく実用的な機能である理由はここにあります。各行は、変更を生んだルール自身が、その変更を生んだ瞬間に出力したものです。あとからまとめ直したり言い換えたりする処理はありません。

提案は何を根拠にしているか ​

判断の大半は 2 層の計測から下されます。ソース分類だけは任意のトラック名も読みます。その他のファイル情報、同順位を解くとき以外のトラックの並び順、その曲が何であるかという知識は関与しません。

トラック単位 では、1 回の STFT と 1 回の BS.1770 ラウドネス計測から、tracks[] として返るプロファイルが作られます。加えて内部には 2 つの計測が保持されます。バンドごとのエネルギー包絡(7 バンド × 全解析フレーム。衝突をフレーム単位で検証するためのもの)と、時間平均したパワースペクトル(全ビン、時間軸なし。カットをバンド内のどこに置くか、ハイパスのコーナーより下にどれだけエネルギーがあるかといった、バンドでは粗すぎる問いに答えるためのもの)です。分類は計測プロファイルと任意の名前だけを読みます。プロファイルにある生の spectralCentroidHz は意図的に特徴量から外されています。線形周波数の平均は静かなシンバルの余韻ひとつやサンプルレートで動いてしまうため、決定表はバンド占有率を対数周波数で重み付けした重心を使います。

トラック間 では 4 つのパスが走ります。いずれも、その結果を読むドメインが有効なときにだけ実行されます。

計測内容読むドメイン
バンド優勢度(bandDominance[])バンドごと・順序付きペアごとに、マスカー側がバンド合計エネルギーに占める比率 E_a / (E_a + E_b) を、両トラックがそのバンドのエネルギー下限を越えているフレームだけで平均したもの。0.5 が対等、1 は完全な占有。エネルギー比であってラウドネスモデルではなく、聴覚フィルタバンクも励起パターンも使いません。EQ、ダイナミクス
アライメント(alignment[])順序なしペアごとに、正規化相互相関の最大ピークのラグと極性。両者がもっとも同時に活発な 1 秒の窓で計測します。相関の絶対値が 0.5 以上のときだけ関連ありとみなし、それ未満には手を付けません。イメージ
イメージ占有(crowdedBands[])9 段階のパン位置にエネルギーが現状どう分布しているかのバンド別ヒストグラム。ステレオトラックは自身の左右バランスからバンドごとに配置されます。エネルギーが実効 1.7 位置未満に集中しているバンドは混雑と判定されます。イメージ
モノラルリスク(monoRisks[])相関が 0.30 未満、幅が 1.0 超(サイドのエネルギーがミッドを上回る)、または低域だけが広いステレオトラック。低域については、sub と low の 2 バンドがトラックのエネルギーの 10% 以上を占め、かつその 4 分の 1 以上がサイドである場合です。イメージ

各ドメインが読むもの ​

ドメイン読むものルール
構造ソースクラスのみ。トラック間の計測は読みません。バスの所属はクラスからの固定マップです。5 つの個別キットクラスと drumKit は drumBus を共有し、vocal と backing は voxBus を共有し、それ以外のクラスは専用のバスを持ち、unknown はどこにも属しません。サブグループは 2 トラック以上が対応するときだけ作られ、作られたサブグループにはユニティの VCA が付きます。プレートリバーブのバスとステレオディレイのバスは、センドテーブルにセンドレベルを持つクラスのトラックがあるときに提案されます。
ゲインIntegrated LUFS と targetTrackLufs の差。クラスは読みません。トラックごとに、絶対目標へ向けた静的な入力トリムを 1 つ。その後マスタートリムが、振幅加算した True Peak を mixBusHeadroomDbtp まで下げます。
バランスクラスとその確信度。クラス相対のフェーダーオフセットの固定テーブル(vocal がもっとも前で +5 dB、fx がもっとも後ろで −5 dB、drumKit は +0.5 dB)を、0.5〜1 の分類確信度でスケールします。参照はプロファイラのジャンル推定をキーにしていますが、テーブルは 1 つしかないため、現状はどのジャンルも同じオフセットに解決されます。
EQバンド優勢度、両トラックの bandOccupancy、平均スペクトル。一方のトラックがバンドのエネルギーの 0.65 以上(およそ 2:1)を、両者が鳴っているフレーム 32 以上にわたって占めるとき、カットが検討されます。譲る側は役割の優先順位が低いほうです。lead と kick が最上位、fx が最下位の固定順位であり、静かなほうが譲るのではありません。カットが提案されるのは、そのバンドが勝つ側自身のエネルギーの 40% 以上、譲る側の 7% 以下である場合だけです。深さは優勢度に応じて完全占有時の 6 dB まで増え、eqMaxCutDb で頭打ちになり、0.5 dB 未満は捨てられます。Q は 1.2 です。
ダイナミクスクラス、Integrated LUFS、クレストファクター、サステイン比、アタック密度。サイドチェインには低域の優勢度。クラスごとの出発点テーブルからコンプレッサーを 1 つ(drumKit はレシオ 3:1、スレッショルドオフセット −6 dB、アタック 15 ms、リリース 150 ms から始まります)。スレッショルドはトラック自身の計測ラウドネスからのオフセットで、絶対値は使いません。レシオ・アタック・スレッショルドは 12 dB を基準にクレストファクターで、リリースはサステイン比で動きます。kick snare tom percussion にはトランジェントシェイパー、vocal と backing にはレベルライダーとディエッサー。ゲートは kick snare tom だけ、しかもクローズマイクであることが疑いようのない場合(サステイン比 0.25 以下、クレストファクター 14 dB 以上、毎秒 0.5 オンセット以上、確信度がクラス上限付近)に限られます。サイドチェインは 1 種類のみ、kick の下で bass をダッキングします。最大 4 dB で、両者が鳴っているあいだ kick が sub と low のエネルギーの 0.55 以上を占めるときだけです。
イメージ配置にはクラス。それ以外にはアライメント、混雑度、モノラルリスク。クラスのテーブルでは kick snare bass lead vocal drumKit を中央に固定します。さらに、配置対象のトラックで 120 Hz 未満にエネルギーの半分以上がある場合は、クラスに関係なく中央に置きます。それ以外は左・右・左と交互に、対ごとに内側へ寄せながら、クラスごとの上限(guitar と percussion は 0.8、fx は 0.9、keys strings backing tom cymbal は 0.55、hiHat は 0.3)まで広げ、±0.9 を越えることはありません。広げられるクラスのメンバーが 1 つだけなら半分まで。混雑したバンドは広がりを 4 分の 1 増やします。関連があり極性が反転しているペアは後のトラックを反転し、関連がありラグのあるペアは先に届く側を遅らせます。モノラルリスクのトラックは幅を 0.7 まで狭め、低域が広い場合は 120 Hz の stereo.monoMaker を追加します。

確信度のゲート。 バランス、EQ、ダイナミクス、パン配置がクラスに基づいて動くのは、sourceConfidence が 0.5 以上のときだけです。それ未満のトラックは入力トリムを適用したレベルのまま、呼び出し側が置いた位置に留まります。構造は分類されたトラックをルーティングし、未分類のトラックはどのバスにも入りません。極性とディレイはクラスをまったく見ません。打ち消しは 2 つの信号のあいだで計測されるもので、それが何であるかは関係ないからです。

知りようのないこと ​

アシスタントは計測された信号の性質から判断しています。ミキシングエンジニアが持ち込むもののうち、信号に含まれていないものはすべて欠けています。

  • 曲の主役がどのパートか。 役割の優先順位はテーブルです。lead は vocal に勝ち、vocal は kick に勝ちます。フックを弾いているギターは guitar と分類され、それをなぞっているだけのボーカルに譲ります。それがリードなら、トラック名を lead にしてください。
  • アレンジ。 どちらもそのバンドで成り立っている 2 つのパートにはカットが出ません。キックとベースが 80 Hz を共有しているのはアレンジの問題であり、アシスタントは片方を痩せさせることでそれに答えたりはしません。
  • ジャンル、テンポ、好み。 レベル、パン、センドの数値はすべてスタジオの慣習に基づく出発点であり、素材から導かれたものではありません。tempoBpm を省略するとフォールバックテンポが使われるため、ディレイはトラック本来のテンポには同期しません。
  • キーボード、ストリングス、リード、ボーカル、バッキング、効果音。 ピアノと弾かれたギター、バッキングの重ねとリードボーカル、声と持続的なシンセパッド・リード音・管楽器を区別できる計測特徴量はありません。この 6 クラスは計測結果が矛盾しない場合にトラック名から指定でき、名前のない声・パッド・リード音は unknown のままです。
  • 時間。 すべての判断は曲全体に対する静的な設定 1 つです。オートメーションも、ダイナミック EQ も、セクションの認識もありません。サビでだけぶつかるパートは、曲全体のうちどれだけぶつかっているかで判断されます。
  • エフェクトが何を足すか。 マスターのヘッドルーム推定はドライのストリップを合計したものです。リバーブとディレイのリターンやインサートのメイクアップゲインは含まれていません。目標が −6 dBTP に置かれているのはそのためです。
  • 楽器を正しく聞き取れたか。 決定表は分離された生楽器のパートとキット全体のヒットステムを対象にしています。持続的な倍音成分からは、歌声とパッド、リード音、管楽器を区別できないため、計測結果が矛盾しない名前で vocal と lead を指定します。名前のない声・パッド・リード音は unknown のままです。1 トラックにまとまったキット全体は、重心が 40〜8000 Hz、サステイン比が 0.45 以下、アタック密度が毎秒 1.5 以上、低域と高域のヒット比率がそれぞれ 0.15 以上なら drumKit に分類できます。中央に置かれ、キット全体用のダイナミクスが適用されます。対応先は drumBus ですが、サブグループは 2 トラック以上が対応するときだけ作られるため、キットが 1 トラックだけなら直接マスターへ送られます。シーンを適用する前に tracks[].source を確認してください。
  • ラベルと計測が一致するか。 計測結果と矛盾しない名前は分類を変更できます。変更できない矛盾した名前は、計測されたクラスの確信度を 0.25 下げ、ゲートを下回ることがあります。これはエンジニアと計測が食い違ったときの設計上の応答です。どちらかの肩を持つのではなく、控えめにします。

エントリポイント ​

バインディングエントリポイント形
WASM(@libraz/libsonare)suggestMixScene(request)リクエストオブジェクト 1 つ({ tracks, sampleRate, options? })。同期。MixAssistantResult を返します。
Node(@libraz/libsonare-native)suggestMixScene(request)同じリクエストオブジェクトと同じ結果。同期。
Python(libsonare)suggest_mix_scene(tracks, *, sample_rate, ...)トラックは位置引数、オプションはすべて snake_case のキーワード。dict を返します。
C ABIsonare_mixing_assistant_suggest_scene_json(...)フラットな C 配列と SonareMasteringParam のリスト。JSON を char** json_out へ書き出します。

sampleRate はどのバインディングでも必須で、既定値はありません。

シーンだけを返す呼び出し ​

どのバインディングにも、同じリクエストを受け取り、Mixer.fromSceneJson が読むスキーマで直列化済みのシーンだけを返す対の関数があります。

バインディングシグネチャ
WASM / NodesuggestMixSceneJson(request: SuggestMixSceneRequest): string
Pythonsuggest_mix_scene_json(tracks, *, sample_rate, ...) -> str
C ABIsonare_mixing_assistant_suggest_scene_json(...) はシーンを json_out へ書き、sonare_mixing_assistant_suggest は完全な結果を書きます。どちらも sonare_free_string で解放してください。

コストは完全版の呼び出しと同じです。プロファイル、トラック間のパス、説明文はすべて計算されたうえで捨てられます。つまり、確認せずに適用する呼び出し側のための便宜であって、軽い経路ではありません。理由や計測値をユーザーに見せる段になったら、suggestMixScene を呼んで result.scene を自分で直列化してください。

ソースクラスの語彙 ​

ソースクラスが現れる場所 — tracks[].source、explanation の理由文、トラックがルーティングされたバス — はすべて、camelCase の識別子 16 個からなる固定の集合を使います。この語彙を扱う呼び出しが 2 つあります。

呼び出し返り値
mixSourceClassNames(): string[] / mix_source_class_names() -> list[str]ワイヤ順の識別子。unknown kick snare hiHat tom cymbal bass guitar keys strings lead vocal backing percussion fx drumKit。このリスト内の位置がその名前の序数です。
mixSourceClassFromName(name: string): number / mix_source_class_from_name(name) -> intそのリストにおける name の序数。含まれていなければ -1。照合は完全一致で、大文字小文字を区別します。'hihat' ではなく 'hiHat' です。

-1 を「該当なし」の値にしているのは意図的です。コア側の参照は未知の名前を unknown に畳み込みますが、これは序数 0 の実在するクラスなので不一致を報告できません。そのためバインディングは自前でインデックス検索を行っています。この対の用途は、ハードコードした写しではなく実行時のリストからクラス選択 UI や凡例を組み立てること、クラス文字列を処理する前に検証すること、文字列比較ではなく安定した序数でプロファイルを並べ替えたりまとめたりすることです。アシスタントを含まないビルドでは、mixSourceClassNames は空配列を返し(C ABI では空文字列、Python では RuntimeError)、どの名前も -1 に解決されます。

これらはアシスタントが報告する識別子です。分類器がトラック名の中で反応する語のリストではありません。そちらは別の部分文字列語彙(bassdrum、vox、gtr、rhodes など)で、たまたまこの識別子の大半を含んでいますが、公開はされていません。

オプション ​

オプション集合は意図的にフラットです。入れ子のグループも、ドメインごとのサブオブジェクトもありません。すべて省略可能で、省略したキーはそもそも転送されないため、コア側の既定値がそのまま効きます。

キー(JS)型既定値意味
targetTrackLufsnumber-18.0トラックごとの Integrated ラウドネス目標。読み込んだ集合の平均ではなく絶対値なので、静かなセッションはそのまま放置されず引き上げられます。
suggestionStrengthnumber1.0[0, 1]。レベル系の判断すべてをスケールします。
eqMaxCutDbnumber4.01 つのカットの上限。
mixBusHeadroomDbtpnumber-6.0マスターの静的トリムの目標値。
tempoBpmnumber0.00 はトランスポート側のフォールバックテンポを選びます。20〜400 の外にある正の値はクランプされず拒否されます。
enableStructurebooleantrueバス構成とルーティング。
enableGainbooleantrue入力トリムとラウドネスの調整。
enableBalancebooleantrueフェーダー。
enableEqbooleantrue補正 EQ。
enableDynamicsbooleantrueコンプレッション。
enableImagebooleantrueパンと幅。
enableHighPassbooleanfalseトラックごとのハイパス。既定でオフです(後述)。
nFftnumber2048共通の STFT ジオメトリ。
hopLengthnumber512

Python は同じキーを snake_case で受け取ります(target_track_lufs、enable_high_pass、n_fft など)。C ABI は camelCase の綴りを SonareMasteringParam のエントリとして受け取ります。

suggestionStrength: 0 は「何も提案しない」ではありません

レベル系の判断はゼロへ向かってスケールしますが、バス構成、ルーティング、極性、アライメントディレイ、低域のモノラル化はスケールしません。これらは構造的な決定であり、半分だけ適用されたルーティンググラフはミックスとして成立しないからです。したがってゼロでも、バスとセンドと補正を含んだシーンが返ります。あるドメインについて何も提案させたくない場合は、そのドメインごとオフにしてください。

無効化したドメインは計測もされません

enableEq: false は「かぶりは計測するがカットは提案しない」ではありません。そのドメインは評価自体が行われないため、そこへ供給される計測も実施されません。ドメインを切るのは呼び出しを軽くする手段であって、助言抜きで解析結果だけを得る手段ではありません。

返ってくるもの ​

MixAssistantResult は 4 つのフィールドを持ちます。

フィールド型内容
sceneMixSceneDocumentMixer.fromSceneJson が読むドキュメント。スキーマは ミキシングシーン JSON です。
tracksMixAssistantTrackProfile[]トラックごとの計測値。入力順。
mixMixAssistantMixProfileトラック間で計測されたもの。
explanationstring[]適用順に並んだ理由。

tracks — 各トラックが何であるか ​

フィールド意味
stripId、name渡した値そのまま。
source分類されたソース。unknown kick snare hiHat tom cymbal bass guitar keys strings lead vocal backing percussion fx drumKit のいずれか。
sourceConfidence決定表による分類の確信度。
usable計測できなかったトラックでは false。使えないトラックには、ゼロの提案ではなく提案そのものが付きません。
exclusionReasonusable が false のとき、その理由を文章で。
channelCount、durationSecバッファの形。
integratedLufsnumber | null。計測値が -Infinity になる場合(ゲート済みブロックが 1 つもないトラック)は null です。JSON にその数値表現がないためです。
truePeakDb、crestFactorDbピークレベルと、ピークと RMS の差。
spectralCentroidHz、spectralFlatness明るさとノイズらしさ。
attackDensity、sustainRatioどれだけ打撃的か、どれだけ持続的か。
bandOccupancyそのトラック自身のエネルギーに占める、バンドごとの比率。

bandOccupancy は 7 つのバンド名をキーに持ちます。EQ の理由文はすべてこのバンド名で書かれるため、範囲を覚えておく価値があります。

バンド範囲
sub20〜60 Hz
low60〜250 Hz
lowMid250〜500 Hz
mid500 Hz〜2 kHz
highMid2〜6 kHz
high6〜12 kHz
air12 kHz〜ナイキスト周波数

mix — トラック間で起きていること ​

フィールド内容
trackCountプロファイルされたトラック数。
bandDominance[]どのトラックがどのトラックを、どのバンドで、どれだけマスクしているか(masker、maskee、band、ratio、validFrames)。
alignment[]ペアごとのタイミングと極性(reference、target、lagSamples、correlation、polarityOpposed)。
crowdedBands[]セッション全体が奪い合っているバンド。
monoRisks[]モノラルで一部が消えるストリップ(correlation、width、wideLowEnd)。

説明文の読み方 ​

explanation は各差分が適用されるたびに組み立てられ、あとからまとめ直されることはありません。したがって上から順に読めば、シーンがどう構築されたかをそのまま追えます。すべてのドメインが無効な場合や、使えるトラックが 1 つもない場合は空になります。

アシスタントの慎重さは文面に現れます。EQ の理由は、周波数を示し、その周波数が計測されたものなのかバンド中心へのフォールバックなのかを述べ、両方のパートの比率を挙げます。

text
carved 3.2 dB at 1247 Hz (measured overlap in mid, which vocal needs at 41.6%
of its energy and guitar can spare at 12.4% of its own) out of guitar to make
room for the parts it shares those bands with

カットが eqMaxCutDb に当たった場合は、衝突が解決したふりをせず、そのことを明記します。

text
…; the mid cut was held at the 4.0 dB ceiling, so the collision is only partly resolved

これで、編集可能なコントロールに計測された周波数と双方のエネルギー比率を添えられます。

提案をミックスにする ​

提案は、シーンを確認・編集してからミキサーへ読み込む 2 段階で適用します。

typescript
const result = suggestMixScene({ sampleRate, tracks });
// …result.explanation を表示し、result.scene をユーザーに編集させる…
const mixer = Mixer.fromSceneJson(JSON.stringify(result.scene), sampleRate);
python
result = sonare.suggest_mix_scene(tracks, sample_rate=sample_rate)
# …result["explanation"] を表示し、result["scene"] をユーザーに編集させる…
mixer = sonare.Mixer.from_scene_json(json.dumps(result["scene"]), sample_rate)
bash
sonare suggest-mix --input kick=kick.wav --input vocal=vocal.wav --scene-out scene.json
sonare mix --scene scene.json --input kick=kick.wav --input vocal=vocal.wav -o mixed.wav

suggest-mix は両方のコマンドラインフロントエンドにあります。2 つめのコマンドはそうではありません。mix --scene は Python 専用なので、提案されたシーンを最後までレンダリングするシェルパイプラインは、後半に PyPI の sonare CLI を必要とします。

アシスタントが書き出すシーンは、レーン、フェーダー、センド、バスからなる通常のミキサーシーンです。下のデモはそのミキサーシーンを表示します。

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

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

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

控えめな EQ と enableHighPass ​

アシスタントが提案する EQ は、初めて読む人の予想よりずっと少なく、それは機能の欠落ではなく設計です。

バンドが削られるのは、一方のパートがそのバンドで成り立っていて、もう一方がそこを譲れる場合だけです。判定は、各トラック自身のエネルギーに占めるそのバンドの比率で行われます。双方がそのバンドで成り立っている 2 つのパートがぶつかった場合、何も提案されません。どちらかの土台を抜いてしまわないカットが存在しないからです。キックとベースがどちらも 80 Hz に居るのはアレンジの問題であり、そうでないふりをする EQ は、どちらかを痩せさせるだけです。

カットが正当化された場合、その中心周波数はバンドの中点ではなく、バンド内で計測されて決まります。ここでのバンドは最大 2 オクターブに及ぶため、中点が計測された重なりから大きく離れることがあります。理由文には、周波数を計測したのか中点へフォールバックしたのかが書かれています。

enableHighPass がオフである理由 ​

enableHighPass の既定値は false です。有効にすると、クラスがカットオフ周波数を決め、低域エネルギーの計測値がハイパスを提案するかどうかを決めます。確信度のゲートも適用されるため、すべてのトラックに一律でハイパスを追加することはありません。

有効にすると、コーナー周波数はクラスから、フィルターするかどうかは計測から決まります。

クラスカットオフ周波数
kick、bass、fx、drumKitフィルターしません。前の 2 つは低域そのもので、キット全体にはキックが含まれ、効果音には下に潜る音域がありません。
keys50 Hz
strings、tom60 Hz
guitar75 Hz
vocal、lead、snare80 Hz
backing100 Hz
percussion150 Hz
hiHat、cymbal400 Hz

フィルターが提案されるのは、コーナーより下にあるトラックのエネルギーの比率が 0.5% 以上 10% 以下 のときだけです。0.5% 未満なら取り除くものがなく、10% を超えていればその内容はパート自身の素材 — ダウンチューニングしたギター、ベースラインを弾いているキーボード — であり、フィルターは実際の音を削ってしまいます。理由文には計測された比率が引用されます。EQ ドメインの確信度ゲートを下回るトラックにはハイパスも出ません。コーナーはクラスから読むため、分類器が確信を持てないクラスでフィルターすべきではないからです。

有効にする前に確認すること ​

  • スタジオのステムですか。 フィルターの根拠はステージの振動、ハンドリングノイズ、近接効果です。クローズマイクで録ったスタジオ素材には、フィルターに値するほどのそれらは通常なく、調査が示したのもその点です。
  • 分類は確信できていますか。 コーナーは tracks[].source に従います。tom と分類されたスネアは 80 Hz ではなく 60 Hz のコーナーを引き継ぎます。コーナーを信頼する前に sourceConfidence を確認してください。
  • 曲の一部でだけ音域より下を弾くパートはありませんか。 比率はトラック全体で計測されます。一時的に 1 オクターブ下がるパートは、平均で 10% 未満にとどまり、フィルターの条件を満たすことがあります。計測された比率を確認してください。
  • あとで低域を足し直す予定はありませんか。 フィルターされたパートの下にミックスの後段でサブレイヤーを重ねると、アシスタントが挿したハイパスと効果が相反します。フィルターはオフにしておき、トラックごとに決めてください。
  • enableEq はオンですか。 ハイパスは EQ ドメインの一部なので、enableEq: false なら enableHighPass の値にかかわらずスキップされます。

はっきり述べておくべき 2 つの挙動 ​

劣化した入力はエラーではありません。 トラックが 1 つもない、無音のトラック、計測できないほど短いトラック、サンプルレートが正でない、バッファに NaN や無限大が混じっている — いずれも例外を投げません。呼び出しは成功し、空のシーン、空の説明文、そしてトラックごとの exclusionReason が返ります。

exclusionReason原因
track has no samplesバッファが null、またはフレーム数がゼロ。
track sample rate is not positive
track has non-finite samplesNaN または無限大。無音と取り違えず、非有限値として報告されます。
track is shorter than the minimum measurable duration
track is silent
track has no energy in the analysis bands

唯一拒否される入力は、トラック ID の重複です。 これは吸収されずに InvalidParameter を発生させます(Node では RangeError、Python では SonareValueError)。同じ ID のストリップを 2 つ持つシーンは、ミキサーが読み込みを拒否するシーンだからです。ここで受け入れても、後段のより原因の分かりにくい場所で失敗するだけです。

利用可否 ​

アシスタントは切り離し可能なビルド単位です。BUILD_MIXING_ASSISTANT の既定は ON で、有効にすると BUILD_MIXING も強制的に有効になります。Node ネイティブアドオンの通常ビルドは既存の CMake キャッシュ値にかかわらず BUILD_MIXING_ASSISTANT=ON を強制します。一般の CMake ビルドでは引き続きこのオプションを OFF にできます。一方 SONARE_WASM_ANALYSIS_ONLY はこれを強制的に OFF にするため、解析専用の WASM モジュールはアシスタントを含みません。

シンボルではなくケーパビリティを見てください

typescript
import { capabilities } from '@libraz/libsonare';

if (capabilities().features.mixingAssistant) {
  // 呼び出して安全
}

typeof suggestMixScene === 'function' は判定として成立しません。アシスタントを含まないビルドでもエントリポイントは登録されたままで、呼ぶと例外になります(C ABI では SONARE_ERROR_NOT_SUPPORTED を返します)。つまりシンボルはどちらの場合も存在します。

関連ページ ​