サンプリング周波数の変換 [プログラム]
とある事情により、LPCMデータでサンプリング周波数を
48kHz → 44.1kHz → 48kHz
に変換するという、バカな状況が起こってしまいました。
テレビ放送を録画したもの(48kHz)からffmpegで音声を取り出しながらサンプリング周波数変換(48kHz->44.1kHz)を行ない、CD-Rに書き込んだのです(44.1kHz)が、それが多くなったのでCD-Rからffmepgを使ってサンプリング周波数変換(44.1kHz->48kHz)を行ない、DVD-R(DVDプレーヤーでDVDとして再生が可能なもの; 48kHz)に焼き直したのです(最後のものは、Roxio Toastで簡単にできます)。
しかし、聞いてみると、どうも音が濁っています。CDプレーヤーの再生回路よりもDVDプレイヤーの音声再生回路が悪いのかとも思いましたが、それにしては、普通のDVDの音声がマトモ過ぎますから、DVDプレイヤーの音声再生回路は犯人ではなさそうです。
となると、「音が汚なすぎるのは、もしかしたらffmpegのせいではないか?」と私が思ってもまったく不思議ではありませんでした。
さて、ffmpeg以外にも、SoX(Sound eXchange)でもサンプリング周波数の変換ができます。ただ、コマンドラインのオプションの使い方がよくわからず、検索でヒットしたページのとおりに指定したら、そんなオプション知らん!と言われたり、とにかく使いこなすのが大変なので、遠ざかっていました。今回、SoXを使って、サンプリング周波数の変換などを試してみましょう。ついでに、AudacityやToast(Toast10; mac版 10.0.7)も試してみます。
調査は次の手順で行ないます。
まず、基準となる音声ファイルを作ります(たとえば、10秒の1kHzの正弦波、48kHzと44.1kHzでサンプリング)。
次に、これをffmpegとsoxとaudaciy(Best Sinc Interpolator,Dither None)とToastでサンプリング周波数の変換をします。
さらに、サンプリング周波数の変換をした音声ファイルを、もう一度サンプリング周波数の変換をして、元のサンプリング周波数に戻したものを作ります。
つまり、次の4つのファイルを作ります。
- 基準(44.1kサンプリング) → 48kに変換
- 基準(48kサンプリング) → 44.1kに変換
- 基準(44.1kサンプリング) → 48kに変換 → 44.1kに変換
- 基準(48kサンプリング) → 44.1kに変換 → 48kに変換
今回、差の比較方法として、単純に、差の絶対値の合計としましたが、その結果だけで、soxやaudacityと、ffmpegは「十分なほどの差がありました」。
ところで、今回始めて知ったのですが、SoXやAudacityでは、設定により、変換の際にあえて「ノイズを加える」処理を行なうようになっていました。これは、SoXで生成したファイルが毎回値が異なっていたためわかったのですが、今回の調査では不要ですので、変換時には、-Dでoffしたり、-Rでrandom seedを固定したりしました。
なお、バージョンは以下のとおりです。
- sox: SoX v14.3.1
- FFmpeg SVN-r23334
libavutil 50.16. 0 / 50.16. 0
libavcodec 52.70. 0 / 52.70. 0
libavformat 52.66. 0 / 52.66. 0
libavdevice 52. 2. 0 / 52. 2. 0
libavfilter 1.20. 0 / 1.20. 0
libswscale 0.10. 0 / 0.11. 0
libpostproc 51. 2. 0 / 51. 2. 0 - Audacity v1.3.10-beta (Unicode)
- Toast10(10.0.7)
-h high 95% 125 16-bit mastering (use with dither)
と、
-v very high 95% 175 24-bit mastering
の両方を試してみました。なお、SoXのページのFAQにある
dither -s
は、-hと-vでの両方で試してみましたが、結果(誤差とFFT)をみる限りにおいて「使うべきでない」と判断しました。誤差が多く、FFTでは20kHzのまわりに波形がでていました。さらに、SoXでは-Dと-Rオプションを指定し、ノイズを追加しないようにしました。
結果は以下のとおりです。
ffmpeg |
sox |
sox -v | ||||
誤差合計 | 最大誤差 | 誤差合計 | 最大誤差 | 誤差合計 | 最大誤差 | |
48k->44.1k | ||||||
1kHz | 605,294 | 493 | 220,620 | 164 | 220,718 | 164 |
5kHz | 2,368,239 | 2,588 | 226,804 | 844 | 227,157 | 845 |
10kHz | 4,740,763 | 6,168 | 234,503 | 1,813 | 235,218 | 1,816 |
15kHz | 740,473,840 | 15,374 | 248,212 | 3,033 | 249,627 | 3,078 |
20kHz | 6,419,425,028 | 22,868 | 333,519 | 6,353 | 339,598 | 6,164 |
44.1k->48k | ||||||
1kHz | 740,226 | 334 | 246,558 | 178 | 246,649 | 178 |
5kHz | 2,387,836 | 1,738 | 256,104 | 906 | 255,722 | 903 |
10kHz | 4,682,808 | 3,974 | 265,215 | 1,957 | 265,982 | 1,951 |
15kHz | 190,046,717 | 8,711 | 290,234 | 3,476 | 288,278 | 3,464 |
20kHz | 5,014,488,725 | 17,389 | 7,858,724 | 7,779 | 963,641 | 7,746 |
44.1k->48k->44.1k | ||||||
1kHz | 1,097,677 | 448 | 35,959 | 235 | 36,072 | 236 |
5kHz | 4,482,480 | 2,356 | 45,839 | 1,200 | 45,820 | 1,200 |
10kHz | 9,065,992 | 5,743 | 58,468 | 2,560 | 59,331 | 2,560 |
15kHz | 898,376,184 | 15,565 | 86,089 | 4,270 | 247,523 | 4,309 |
20kHz | 7,020,275,885 | 25,009 | 7,402,131 | 8,651 | 913,698 | 8,431 |
48k->44.1k->48k | ||||||
1kHz | 1,198,943 | 541 | 66,332 | 269 | 66,503 | 269 |
5kHz | 4,892,888 | 2,835 | 76,336 | 1,374 | 76,462 | 1,372 |
10kHz | 9,947,555 | 6,793 | 90,257 | 2,947 | 91,257 | 2,941 |
15kHz | 967,383,206 | 17,418 | 125,734 | 5,033 | 119,265 | 5,048 |
20kHz | 7,467,202,670 | 25,039 | 7,874,228 | 10,623 | 974,258 | 10,528 |
audacity |
toast10 |
|||
誤差合計 | 最大誤差 | 誤差合計 | 最大誤差 | |
48k->44.1k | ||||
1kHz | 221,073 | 97 | 1,387,077 | 155 |
5kHz | 226,880 | 508 | 6,434,139 | 796 |
10kHz | 234,233 | 1,154 | 14,760,306 | 1,819 |
15kHz | 247,930 | 2,292 | 31,593,019 | 3,732 |
20kHz | 330,915 | 6,780 | 4,556,636,932 | 23,212 |
44.1k->48k | ||||
1kHz | 246,892 | 97 | 239,668 | 147 |
5kHz | 254,867 | 503 | 243,641 | 764 |
10kHz | 263,383 | 1,136 | 244,574 | 1,731 |
15kHz | 283,170 | 2,209 | 255,127 | 3,465 |
20kHz | 376,568 | 5,992 | 4,877,475,407 | 25,451 |
44.1k->48k->44.1k | ||||
1kHz | 34,529 | 81 | 1,241,147 | 156 |
5kHz | 43,033 | 424 | 6,293,410 | 813 |
10kHz | 55,394 | 972 | 14,621,993 | 1,887 |
15kHz | 78,547 | 1,982 | 31,461,050 | 4,029 |
20kHz | 201,524 | 6,712 | 6,303,307,517 | 26,320 |
48k->44.1k->48k | ||||
1kHz | 66,762 | 438 | 1,120,589 | 215 |
5kHz | 80,077 | 1,949 | 5,606,065 | 1,118 |
10kHz | 90,302 | 2,497 | 13,055,018 | 2,571 |
15kHz | 98,336 | 2,759 | 28,513,225 | 5,349 |
20kHz | 215,840 | 8,736 | 6,717,687,514 | 26,929 |
まず第一に最大誤差を見ますと、明らかにffmpegは酷すぎます。サンプリング周波数変換のためには使うべきではありません。特に20kHzでの変換結果は異常です。波形を見ると振幅が非常に小さくなってしまっています。採用しているサンプリング周波数変換のアルゴリズムに問題がありそうです(ひょっとするとオプションで選択できたりするのでしょうか?)。
SoXは、良い方です。なお、48kHzに変換する場合には-vオプションを使うとよいでしょう。
Audacityは全体的に優秀です。
Toastは、低中音域ではSoXと同程度の良さなのですが、ffmpegと同じく致命的な欠点があります。ffmpegほどではありませんが、20kHzの振幅が半分程度に小さくなってしまうのです。これですべてが台無しです。これも採用しているサンプリング周波数変換のアルゴリズムに問題がありそうです。
また、変換結果をAudacityで開いて、plot spectrumを使って3〜6秒の間の周波数成分をみますと、SoXやAudacityは鋭いピークのみですが、ffmpegはそれ以外のところ数ヶ所にピークが出ていたり、toastではピーク以外に全体的に変な形になっていたりと、明らかに問題のある変換結果でした。問題のあるものだけ示します。
まず、ffmpegの20kHzでの、サンプリング周波数48kHz→44.1kHzと44.1kHz→48kHzへの変換の結果のスペクトラムです。48kHzは明らかですが、44.1kHzの場合も450Hzと4600Hzあたりに小さなピークが出ています。
次にToast10です。ffmpegよりもすごいことになっています。1kHzと20kHzでの、サンプリング周波数48kHz→44.1kHzへの変換の結果のスペクトラムです。
また、Toastでは、次のように振幅が半分程度に小さくなってしまっています(この現象はffmpegでも見られました)。20kHzでの、サンプリング周波数48kHz→44.1kHzへの変換の結果のデータです。
簡単にまとめますと、私の結論としては
- サンプリング周波数の変換には、Audacityを持ちいること。Audacityが使えないときはSoXを使うこと。
- SoXのdither -sオプションは使わない方がよさそうだ。デフォルトか、-vオプションを使うこと。-vの方が高域の誤差が少ない。
- ffmpegでは音声データの変換を行なわないこと。ffmpegではフォーマット変換のみを行なうこと。
- Toastでは音声データの変換を行なわないこと。
さて、Audacityではサンプリング周波数の変換のためのバッチ処理はできません。エフェクトのバッチ処理はできるのですが、そこにサンプリング周波数の変換はないのです。全部手作業となると、非常に面倒です。一方、SoXはバッチ処理ができます。以上のことから、私としては、可能な限りaudacityを用い、バッチ処理が必要なほど多いときにはSoX -vで処理する、という方針にしました。ただし、この決定は、各ソフトのバージョンが変わったときに変更される可能性があります。なにしろ、過去には「SoXの変換は音が悪い」と言われていたこともあるのですから。
なお、今回は、ノイズの追加処理を行なわないようにして調査しましたが、調査を主目的とするのでなければ、デフォルトやdither -sオプションによるノイズの追加処理を使う方がよいかもしれません。自分の耳で確認してください。
というわけで、soxでサンプリング周波数の変換をするためのオプションは次のとおり。
sox 元の音声ファイル 変換後の音声ファイル rate -v 44.1k48kHzにするなら、44.1kのところを48kにします。また、very highにしないのならば-vをとります。音声ファイルのフォーマットは、各ファイルの拡張子で判断されます。なお、SoXのFAQページによるCDのためのマスタリングは次のオプションを指定しろとかかれています。
sox 元の音声ファイル 変換後の音声ファイル rate -v 44.1k dither -s
参考までに、テストで使ったコマンドとオプションの例です。
基準となる音声ファイルの作成(10秒の1kHzの正弦波、-1.938db、44.1kHz)
sox -n 基準となる音声ファイル synth 10 sine 1000 gain -1.938
サンプリング周波数の変換
ffmpeg -i 基準となる音声ファイル -ar 48000 変換後の音声ファイル
sox 基準となる音声ファイル -D -R 変換後の音声ファイル rate -v 48k
RAW(LPCM)への変換(16bit LPCM、BigEndian)
ffmpeg -i 音声ファイル -f s16be LPCM音声ファイル
sox 音声ファイル -B LPCM音声ファイル.s16