Skip to content

録音素材をまとめて整音する ​

荒いテイクの束がドライブに転がっている。ゲインが高すぎてクリップしたもの、アースが悪くて 50 Hz や 60 Hz の商用電源ハムが乗ったもの、そしてほとんどのテイクは頭と尻に 1、2 秒の無音が余分に付いている。どのテイクにどの問題があるのか、ファイルごとには分かっていない。このページはまずそれを測り、その測定結果が求める処理だけを当て、両方を記録に残します。

この作業は「全部に declip を掛ける」ことではありません。何も壊れていないのに修復ステージを走らせるのは、ただ品質を削るだけです。declip は一度もクリップしていないサンプルまで再合成しますし、dehum は商用電源周波数に乗っている本物の音楽的な成分までノッチで削ります。修復アシスタントの価値は、根拠のないステージを断る点にあり、--explain はそれが実際にそうなったかを確かめる手段です。

ライブラリ API を使う手順と欠陥ごとの処理は、音声リペア、ノイズ・ハム除去、クリック・クラックル・クリッピング修復、デリバーブで説明しています。

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

このページを読むと、次のことができるようになります。

  • テイクに触れる前にまず何が壊れているかを測り、アシスタントが選んだ、あるいは選ばなかった各修復ステージの理由を読める。
  • repair・trim-silence・declip をファイルごとのバッチ処理としてスクリプト化し、クリーンなテイクと、何を見つけ何をしたかの JSON 記録の両方を書き出せる。
  • repair を動かす 3 つの方法 —— 計測して選ばせる・名前付きプリセット・フィールド単位の上書き —— を区別し、それぞれがどんな場面向けかを判断できる。5 つあるレストレーション用プリセットのうち、どれをまず試すべきかも含めて。
  • 修復には必ず失うものがあることを踏まえ、ある数値をゼロまで追い込むことが割に合わない場面を見分けられる。

全体のスクリプト ​

以下がこの作業の全体です。テイクの入ったフォルダに対して実行し、そのうえで各段階が何をしているか、なぜそうなっているかを読んでください。

bash
#!/usr/bin/env bash
set -euo pipefail

mkdir -p clean reports

