Skip to content

マスタリングアシスタント API

libsonare は、レンダリング済み音声だけでなく判断根拠を扱いたいアプリ向けに、JSON を返すマスタリング補助 API を 3 つ提供します。ローカル DSP 解析のみで動作し、アップロードも外部モデルも隠れたプリセットも使わず、UI 表示やレポート保存に使える構造化 JSON を返します。

「LUFS」「トゥルーピーク」「クレストファクター」「トーナルバランス」に馴染みがなければ、先に マスタリングとは?メーターの読み方 を読んでください。本ページは用語を前提に JSON の契約に集中します。

「アシスタント」は自動仕上げボタンではない

ここでのアシスタントは、音源を測定し、なぜその処理が妥当そうかを JSON で説明する補助 API です。実際の音作りは、提案をユーザーが確認・調整し、別のレンダリング API に渡して行います。

最初に組み込む場合は、次の順で読むと分かりやすくなります。

  1. masteringAudioProfile(...) で、元音源の状態をユーザーに見せる。
  2. masteringAssistantSuggest(...) で、編集可能なマスタリングチェーンを初期入力する。
  3. ユーザーが提案を確認・調整してからレンダリングする。
  4. masteringStreamingPreview(...) で、配信プラットフォームが音量をどう扱うか説明する。

アシスタントの位置

アシスタントは説明と提案を行い、あなたの代わりに決定はしません。よい流れは、ソースをプロファイル→方向を提案→ユーザーが調整→レンダリング→ストリーミング各社での再生をプレビュー、です。最終判断は耳で行ってください。JSON は UI の材料であって、耳の代わりではありません。

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

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

  • ソースプロファイル、チェーン提案、レンダリング、配信プレビューを別々の UI ステップとして扱える。
  • JSON を返す 3 つのヘルパーをパースし、どのフィールドが測定値で、どれが提案かを区別できる。
  • ユーザー調整を残したまま、アシスタントの提案を masteringChain のレンダリングへ渡せる。
  • これらのヘルパーがリモート自動マスタリングではなく、ローカル DSP 解析である理由を説明できる。

3 つの API の概要

ステップJavaScriptPython戻り値
ソースを調べるmasteringAudioProfile(samples, sr)mastering_audio_profile(...)測定プロファイル
チェーンを提案masteringAssistantSuggest(samples, sr, params)mastering_assistant_suggest(...)完全なチェーン設定 + 根拠
配信をプレビューmasteringStreamingPreview(samples, sr, platforms)mastering_streaming_preview(...)プラットフォーム別の正規化

3 つとも JSON 文字列を返します — JSON.parse(JS)または json.loads(Python)で解析してください。スキーマは C・Node・Python・WASM バインディングで同一です。PyPI 版 sonare CLI でも sonare mastering-profilesonare mastering-suggestsonare mastering-streaming として同じ JSON を標準出力に得られます。

3 つのヘルパーは、それぞれ別の問いに答えます。

ヘルパー答える問い測定か提案か
masteringAudioProfile「この元音源はどんな状態か?」測定のみ
masteringAssistantSuggest「どんなチェーンを出発点にするとよさそうか?」プロファイルに基づく提案
masteringStreamingPreview「配信側でどれくらい上げ下げされるか?」ラウドネス測定に基づく配信シミュレーション
typescript
import { init, masteringAudioProfile, masteringAssistantSuggest, masteringStreamingPreview } from '@libraz/libsonare';
await init();

const profile    = JSON.parse(masteringAudioProfile(samples, sampleRate));
const suggestion = JSON.parse(masteringAssistantSuggest(samples, sampleRate, { targetLufs: -14, ceilingDb: -1 }));
const preview    = JSON.parse(masteringStreamingPreview(samples, sampleRate, [
  { name: 'YouTube',  targetLufs: -14, ceilingDb: -1 },
  { name: 'Podcast',  targetLufs: -16, ceilingDb: -1 },
]));
python
import json
import libsonare as sonare

profile    = json.loads(sonare.mastering_audio_profile(samples, sample_rate=sr))
suggestion = json.loads(sonare.mastering_assistant_suggest(
    samples, sample_rate=sr, params={"targetLufs": -14, "ceilingDb": -1}))
preview    = json.loads(sonare.mastering_streaming_preview(samples, sample_rate=sr, platforms=[
    {"name": "YouTube", "targetLufs": -14, "ceilingDb": -1},
    {"name": "Podcast", "targetLufs": -16, "ceilingDb": -1},
]))
bash
# assistant 専用 CLI はないため、同じターゲットの解析・レンダーを CLI で行う。
sonare lufs source.wav --json
sonare mastering source.wav --target-lufs -14 --ceiling-db -1 -o master.wav

