FFmpeg の mix で音声合成したら音量が小さくなる
議事録ツール「ワンミニッツ」では、複数人の会議音声を合成して保存する処理があります。
昔は会議室でみんなで会議していましたが、最近はリモート会議も増えたため、それぞれが録音した音声を合成して1つの会議音声とする必要があるためです。
会議の内容は文字起こしされていますが、文字起こしがうまくできていなかったり、文字になっていない情報を拾うために音声を聞きたいこともあります。
そこで、FFmpeg の mix フィルタを使って音声を合成する処理を実装しました。しかし、リリース後しばらく経ってから、音声トラックが増えると音量が小さくなるという不具合が見つかりました。
FFmpeg について
FFmpeg は音声や動画を処理するためのコマンドラインツールです。動画や音声を扱っているサービスで FFmpeg を使っているところは多いでしょう。
ワンミニッツでは動画は扱っておらず音声だけ処理すればいいので、SoX (Sound eXchange)などの他のツールを使ってもいいのですが、ツールの使い勝手と実績を考えた結果 FFmpeg を採用しています。
なんでもできる一方コマンドは非常に複雑で、ちゃんと使うためにはドキュメントをかなり読み込まないといけません。利用者が多いツールなのでネットに情報は転がっているのですが、仕様を把握していないと我々のような罠を踏みます。
ちなみに、FFmpeg を Laravel から利用できるライブラリもあるようなのですが、ワンミニッツでは利用していません(ワンミニッツは PHP と Laravel で開発されています)。
採用しなかった理由は単純に存在を知らなかっただけなのですが、ワンミニッツでは複雑な音声処理はしていないので、どのライブラリを使ってもほとんど差はなさそうです。むしろ laravel-ffmpeg を使ってしまうと Laravel への依存が強くなるので、避けた方がいいのかもしれません。
ワンミニッツでは PHP-FFMpeg を利用しています。laravel-ffmpeg も結局は PHP-FFMpeg のラッパーのようです。
https://github.com/PHP-FFMpeg/PHP-FFMpeg
なぜ音量が小さくなるのか
ワンミニッツでは FFmpeg の mix コマンドを使って音声合成しているのですが、mix コマンドで n 個の音声を合成すると、デフォルトではそれぞれの音量を 1/n にしてから合成するためです。
10人が会議に参加していたら音量が 1/10 になってしまいます。
対処方法
例えば、 a, b, c の3つの音声を単に mix するとき、以下のような mix 処理を書きます
[a][b][c]amix=inputs=3[result]
a,b,c の3つの音声を mix して、result という合成後の音声を出力します。ただし、これだと音量が 1/3 になってしまうので、volume というパラメータで音量を3倍にします。
[a][b][c]amix=inputs=3,volume=3[result]
merge と mix どっちを使うべきか
今回の問題の対応で以下の記事を参考にしたが、この記事では merge と mix を比較した上で最終的に merge を採用していました。
しかし、merge の場合には channel_layout というのを考える必要があり、コマンドが複雑になります。音をステレオにしたい場合などは音声チャンネルについて考えるのもやむをえないですが、ワンミニッツの処理はモノラルでよいし、もっとシンプルです。
そこで、ワンミニッツではもっとシンプルにすむ amix を採用しました。結果をステレオにする必要もないので、 amix で十分です。
ただし、 amix でも処理するファイルが多すぎると重くなるので、入力された音量を適当に分割して処理するようにしています。20個の音声を10個ずつに分け、最初の10個を合成、できた音声と残りの10個を合成するようにしています。
まとめ
ワンミニッツで会議音声を処理している際に、意図せず音声が小さくなってしまう問題を修正した話でした。
ワンミニッツは会議の録音、文字起こし、自動要約などを行うためのプロダクトです。まだまだ改善の余地があるプロダクトですので、これからもじゃんじゃん改善していきたいと思います。