はじめに
iPhoneのマイクだけでハウリングを起こしやすい帯域を見つけるアプリを作っています。いま、コンプもリミッターも通さないステレオ録音を作っています。
潰す処理を入れないので、入力を小さめにしないと音が割れます。小さめに録れば、あとで小さい。そこで、録ったあとに音量をそろえる処理を足しました。
やり方はゲインだけです。目標を -16 LUFS、上限を -1 dBTP にして、その範囲で全体を持ち上げます。圧縮はしません。
実装は動いたのですが、書き出したファイルを測ったら、思っていないところが壊れていました。
起きたこと
保存形式にm4a(AAC)を選んで音量をそろえると、書き出したファイルのビットレートが半分以下になっていました。
| ビットレート | |
|---|---|
| 元のファイル | 241,836 bps(242kbps) |
| 音量をそろえたもの | 112,833 bps(113kbps) |
アプリは256kbpsで録っています。そろえた瞬間に落ちていました。
音を触っているのはゲインだけなので、波形そのものは劣化していません。落ちていたのは、書き出すときの設定です。
原因
元のファイルの設定をそのまま渡していました。
// ×これだと落ちる
let 設定 = f.fileFormat.settings
let out = try AVAudioFile(forWriting: 先, settings: 設定,
commonFormat: .pcmFormatFloat32, interleaved: false)
AVAudioFile.fileFormat.settings は、AVFormatIDKey やサンプルレート、チャンネル数は返します。ですが AACのビットレート(AVEncoderBitRateKey)は入っていません。
入っていないので、AVAudioFile(forWriting:) が既定値で書き直します。それが113kbpsでした。
WAVやCAFでは起きません。非圧縮なので、そもそもビットレートの指定がないからです。AACのときだけ静かに落ちます。
直し方
AACのときだけ、書き出しのビットレートを明示します。
var 設定 = f.fileFormat.settings
if let fid = 設定[AVFormatIDKey] as? UInt32, fid == kAudioFormatMPEG4AAC {
設定[AVEncoderBitRateKey] = 320_000
設定[AVEncoderBitRateStrategyKey] = AVAudioBitRateStrategy_VariableConstrained
}
let out = try AVAudioFile(forWriting: 先, settings: 設定,
commonFormat: .pcmFormatFloat32, interleaved: false)
AACは非可逆です。開いて書き直せば、ビットレートを上げても2回目のエンコードになります。劣化させたくない場合は、録るときにWAVかCAFを選びます。
どのビットレートにするか、実測で決めた
元のm4aに同じゲインをかけた無劣化版を基準にして、各設定で書き直し、生の数値で引き算しました。
| 設定 | ビットレート | 差の平均 | サイズ |
|---|---|---|---|
| 256k CBR | 255,994 | -65.7 dB | 1,049,237 |
| 320k CBR | 319,990 | -70.8 dB | 1,309,485 |
| 256k VBR制約 | 259,543 | -66.5 dB | 1,063,668 |
| 320k VBR制約 | 325,287 | -72.2 dB | 1,331,027 |
| 最高品質VBR | 74,158 | -47.8 dB | 309,771 |
320k VBR制約がいちばん元に近くなりました。256kより6.5dB良く、サイズは27%増えます。録音そのものは256kbpsのままで、そろえた時に書き出すファイルだけ320kにしています。
書き出しに使ったソフトの方に上限があった
この表を出すまでに一度やり直しています。最初はffmpegで測っていて、320kを指定しても値が上がりませんでした。
ffmpeg内蔵のAACエンコーダで確かめます。30秒のサイン波を2つ重ねたステレオファイルを、指定を変えて書き出しました。
頼んだ 128k → 実際 127,992 bps
頼んだ 192k → 実際 191,738 bps
頼んだ 256k → 実際 247,579 bps
頼んだ 320k → 実際 242,959 bps
320kを頼むと、256kを頼んだときより低くなります。
同じファイルをApple純正のafconvertで書き出すと、こうなりました。
afconvert 320k → 313,089 bps
このテストの音はサイン波2本なので、エンコーダが割り当てを絞ります。値そのものは実際の音楽より低く出ます。それでも、ffmpegの方だけ320kに届かないことは分かります。
AVFoundationが使うのはafconvertと同じエンコーダです。ffmpegで測って「上がらない」と判断していたら、設定を間違えたまま出していました。
おわりに
音は触っていないのに、書き出しの設定が抜けているだけでファイルが半分になります。しかも例外もエラーも出ません。
書き出したファイルは、必ず測り直す。 書き出しに使うソフトそのものが上限を持っていることもあります。
出典
- AVEncoderBitRateKey(Apple Developer Documentation)
- AVAudioBitRateStrategy_VariableConstrained(同)
- AVAudioFile.fileFormat(同)
ビットレートの数値は、手元の実装と実測データに当たって書いています。ffmpegとafconvertの比較は、この記事のために測り直したものです。
チェックワンツー(App Store・無料) … アナライザーは、払わなくても使えます。ステレオ録音は、無料では1回5分・2件までです。