短いクリップ: profile と suggest には実際のスペクトル成分が必要

masteringAudioProfilemasteringAssistantSuggest は STFT ベースの解析全体を実行し(既定の nFft は 2048)、解析窓を満たせないほど短いクリップでは SonareError を投げます。しきい値は 2 つあります。~512 サンプルより短いバッファはそのまま例外になり、1 窓ぶん(nFft、既定 2048 サンプル)より短いバッファは、意味のあるプロファイルを取るにはスペクトル成分が足りません。安全のため 1 窓ぶんを下限として守ってください。masteringStreamingPreview はラウドネスを測るだけなので、空でない音声バッファと空でないプラットフォームのリストさえあれば、どんなバッファでも受け付けます。UI から短い録音やファイル選択を渡すときは、profile/suggest の呼び出しを最小長チェックで守り、isSonareError を使った trycatch で囲んでから、バッファを渡してください。

typescript
import { masteringAudioProfile, isSonareError } from '@libraz/libsonare';

const MIN_ANALYSIS_SAMPLES = 2048; // 既定の解析窓 1 つ分(nFft)
if (samples.length < MIN_ANALYSIS_SAMPLES) {
  // 短すぎてプロファイルできない — スキップするか、解析前にパディングする
} else {
  try {
    const profile = JSON.parse(masteringAudioProfile(samples, sampleRate));
    // …
  } catch (err) {
    if (isSonareError(err)) {
      // 「クリップが短すぎる/スペクトル成分がない」旨を UI に表示する
    } else {
      throw err;
    }
  }
}

masteringAudioProfile — ソースを測る

入力の読み取り専用の要約です。どれだけ大きいか、スペクトル全体にエネルギーがどう広がっているか、どれだけダイナミックか、どのジャンルに似ているか。何も処理しません。

任意の params は数値で、JS 風/Python 風のどちらの名前も受け付けます: nFft/n_fft(既定 2048)、hopLength/hop_length(既定 512)、truePeakOversample/true_peak_oversample(既定 4)。

トゥルーピークでオーバーサンプリングする理由

デジタルのピークは固定された点でサンプリングされますが、実際の波形はそのサンプルとサンプルの間で高くなることがあります。オーバーサンプリングは信号をより高いレートで(ここでは 4 倍で)測り直し、こうしたサンプル間ピークを捉えます。これにより truePeakDb が、コンバーターが実際に出力する値を反映します。倍率を上げるほど正確になりますが、CPU 負荷も増えます。

この結果は、元音源を説明するために使います。合否判定ではありません。たとえば「すでに大きい」「暗い」「密度が高い」「アタックが多い」といった事実を UI に出すためのものです。

