音声圧縮を1から — 聴こえない音を捨てる技術
MP3はなぜ1/10以下のサイズで「同じように」聴こえるのか。聴覚マスキング→MDCT→ビット割り当ての順で音声コーデックの原理を前提知識ゼロから解説し、MP3/AAC/Opusの使い分けまでつなげる。
要点
- 音声圧縮の本質は、量子化雑音を聴覚マスキングの閾値の下に隠すことである。
- CD品質の生の音は44,100×16×2で約141万ビット/秒、1分あたり約10MBになる。
- MDCTは50%オーバーラップとエイリアシング打ち消しで、データを増やさずブロックの継ぎ目を消す。
- 急な立ち上がりで雑音が前に漏れるプリエコーには、エンコーダがブロック長を短く切り替えて対処する。
- 配信・保存にはAAC、双方向リアルタイムにはOpus、レガシー互換が必要な場面にはMP3を使う。
生の音は思ったより重い
音は空気の圧力が細かく揺れる波です。マイクはこの揺れを電圧に変え、ADコンバータが一定間隔で電圧を測って数値の列にします。CD品質なら1秒間に44,100回、1回の測定を16ビットで、それをステレオ2チャンネル分。掛け算すると 44,100 × 16 × 2 = 約141万ビット/秒、1分あたり約10MBです。5分の曲で50MB——スマホで音楽が聴き放題になる世界は、このままでは来ませんでした。
MP3の定番設定である128kbpsは、この約1/11です。ZIPのような可逆圧縮(元に完全に戻せる圧縮)では、音声波形はほとんど縮みません。波形の数値列は一見ランダムに近く、繰り返しパターンがほぼ無いからです。桁を1つ落とすには「完全に戻せる」ことを諦め、何かを捨てるしかない。では何を捨てるか。音声コーデックの答えは明快で、人間の耳に聴こえない音を捨てます。
比喩: 電車の中のささやき声
静かな部屋でなら聴こえるささやき声も、走る電車の轟音の中では消えます。声は物理的には存在しているのに、知覚されない。これが聴覚マスキングです。耳の内部では、大きな音が鳴ると基底膜という器官のその周波数付近の感度が占有され、近くの小さな音の情報が脳に届かなくなります。
コーデックが利用する性質は2つあります。
- 周波数マスキング: 大きな音は、周波数が近い小さな音をかき消す。1kHzの大きなトーンが鳴っている間、そのすぐ隣の周波数の小さな音は聴こえない。影響の届く範囲は「臨界帯域」と呼ばれる耳の解像度の単位で決まる。
- 時間マスキング: 大きな音の直後(そしてわずかに直前)の小さな音も聴こえない。シンバルが鳴った直後の数十ミリ秒、耳は「回復中」で細かい音を拾えない。
つまり人間の耳は高性能ですが、穴だらけの測定器です。音声コーデックはこの穴の地図——心理音響モデル——を信号ごとに計算し、穴の中にだけ劣化を押し込みます。
直感: 捨てるのではなく「雑音を隠す」
正確に言うと、コーデックは音を選んで「削除」しているわけではありません。やっているのは数値を粗く丸めること(量子化)です。丸めればビット数は減りますが、丸めた分だけ元との差、つまり量子化雑音が生まれます。音声圧縮の本質は、この雑音をマスキングの傘の下に隠すことです。
心理音響モデルは周波数帯域ごとに「ここでは雑音がこのレベル以下なら聴こえない」というマスキング閾値を計算します。信号の強さと閾値の比(SMR: signal-to-mask ratio)が大きい帯域は丁寧に(多ビットで)、小さい帯域は雑に(少ビットで)量子化する。全帯域で雑音が閾値の下に収まれば、データは1/10でも耳には元と区別がつかない——これが「聴こえない音を捨てる」の正体です。
この「周波数に変換して、粗く丸めて、劣化を知覚の穴に押し込む」という三段構えは、JPEGが画像でやっていることとまったく同じ構造です。丸めの荒さを上げると何が起きるかは、画像のほうが目で見えるぶん体感しやすいので、ここで触ってみてください。
仕組み: MDCT — 継ぎ目なくブロックを刻む
周波数帯域ごとに丸めの粗さを変えるには、まず波形を周波数成分に分解する必要があります。音は刻々と変わるので、波形を短いブロック(たとえば2048サンプル≒43ミリ秒)に切って、ブロックごとに変換します。
ところが素朴にぶつ切りにすると、量子化で丸めたあと復元したときにブロックの境界で波形が食い違い、周期的な「ブツブツ」というノイズになります。これを解決するのが MDCT(修正離散コサイン変換) で、現代の音声コーデックのほぼすべてに載っている心臓部です。
アイデアは2つです。
- ブロックを50%ずつ重ねて切る。各ブロックは隣と半分共有し、端がなだらかになる窓関数を掛ける。境界の情報が必ず2つのブロックに分かれて入るので、継ぎ目が消える。
- 重ねて切ると普通はデータが2倍になるが、MDCTは2Nサンプルの入力からN個の係数しか出さない。復元時に隣のブロックと足し合わせると、この間引きで生じる歪み(エイリアシング)が符号が逆で正確に打ち消し合うよう設計されている。この性質をTDAC(時間領域エイリアシング打ち消し)と呼ぶ。
定義はこうです。
記号を1つずつ読むと: は窓の中の 個の音声サンプル、 は端をなだらかに落とす窓関数、 は「 番目の周波数の型紙」、 はその型紙と波形がどれだけ似ているかの度合い、 は「各位置で掛けた結果を全部足して1つの数にせよ」という指示です。記号を一切使わずに言い換えると: 波形を短く切り出し、端をなだらかに絞ってから、高さの違う純音を1つずつ当てて「この高さはどれくらい入っているか」を書き留める——やっているのはそれだけです。返ってくるのは周波数ごとの強さの一覧で、コーデックはこの一覧を粗く丸めていきます。入力の半分の個数しか出てこないのがポイントで、50%重ねて切っても総データ量は増えません(臨界サンプリング)。
MDCTには弱点が1つあります。カスタネットのような急な立ち上がりがブロックの後半にあると、量子化雑音はブロック全体に薄く広がるため、アタックの「前」の静かな部分に雑音が漏れて「シュワッ」と聴こえます。プリエコーと呼ばれる代表的な圧縮アーティファクトです。対策として、エンコーダは急変を検出するとブロック長を一時的に短く切り替えます(AACなら2048サンプル→256サンプル)。長いブロックは周波数の解像度が高く、短いブロックは時間の解像度が高い——この使い分けがエンコーダの腕の見せ所です。
ビットを割り当てて、詰める
MDCTの係数は、心理音響モデルが帯域ごとに決めたステップ幅で量子化されます。閾値に余裕がある帯域は大胆に丸められ、係数の多くはゼロになります。ゼロだらけの列はハフマン符号などのエントロピー符号化でさらに縮みます。変換・量子化・エントロピー符号化——この三段が、非可逆圧縮すべてに共通する骨格です。
コードで書くMDCT
numpyなら本体は数行です。
import numpy as np
def mdct(x, N): # x: 2Nサンプル(隣のフレームと半分重ねて渡す)
n = np.arange(2 * N)
w = np.sin(np.pi / (2 * N) * (n + 0.5)) # sin窓(端がなだらかに0へ)
k = np.arange(N)[:, None]
basis = np.cos(np.pi / N * (n + 0.5 + N / 2) * (k + 0.5))
return basis @ (w * x) # N個の係数
窓を掛けて、コサインの型紙たちと内積を取る——式(1)の直訳です。実際のコーデックはこれを高速アルゴリズム(FFTベース)で計算しますが、結果は同じです。
MP3・AAC・Opus: 3世代の使い分け
ここまでの部品(心理音響モデル+MDCT+量子化+エントロピー符号化)は3つとも共通です。違いは世代と得意分野です。
- MP3(MPEG-1 Audio Layer III・1990年代前半に標準化): 32帯域のフィルタバンクとMDCTを組み合わせた折衷設計。効率では後発に譲りますが、再生できない機器がほぼ存在しない互換性が最大の資産で、関連特許もすでに失効しています。
- AAC(MPEG-2/4): MP3の直系後継。純粋なMDCT構成になり、長短ブロック切替や柔軟な符号化テーブルで同レートでの音質を改善。Apple製品・YouTube・地上波放送・HLS配信の既定コーデックで、「作って配る」用途の標準です。
- Opus(IETF RFC 6716・2012年): 通話向けのSILK(声帯の仕組みを模した線形予測)と音楽向けのCELT(MDCT)を1つに統合したコーデック。フレームが2.5〜60ミリ秒と短く低遅延、内部は48kHz動作、ロイヤリティフリーで、WebRTCの標準音声コーデックです。
まとめると: 保存・配信はAAC、通話・会議・ゲームのボイスチャットなど双方向リアルタイムはOpus、レガシー互換が必要な場面だけMP3、が現在の基本線です。
現場ではこう使う
誰が・いつ: 配信サービスのメディアパイプライン担当が音質ティア(ビットレートの段階)を設計するとき。WebRTCで通話機能を組み込むアプリ開発者がコーデックパラメータを決めるとき。ポッドキャストや動画の書き出し設定を決める編集者も毎回これに触れています。
触るもの: 実務の入り口はほぼ ffmpeg です。
ffmpeg -i in.wav -c:a libopus -b:a 96k -application audio out.opus # 配信・音楽
ffmpeg -i in.wav -c:a aac -b:a 192k out.m4a # AAC配布用
ffmpeg -i in.wav -c:a libmp3lame -q:a 2 out.mp3 # LAMEのVBR品質指定
-b:a がビットレート、LAMEの -q:a は品質ベースVBR(数値が小さいほど高品質)、libopusの -application は voip/audio で心理音響の重み付けが変わります。
事故になる落とし穴:
- 世代劣化。非可逆→非可逆の再エンコードは、量子化雑音が毎回上乗せされて累積します。素材は必ず可逆(WAV/FLAC)で保管し、非可逆化は配信直前の1回だけにする。「MP3をもらってAACに変換して納品」は静かな事故です。
- Bluetoothでもう1段。ユーザーがワイヤレスイヤホンで聴く時点で、端末がSBCやAAC等でもう一度エンコードします。届く音は常に2段圧縮である前提で品質ティアを決めること。
- サンプルレートの罠。Opusは内部48kHzなので、44.1kHz素材はリサンプルされます。動画(48kHzが標準)と混ぜる案件なら、収録から48kHzで統一すると事故が減ります。
- エンコーダ遅延とギャップレス再生。MDCTの重なりとエンコーダのパディングで、出力の先頭にはわずかな無音が挿入されます。ループ素材や曲間なしのアルバムで「プチッ」と鳴ったら、まずこのパディング情報(gapless metadata)の欠落を疑ってください。
面接で問われたら: 「MP3はなぜ1/10にできるのか」への答えの筋道は、①可逆では縮まない → ②耳にはマスキングという穴がある → ③MDCTで周波数に変換し、穴の下に量子化雑音を隠し、ゼロだらけの係数をエントロピー符号化で詰める、の3段です。
まとめ
- 音声圧縮の本質は「音を消す」ではなく「量子化雑音を聴覚マスキングの閾値の下に隠す」こと
- MDCTは50%オーバーラップ+エイリアシング打ち消しで、データを増やさずブロック境界の継ぎ目を消す
- プリエコー対策のブロック長切替が、変換系コーデック共通の急所
- 使い分けの基本線は、配信・保存=AAC、リアルタイム=Opus、互換性=MP3
次は、この骨格に「時間方向の予測」を足すと動画になる話——動画圧縮を1からへ進んでください。
コメント
コメントにはログインが必要です