for raw in raw/*.wav; do
  name="$(basename "${raw%.wav}")"

  # 1. Measure the damage. Writes nothing -- safe to run on every take.
  sonare repair "$raw" --detect --json > "reports/${name}.before.json"

  # 2. Let the assistant choose only the stages step 1's evidence calls for.
  sonare repair "$raw" -o "clean/${name}.wav" --explain --json > "reports/${name}.repair.json"

  # 3. Trim the dead air every take was padded with.
  sonare trim-silence "clean/${name}.wav" -o "clean/${name}.trimmed.wav" --json \
    > "reports/${name}.trim.json"

  # 4. Measure again. This is the verification step, not a separate audit.
  sonare repair "clean/${name}.trimmed.wav" --detect --json > "reports/${name}.after.json"

  # 5. Fold the four records into one per-file report.
  python3 - "$name" <<'PY'
import json
import sys

name = sys.argv[1]
before = json.load(open(f"reports/{name}.before.json"))["defects"]
repair = json.load(open(f"reports/{name}.repair.json"))
trim = json.load(open(f"reports/{name}.trim.json"))
after = json.load(open(f"reports/{name}.after.json"))["defects"]

record = {
    "file": name,
    "detect_before": before,
    "stages_chosen": repair["stages"],
    "reasons": repair.get("explanation", []),
    "output_gain_db": repair["output_gain_db"],
    "trimmed_duration": trim["duration"],
    "detect_after": after,
}
with open(f"reports/{name}.json", "w") as f:
    json.dump(record, f, indent=2)
PY

  rm -f "reports/${name}.before.json" "reports/${name}.repair.json" \
        "reports/${name}.trim.json" "reports/${name}.after.json"
done

2 本のテイク —— 一方はハードクリップと商用電源ハム、もう一方はハムとクリックだけ —— に対して実行すると、こう出力されます。

text
reports/take-01.json  ->  repair.declip, repair.declick, repair.decrackle, repair.dehum
reports/take-02.json  ->  repair.declick, repair.decrackle, repair.dehum

take-02 にはクリップがなく、そのステージ一覧に declip はありません。この 1 行に、先に測ることの意義がすべて表れています。アシスタントは 2 本の異なるテイクを見て、実際に 2 通りの違うものを直しました。

バッチ整音のパイプライン
音声テイク一式 (WAV)repair --detectアシスタントが選択repair --explain修復済みテイクtrim-silenceクリーンなテイクrepair--detect (検証)ファイルごとのrecord.json
検出器はテイクごとに 2 回走ります。1 回目は何を直すか決めるため、2 回目は直せたことを確かめるためです。

ステップ 1 — 触る前にまず測る ​

bash
sonare repair take-raw.wav --detect --json

--detect は計測して報告するだけです。ファイルは何も書き出さず、-o を渡すことすらできません。だからこそフォルダ内のテイク全部に対して、何も決める前に気軽に走らせられます。オプション全体は CLI リファレンス — repair にあります。

json
{
  "mode": "detect",
  "defects": {
    "click_count": 1,
    "click_rejected": 4989,
    "crackle_per_second": 0.759,
    "clip_sample_fraction": 0.1139,
    "clip_run_count": 3137,
    "clip_longest_run_samples": 45,
    "clip_flat_level": 0.99997,
    "noise_floor_dbfs": -120.0,
    "hum_peak_found": true,
    "hum_fundamental_hz": 50.0,
    "hum_fundamental_prominence": 18.62,
    "hum_harmonics": 2,
    "late_decay_ratio_db": -4.86
  }
}

このテイクでは 4 つの数値だけでほぼ全体像が分かります。clip_sample_fraction はサンプルの 11.4% がクリップしていることを示し、1.0 に近い clip_flat_level はそれが単に大きいのではなく平らに張り付いていることを示します。ちょうど 50 Hz に商用電源の倍音が 18.6 dB の突出度で乗っており、crackle_per_second はノイズフロアが単なる静かなヒスではなく広帯域のクラックルであることを示します。click_count はわずか 1 ですが、click_rejected(4989)は検出器が「クリックらしく見えて実はそうではない」と判断したイベントの数です。検出器が何かを見逃したと結論する前に、この数字を知っておく価値があります。

ステップ 2 — アシスタントに選ばせる ​

bash
sonare repair take-raw.wav -o take-clean.wav --explain --json
json
{
  "mode": "assistant",
  "stages": ["repair.declip", "repair.declick", "repair.decrackle", "repair.dehum"],
  "explanation": [
    "declip: runs of samples sit pinned at one level",
    "declick: impulsive runs stand out from their neighbours",
    "decrackle: samples depart from the local median",
    "dehum: a prominent harmonic series sits on a mains frequency"
  ],
  "output": "take-clean.wav",
  "output_gain_db": -1.87
}

各ステージは、検出器が使ったのと同じ語彙で、それを発火させた根拠そのものを述べています。denoise と dereverb は入っていません。このテイクの noise_floor_dbfs と late_decay_ratio_db がそれらを求めなかったので、一度も走っていないということです。output_gain_db は修復ステージではありません。declip はクリッパーが削ったピークを再構築するため、その結果はフルスケールを超えて膨らみがちです。そこでサンプルごとにクランプするのではなく、ファイル全体に 1 つのゲインを当てて収めます。

--explain と --detect は併用できません

--detect では修復ステージが 1 つも走らないため、説明すべき選択が存在しません。両方渡すと不正なパラメータとして拒否されます。理由を見たいなら --detect を外し、数値だけでよいなら --explain を外してください。

このテイクでは denoise が走らなかったので、ここまでの出力には denoise の仕事が一度も出てきません。下のデモで聴けます。きれいな持続コードに広帯域のヒスノイズを乗せ、リペアステージがそれを取り除きます。Compare を切り替えると両者を聴き比べられます —— ゲインには手を加えていないので、動くのはヒスノイズだけです。アルゴリズムを切り替えると、持ち上がった高域のフロアをどれだけ下げられるかの違いが分かります。

A/B PROCESS · DENOISEIDLE
デノイズ修復 — 修復前と修復後

きれいなコードに広帯域のヒスノイズを加えています。修復前後の音とスペクトルを比較し、デノイズの方式を切り替えてください。追加の音量合わせは行っていません。ノイズ低減は、音楽の立ち上がりや音色にも影響することがあります。FLOOR は高域の平均低減量(dB)です。

比較
アルゴリズム

ステップ 3 — 無音の余白をトリムする ​

bash
sonare trim-silence take-clean.wav -o take-trimmed.wav --json
json
{"length": 350528, "duration": 7.303, "threshold_db": -60.0, "n_fft": 2048, "hop_length": 512}

テイクは頭と尻の余白を含めて 7.9 秒でしたが、トリム後は 7.303 秒です。つまりおよそ 0.6 秒の無音が取れました。--threshold-db(既定値 −60 dB)は、あるフレームを無音と数える閾値です。ファイル自身のピークを基準にした値で同じ切り方をしたい場合は --top-db も受け付けます。トリムは修復とは無関係で、テイクに修復が必要だったかどうかに関係なく走ります。パイプラインの中で修復のあとに置いているのは、切り落とすのが無音の余白であって、余白に見えるだけのクリップした端ではないようにするためです。

ステップ 4 — 複数テイクを共有する無音で切る ​

問題が 1 本の欠陥テイクではなく、そこそこ良い出来のテイクが何本もあることもあります。同じフレーズに 3 回挑戦し、それぞれ入りと抜けのタイミングが少しずつ違っていて、それらをスライスごとに聴き比べたい、という場合です。ステップ 3 の trim-silence を各テイクに個別に掛けても役には立ちません。テイクごとに別々の切り出し位置が決まってしまうため、境界がファイルごとに違う場所へ来てしまい、「同じ」スライスのつもりが実は 3 種類の違うスライスになります。

split-silence はまさにこの用途のために、一度に複数のファイルを受け取れます。1 本目のテイクは位置引数で渡し、同じ部分の残りのテイクは繰り返し指定できる --input で加えます。

bash
sonare split-silence take1.wav --json
json
[{"start_sample": 28160, "end_sample": 116224}, {"start_sample": 153088, "end_sample": 241152}, {"start_sample": 287232, "end_sample": 394752}]

テイク 1 本だけでは、フレーズごとに 1 つずつ、3 つの非無音区間が報告されます。trim-silence が見つけるのと同じ形を、1 つのトリム後の長さではなくサンプル範囲として表したものです。同じ部分の残り 2 本のテイクを加えます。

bash
sonare split-silence take1.wav --input take2.wav --input take3.wav --json
json
[{"start_sample": 23040, "end_sample": 121344}, {"start_sample": 147968, "end_sample": 246272}, {"start_sample": 282624, "end_sample": 399872}]

最初の区間は 28160–116224 から 23040–121344 へと広がります。この広がり方こそがこの機能の要点です。報告される区間は各テイク自身の非無音区間の 和集合 で、接するところは統合されます。つまり切り出しが起きるのは全テイクが同時に無音になっている場所だけで、どのテイクのフレーズの途中にもかかりません。各テイクを自分自身の区間で切ってしまうと、あるテイクの早い入りが別のテイクのスライスではアタックを削り取ることになります。3 本まとめて和集合で切れば、どのテイクも全テイクが共有する無音の分だけを失い、それ以上は失いません。

split-silence は -o を一切受け付けません。区間を報告するだけのコマンドで、--output を渡すと使用法エラーで終了します。共有区間を実際の音声にするのが --write-takes PREFIX で、テイクごと・区間ごとに 1 ファイルずつ、PREFIX{take:02d}_{interval:03d}.wav(どちらの番号も 1 始まり)という名前で書き出します。

bash
sonare split-silence take1.wav --input take2.wav --input take3.wav --write-takes comp --json

3 本のテイクと 3 つの共有区間から、9 個のファイル comp01_001.wav から comp03_003.wav までが書き出されます。1 つの区間の中では、テイクをまたいでもファイルの長さが揃います —— 区間 1 ならどのテイクでも 98304 サンプルです。テイク自身はサンプル単位で揃っているわけではないのに、です。和集合の境界より前に終わっているテイクは、短く切られるのではなくその分だけ無音でパディングされます。だからこそ comp01_001.wav・comp02_001.wav・comp03_001.wav は横並びで聴き比べられます。開始位置も長さも同じ、同じフレーズの 3 つの異なる演奏です。

ここで やらないこと も明確にしておきます。最良のテイクを選ぶことはなく、クロスフェードもせず、フレーズ途中のタイミングのずれも直しません。途中で走ったり遅れたりしているテイクは、切り出した後もそのまま走ったり遅れたりします。手元に残るのは判断材料としての揃ったスライスであって、完成したコンピングではありません。

全テイクのサンプルレートが一致している必要があり、これは何よりも先にチェックされます。

bash
sonare split-silence take1.wav --input take2.wav --input t3_44.wav --json
# Error: take sample rate differs: t3_44.wav is 44100 Hz, the first take is 48000 Hz

これは使用法レベルの不一致で(終了コード 3)、このページの他の場所で不明なパラメータキーを渡したときと同じ分類です。

--top-db は各テイク自身のピークが基準で、絶対値ではありません

同じ --top-db でも、ノイズの多いテイクほど見つかる無音は少なくなります。閾値は固定の dBFS ではなく、そのテイク自身のピークを基準にしているからです。take1.wav は(repair --detect で測ると)ノイズフロアが約 −99.9 dBFS で、3 つのフレーズの間にはっきりした隙間が残ります。ファイルの他の部分は変えずにそのノイズフロアを約 −69.1 dBFS まで持ち上げると、同じテイクの結果はファイルのほぼ全体を覆う 1 つの区間に潰れてしまいます —— 閾値が、無音と呼べるほど静かな場所をもう見つけられなくなったということです。「このくらいのヘッドルームがあれば安全」という一律の dB はありません。まず noise_floor_dbfs を確認し、ノイズフロアが高いテイクは --top-db を下げて帳尻を合わせるのではなく、コンピングの前にステップ 2の修復アシスタントに通してください。

ステップ 5 — 検証: もう一度 --detect を走らせる ​

検出器はそのまま受け入れ試験にもなります。結果に対して再度走らせ、比較してください。

bash
sonare repair take-clean.wav --detect --json
フィールド修復前修復後
clip_sample_fraction0.11390.000227
click_count10
crackle_per_second0.7590
hum_fundamental_prominence18.627.94
hum_harmonics21
noise_floor_dbfs−120.0−101.5

クリップ・クリック・クラックルはいずれもほぼゼロまで下がります。ハムの突出度は半分以下に下がりますがゼロにはならず、noise_floor_dbfs はむしろ上がります。declip の LPC 再構築と 2 つのインパルス系修復ステージが、手を入れたサンプルにごくわずかな広帯域の痕跡を残すためです。どちらも実行の不具合ではなく、ありのままの修復結果です。ハムの数値をさらに追い込むことがたいてい割に合わない理由は、次の節で扱います。

検出器そのものが答え

「うまくいったか」を確かめる別コマンドはありません。何を直すか決めたのと同じ --detect が、直せたかどうかを、同じ数値・同じ単位で証明します。

ステップ 6 — すでに欲しいものが分かっているとき ​

アシスタントに判断させたくない場合の逃げ道が 2 つあります。

名前付きプリセット は計測を飛ばし、修復ステージをマスタリングプリセット自身の設定からそのまま持ってきます。sonare mastering-presets --json が 30 種すべての名前を列挙しますが、そのうち 5 つはレストレーション用のプリセットで、修復ステージだけを持ちます。どれを選ぶかはファイルの測定値ではなく、手元の素材について最初から分かっていることで決める振り分けです。

手元にあるものまず試すプリセット走るステージ(チェーン順)
LP のトランスファー —— 溝の摩耗によるクリックとポップ、表面のクラックル、その下に敷かれたノイズフロアvinyldeclick・decrackle・denoise
78 回転 SP 盤(シェラック盤)のトランスファー —— LP より幅の広いポップと、より密で高い表面ノイズshellac78vinyl と同じ 3 ステージを、それぞれ強めに。長いクリックまで対象にし、クラックルの閾値を下げ、ノイズ低減を深くしたもの
カセットやオープンリールのトランスファー —— 一定の広帯域ヒスだけtapeHissdenoise
屋外やロケでの収録 —— ヒス、50 Hz や 60 Hz からずれた電源ハム、周囲の空間の響きfieldRecordingdehum(適応追従)・denoise・dereverb
スマートフォンやノート PC のボイスメモ —— 自前の AGC に当たったクリップ、高いマイクのノイズフロア、話者がいた部屋の響きvoiceMemodeclip・denoise・dereverb

5 つのどれにも当てはまらない —— たとえばハムとクリックはあるがヒスはないテイク —— なら、いちばん近いものを選んではいけません。--preset を外して上の計測して選ばせる経路に根拠から決めさせ、特定のステージだけ足りなければ、次のフィールド単位の上書きで補ってください。各プリセットが設定するパラメータとその既定値はマスタリングプロセッサに、プリセットを選び分ける考え方はマスタリングプリセットの選び方にまとまっています。

--preset と --explain も併用できません。理由は --detect のときと同じで、プリセットのステージは計測で選ばれるのではなく直接名指しされるため、--explain が報告することが何もないからです。

bash
sonare repair take-raw.wav --preset voiceMemo -o take-voicememo.wav --json
json
{"mode": "preset", "preset": "voiceMemo", "stages": ["repair.declip", "repair.denoise", "repair.dereverb"]}

このテイクに voiceMemo を当てると declip・denoise・dereverb が走りますが、dehum は一度も走りません。ファイルの測定値に関係なく、そのプリセットがそもそも持っていないステージだからです。結果を再度検出すればそれが確認できます。hum_fundamental_prominence は 20.5 と、元の 18.6 からほとんど変わっていません。プリセットが正しい選択になるのは、素材の素性が最初から分かっていて(電話のボイスメモ、フィールド録音)毎回同じ固定処理を当てたい場合です。ファイル自身の証拠に判断させたいなら向いておらず、それは計測して選ばせる経路に --explain を付ける方法の役目です。

レコード系のレストレーションが何を取り除き、何を別のステージに残すかは、下のデモで聴けます。クリップはレストレーション用プリセットが想定する傷みを乗せたピアノのターンアラウンドで、電源ハム、表面ノイズとヒス、まばらなクリックとクラックルが入っています。ここで当てているのは古典的なデリバーブ 1 段だけなので、落ちるのはノイズの土台と滲んだ余韻です。クリックとハムはそのまま残ります —— それらは declick と dehum の担当だからです。

A/B PROCESS · DEREVERBIDLE
レストレーション — 傷んだレコードの修復前と修復後

ピアノのクリップには、ハム・ヒスノイズ・クリック・クラックルが入っています。このデモは古典的なデリバーブだけを適用し、拡散した持続成分を減衰させます。専用のデクリックやハム除去は適用していません。追加の音量合わせをせずに、スペクトルと音を比較してください。ノイズだけでなく、音楽の余韻や音色も変わることがあります。FLOOR は高域の低減量(dB)です。

比較

フィールド単位の上書き は、どちらの経路を取っていても --params repair.<stage>.<field>=value,... で 1 つのパラメータだけを変えます。アシスタント自身の dehum ステージをさらに強めてみると、ステップ 5 のトレードオフがそのまま見えます。

bash
sonare repair take-raw.wav -o take-clean-harm.wav --params repair.dehum.harmonics=6 --json

ノッチが追う倍音の数をアシスタント自身の 2 から 6 に上げても、残留量はほとんど動きません。hum_fundamental_prominence は既定値での実行の 7.94 に対して 7.88 と、実行ごとの測定誤差に埋もれる程度の差です。動くのは output_gain_db のほうで、既定値の −1.87 dB に対して −3.4 dB まで下がります。ほぼ同じ結果を得るために、波形の作り直しがほぼ倍になったということです。dehum を「ゼロまで追い込みたい」という衝動は、いつもこういう結果になります。残留量は目減りしていく一方、コストは実際に、そして測定可能なかたちで積み上がります。整音・入力系コントロール のグロッサリーページも denoise について同じことを言っています。ノイズを少し残すほうが、ドラムがにじんだり水っぽいアーティファクトが出たりするより良い、と。ノッチフィルタが本物の音楽的成分と共有する基音を削ってしまう場面にも、同じ抑制が当てはまります。

ステップ 7 — 単機能の逃げ道: declip ​

repair 自身の declip ステージは --params 以上には単独で調整できません。特定のテイクに対して控えめすぎる、あるいは強すぎる場合は、専用の制御を持つ単機能コマンド declip を使います。

bash
sonare declip take-raw.wav -o take-declip-only.wav --clip-threshold 0.95 --json
json
{"clip_threshold": 0.95, "lpc_order": 36, "iterations": 2, "lpc_blend": 0.65, "output": "take-declip-only.wav"}

--clip-threshold は、フルスケールにどこまで近づいたサンプルをクリップとみなすかです(修復アシスタントはもう少し控えめな既定値を使います)。--lpc-order・--iterations・--lpc-blend は、線形予測による再構築で失われた波形をどれだけ積極的に組み立て直すか、それとも生の(クリップした)信号をどれだけ混ぜ戻すかを決めます。ここに手を伸ばすのは --detect で declip こそが手で調整する価値のあるステージだと分かってからにしてください。それ以外は何も直しません。

ステップ 8 — ステレオのテイク ​

ここまではすべてモノラルのテイクで実行しました。repair --detect(そして修復チェーン自体)はステレオファイルをモノラルにダウンミックスし、標準エラー出力にその旨を出します。

text
warning: 2-channel input is downmixed to mono by this CLI command; use the stereo library API for channel-preserving processing

ステレオの損傷レポートはモノラル合算を示します

これはモノラル合算だけを表します。合算で見える欠陥は有用な手がかりですが、合算がきれいでも各チャンネルがきれいとは限りません。左右非対称のクリップ、逆極性のハムやクリック、片チャンネルだけの欠陥は隠れることがあります。チャンネルを保ったまま調べて修復するには、この CLI パスではなく Python API のステレオ用エントリポイントを使ってください。

スクリプトとして回す ​

このページのコマンドはすべて --json を受け付け、失敗はすべて規定の終了コードで返ります。--detect と --explain の不正な組み合わせや -o の欠落、認識できないパラメータキーは 3、存在しないファイルは 4 です。したがってバッチスクリプト冒頭の set -euo pipefail だけで、実際に壊れたテイクのところで実行を止められます。それを黙ってスキップしたまま「バッチはきれいに終わった」と報告することはありません。

次に読むもの ​