することしないこと
ラウドネス、トゥルーピーク、クレストファクター、スペクトル、ダイナミクス、ジャンル候補を測る音声を変更しない
レンダリング前に UI へ表示する材料を返す最終設定を単独では決めない
json
{
  "durationSec": 2,
  "bpm": 89.5,
  "bpmConfidence": 0.24,
  "loudness": {
    "integratedLufs": -8.71,
    "lraLu": 0,
    "truePeakDb": -2.41,
    "crestFactorDb": 5.76
  },
  "spectral": {
    "subRmsDb": 6.37, "lowRmsDb": 40.35, "lowMidRmsDb": 13.26, "midRmsDb": 23.56,
    "highMidRmsDb": -1.96, "highRmsDb": -1.99, "airRmsDb": -1.92,
    "centroidHz": 5806.83, "flatness": 0.0035, "rolloffHz": 15386.5
  },
  "dynamics": { "shortTermLufsStd": 0, "attackDensity": 3, "sustainRatio": 1 },
  "genreCandidates": [
    { "name": "hipHop", "score": 0.70 },
    { "name": "edm",    "score": 0.65 },
    { "name": "pop",    "score": 0.45 }
  ]
}
グループフィールド意味
loudnessintegratedLufs全体のラウドネス(EBU R128)
lraLuラウドネスレンジ — 時間方向のラウドネス変動
truePeakDbサンプル間 トゥルーピーク
crestFactorDbピーク対 RMS の差 — 大 = パンチ、小 = 密度(クレストファクター
spectralsubRmsDbairRmsDb帯域ごとのエネルギー(sub → air)。暗い/明るいバランスの把握に
centroidHzスペクトルの「重心」— 明るさの目安
flatness0 = トーン的、1 = ノイズ的
rolloffHzエネルギーの大半が収まる周波数
dynamicsattackDensityトランジェントの密度
sustainRatio持続的か過渡的か
genreCandidates[{name, score}]最も近いスタイル。先頭が提案のベースプリセットになる

スペクトル帯域の読み方

*RmsDb のフィールドは低域から高域へ並びます: sub(重低音)→ low/lowMid(低音と温かみ)→ mid(芯、ボーカル)→ highMid/high(存在感、明瞭さ)→ air(高域のきらめき)。これらを見比べると、ミックスが暗め(低域が強い)か明るめ(air が強い)かが分かります。

ラウドネスレンジ・アタック密度・サステイン比とは?
  • ラウドネスレンジ(LRA、単位 LU) — 体感ラウドネスが曲全体でどれだけ変動するか。値が大きいほど、はっきり静かになったり大きくなったりします(ダイナミックなクラシック曲)。小さいほど、ほぼ一定の音量です(密度の高い EDM マスター)。「LU」(ラウドネス単位)は LUFS と同じ尺度で、絶対値ではなく振れ幅として測ります。
  • アタック密度 — 1 秒あたりおおよそ何回、鋭いノート/ドラムのオンセットが起こるか。高い=忙しく打撃的、低い=まばらまたは持続的。
  • サステイン比(0〜1) — 素材が長く伸びた音(1 に近い)に支配されているか、短いバーストやアタック(0 に近い)に支配されているか。アタック密度とは別に(オンセットではなく RMS 包絡から)測られますが、傾向としては逆向きに動きます。
  • 短時間 LUFS の標準偏差 — 瞬間ごとのラウドネスがどれだけ揺れるか。値が大きいほどレベルが落ち着かず、ゼロに近いほど非常に安定して保たれています。

これらは測定値であって評価ではない

crestFactorDb が 5.8 でも「悪い」わけではなく、信号を記述しているだけです。プロファイルはソースを理解し、何を変えるか判断するために使い、合否スコアとしては使わないでください。

masteringAssistantSuggest — チェーンを提案

プロファイルを土台に、完全なマスタリングチェーンと、人が読める根拠を提案します。第 3 引数に意図(targetLufsceilingDb など)を渡します。

意図として受け付けるキーは targetLufs/target_lufsceilingDb/ceiling_dbenableRepair/enable_repairpreferStreamingSafe/prefer_streaming_safespeechMonoAmount/speech_mono_amount です。

任意の意図キーの働き

enableRepair は、ソースに不具合があるときにクリーンアップ段(declick、denoise など)を有効にします。preferStreamingSafe は、最大音量よりも配信向けの安全なシーリングとターゲットへ提案を寄せます。speechMonoAmount(0〜1)は、小型スピーカーやモノラルスピーカーでの聞き取りやすさのために、スピーチの低域/中央成分をモノラル寄りにまとめます。

このヘルパーは「根拠つきのプリセット生成」と考えると分かりやすいです。そのままレンダリングできる完全な出発点を返しますが、想定ワークフローはユーザーが編集できる形です。

出力の一部使い方
chainConfig.paramsUI コントロールへ展開する、または masterAudio の上書き値として渡す
explanationなぜ各段が有効化・調整されたかを表示する
genreCandidatesベースプリセットを選ぶ、または候補として表示する
profileレポート内で提案を自己完結させる
json
{
  "chainConfig": {
    "version": 1,
    "params": {
      "eq.tilt.enabled": 1,
      "eq.tilt.tiltDb": -0.5,
      "dynamics.transientShaper.enabled": 1,
      "dynamics.compressor.enabled": 1,
      "dynamics.compressor.thresholdDb": -18,
      "saturation.tape.enabled": 1,
      "spectral.airBand.enabled": 1,
      "maximizer.truePeakLimiter.enabled": 1,
      "maximizer.truePeakLimiter.ceilingDb": -1,
      "loudness.enabled": 1,
      "loudness.targetLufs": -14,
      "loudness.ceilingDb": -1
    }
  },
  "explanation": [
    "base preset selected from top genre candidate: hipHop",
    "target loudness and ceiling applied from AssistantConfig",
    "air band enabled because the spectral profile is dark",
    "transient shaper enabled for dense attacks"
  ],
  "genreCandidates": [ { "name": "hipHop", "score": 0.70 } ],
  "profile": { "integratedLufs": -8.7, "truePeakDb": -2.43, "crestFactorDb": 5.75, "...": "平坦化したプロファイル" }
}
フィールド意味
chainConfig.params提案チェーン全体をフラットなドット記法キー(stage.processor.param)で表したもの。*.enabled1/0masterAudio の上書き値が受け付けるキーと同一なので、提案をそのままレンダリングできます。
explanation各判断の平易な理由。UI に表示して選択を透明にしてください。
genreCandidatesプロファイルと同じ順位付きスタイル。先頭がベースプリセット。
profileソースプロファイルの平坦化コピー。提案が自己完結します。
params オブジェクトは既定チェーン全体

上の例は省略版です。実際の params マップは既定チェーンのすべてのパラメータを含みます — リペア全段(declick、declip、decrackle、dehum、dereverb、denoise)、EQ、ディエッサー、トランジェントシェイパー、コンプレッサー、マルチバンド、サチュレーション(tape/exciter)、エアバンド、ステレオ、トゥルーピークリミッター、ラウドネス段 — それぞれ全パラメータと enabled フラグつきです。アシスタントはプロファイルに基づき enabled を切り替え、いくつかの値を調整し、残りは既定のままにします。マップは疎な差分ではなく、上書き可能な完全スナップショットとして扱ってください。

提案をマスターとしてレンダリングする

chainConfig.paramsmasterAudio の上書きキーを使うので、提案のレンダリングは 1 回の呼び出しです。先頭のジャンル候補をベースプリセットにし、params マップ全体(上に示した数キーだけでなく、チェーン全体です)を上書き値として渡します。

typescript
const suggestion = JSON.parse(masteringAssistantSuggest(samples, sampleRate, { targetLufs: -14, ceilingDb: -1 }));
const basePreset = suggestion.genreCandidates[0].name;        // 例 "hipHop"

const mastered = masterAudio(samples, sampleRate, basePreset, suggestion.chainConfig.params);
// mastered: { samples, sampleRate, inputLufs, outputLufs, appliedGainDb, stages }
python
suggestion = json.loads(sonare.mastering_assistant_suggest(
    samples, sample_rate=sr, params={"targetLufs": -14, "ceilingDb": -1}))
base_preset = suggestion["genreCandidates"][0]["name"]        # 例 "hipHop"

mastered = sonare.master_audio(
    samples, sample_rate=sr,
    preset_name=base_preset,
    overrides=suggestion["chainConfig"]["params"],
)
# mastered: samples, sample_rate, input_lufs, output_lufs, applied_gain_db, stages
bash
sonare mastering source.wav --target-lufs -14 --ceiling-db -1 -o master.wav
sonare mastering-processors

suggest と render の間でユーザーに編集させる

意図したパターンは、chainConfig.params を編集可能な UI コントロールへ展開し、ユーザーに値を調整させてから、編集後のマップを masterAudio へ渡すことです。explanation[] の文字列は、各段を有効にした理由を短く添える表示に向いています。

masteringStreamingPreview — 配信をプレビュー

ソースと対象プラットフォームのリストを与えると、各社のラウドネス正規化があなたの音声をどう再生するかを報告します。レンダリングのに、どのサービスで音量が下げられるか、シーリングに余裕があるかを確認できます。

入力 platformsStreamingPlatform オブジェクト(nametargetLufsceilingDb)です。

このヘルパーは「配信側が何をするか」のレポートとして読むと分かりやすくなります。

状態意味
normalizationGainDb が負プラットフォームが音量を下げる
normalizationGainDb が正プラットフォームが音量を上げる可能性がある
ceilingRisktrueそのゲインでピークがプラットフォームのシーリングを超える可能性がある
json
{
  "platforms": [
    { "name": "YouTube", "integratedLufs": -8.70, "truePeakDb": -2.43, "normalizationGainDb": -5.30, "ceilingRisk": false },
    { "name": "Podcast", "integratedLufs": -8.70, "truePeakDb": -2.43, "normalizationGainDb": -7.30, "ceilingRisk": false }
  ]
}
フィールド意味
integratedLufs / truePeakDb測定したソース値(どのプラットフォームでも同じ)
normalizationGainDbターゲットに合わせるためプラットフォームが適用するゲイン。負は音量を下げられることを意味する
ceilingRisk正規化が信号をプラットフォームのシーリングを超えて押し上げる場合 true

ストリーミングでは大きい=良いではない

−8 LUFS のマスターは YouTube で「大きく」はなりません。プラットフォームは normalizationGainDb(ここでは −5.3 dB)を適用して全員をほぼ同じラウドネスに揃えるので、過度なコンプはラウドネス上の利点なしにダイナミクスを犠牲にするだけです。配信ターゲットラウドネスマッチング を参照してください。

METERS · LOUDNESSIDLE
ラウドネス計測 — LUFS・トゥルーピーク・レンジ

バーは再生中の瞬時ラウドネスを追い、パネルは時間ごとのラウドネスです。インテグレーテッド LUFS は全体を表す一つの数値、トゥルーピークはサンプル間も含む本当の上限、LRA はラウドネスの動く幅を表します。ウィンドウを切り替えると、速い瞬時メーターと滑らかな短時間メーターを比べられます。

ウィンドウ

関連