ストリーミング再生における多重再生時の音量管理について

AlphaOggHDRの内部処理にもなるんですが、詳しく書いてなかったので書いておこうと思います。
ストリーミングで複数の音を重ねる場合、「レンジオーバー絡みの悩み」を解消するお話です。
簡単な原理なのですが、あまりこういった処理内容の記事を見かけることもないので。
尚、「1チャンネルで複数の音を合成すること」を前提とします。そうでない場合はハードウェアがやってくれることでしょうから。


ストリーミング再生の場合、単音を鳴らす場合は良いのですが、きっと多くの使用者は複数の音を重ねることと思います。
例えば、BGM、それに加えて環境音(雨の音とか)、それに加えてボイス、それに加えてコミカルな効果音…etc。
ボイスだけでも二人同時に喋ったりするケースは良くあるかと思います。
この時、開発者は「レンジオーバー」(複数の音を合成した結果が16Bitの範囲を越えると音が歪むこと)に対してかなりシビアになって音量管理を行うことと思います。
それはまるでPC-98時代の86音源のようなry


最も簡単な解決方法は、音量を要素数で割る方法で、BGM、SE、ボイスならそれぞれ33%の音量にする方法です。これならそれぞれ最大波形が重なり合っても100%に収まるので、レンジオーバーを招くことがありません。
ただし、プレイ時の音量が凄く小さくなるといった問題を引き起こします。
そういった作品も見かけたことはあるのではないかと思います。
(実際は最大波形が重なり合うことはないので50%ぐらいで良かったりしますが)

レンジ:|----------|
元  :[   B      ][    S     ][    V     ]
等分割:[B ][S ][V ]

B:BGM / S:SE / V:Voice


処理系をHDRにしてい無い場合は合成の際に上限下限で丸め込むしかないのですが、HDR化されている場合は最大レンジ以上の情報も残っていますのでもうちょっと賢い振る舞いが可能です。
人間の視聴モデルとかいうと格好良さげですが、要は出力直前に全体のボリュームをコントロールして、レンジ内におさめる事を行います。
ただ単純にレンジ内に収めればいいのかというとそうでもなくて、人間の耳に不快感がないようにコントロールしなければなりません。
過去にも考察(http://d.hatena.ne.jp/Ko-Ta/20061209/p1)しましたが、実際組んだ際はまたちょっと違うパラメータになってます。
これにより、もしレンジオーバーしてしまっても音が大きく歪むことを回避することが出来、開発者もシビアな音量管理から解放されるというのが最大の利点であります。
では長々と説明。


■全体の流れ
波形の合成は32Bit(出力レンジは16Bitとする)で行い、レンジオーバー時にボリューム調整をし、最終的に16bitで出力するモノとします。

[SrcBGM(16Bit)  ] x [BGM Volume  ] --+
[SrcSE(16Bit)   ] x [SE Volume   ] --+--[合成(32bitHDR)]-->[音量調整]-->[出力(16bit)]
[SrcVoice(16Bit)] x [Voice Volume] --+


■音量調整
出力直前の音量調整が今回の肝になります。
調べる要素は単純で、波形の最大値(+−両方向、つまり絶対値)を取得します。
もしレンジオーバーしているようなら、音量を小さくします。
実際はこの処理をサンプル数分やるとそれなりに重いので、16なり32の小さなブロックに分けてやっております。


と、これだけではイケマセン。このままだと単なる飽和な丸め込みと同じです。
人間のモデルと同じく、徐々に元に戻るような、フェード要素がないと不快な音になってしまいます。
フェードを決める各要素は次の通りになりました。
個人差はあると思いますが、個人的には以下の4要素に落ち着きました。
最後の1つはフェード遷移用変数です。

・最小範囲(0〜32768〜0xFFFFFF)
・最大範囲(0〜32768〜0xFFFFFF)
・ディレイ値(Hz数 : 44100Hzの場合44100で1sec)
・減衰値

・現在範囲(0〜32768〜0xFFFFFF)


■最小範囲、最大範囲、現在範囲
現在範囲がどこまでも下がったり上がったりすると管理に苦しむので、最小〜最大の有効範囲を定めます。

・最小範囲に満たない場合(そのまま)
入力  :wwwww..
有効範囲:.......|*-------|
出力  :wwwww..|

・有効範囲内(出力範囲にスケーリング)
入力  :wwwwwwwwwww
有効範囲:.......|--*-----|
出力  :wwwwwww|

・最大範囲以上(スケーリング&オーバーした部分はカット)
入力  :wwwwwwwwwwwwwwwwwwww
有効範囲:.......|-------*|
出力  :wwwwwww|---

・ちなみに、有効範囲を出力レンジより小さくすると音を大きく効果がある
入力  :wwww...
有効範囲:..|*------------|
出力  :wwwwwww|

※「*」は現在範囲


■ディレイ値、減衰値
現在範囲をどのようにフェードさせかを決めます。
処理は2パターンに分かれます。


・現在範囲よりも大きな波形が来た場合
この場合は現在範囲を広げます。
ただし、上記の最小〜最大範囲に収まるようにしましょう。


・現在範囲よりも小さな波形が来た場合
この場合にフェード処理が行われます。現在範囲を最小範囲方向に減衰させます。
いきなり減衰を行うと音量の揺らぎが生じ、不快な音に早変わりします。(ホラー映画で良くある環境音ですね)
なので、ディレイ値分待ってから、減衰させます。
減衰の強さは、減衰値によって決定されます。強すぎるとまた違和感が生じるのでその辺は経験則で。
だいたい、ディレイは0.5〜1.0秒ぐらい、減衰は5秒ぐらいで元の数値に戻るぐらいが個人的には良いかなぁ〜と思います。


■利点と応用
開発者がレンジオーバーを意識する必要が全くなくなるわけではありませんが、これによりかなり軽減されます。
特にボイスは最小音量と最大音量の差が広いので、この保護機能は有効です。(普通の会話は大丈夫だけど大きな声で歪みが生まれたりするんですよね結構)
範囲も普通の使い方だとレンジの1.5〜1.8倍までには収まってくれるので、ガンガン音量が上下することもあまりないみたいですから、「音量が変動して気持ち悪い」というのも聞きませんし。(4作品ほど市場に投入しましたが今のところ不評は耳にしませんし(そもそも気付かない)。音声処理については特にパッケージに表記する項目でもないというのもありますし。)
なので、わりとアップデートの際に「こっそりと」導入できちゃったりしてます:D


・応用
今回は応用は主眼ではありませんが、他にもわりと便利なテクニックがあったりします。
これらの機能を用いると、環境音やボイスをHDRデータとして扱うことが出来ます。
と言っても、ソース、音源を32bitで圧縮、というのはなかなか環境移行の手間を考えると面倒です。
そこで、音量を元の50%で録音、作成し、再生時に200%に戻すという手法を使えば、簡単に2倍HDR環境と相成ります。
25%にすれば4倍です。4倍もあればほぼ大丈夫でしょう。
特に効果的なのがボイスで、ノーマライズ作業を行うにしても若干レンジが足らなくて渋々下げざるをえない場合などには、この方法が割と有効です。


とまぁ、そんな感じです。
HDR化なんて考えてネーよ」って場合でも、この保護機能は便利ダヨーってお話です。
VSTプラグインにも「CDマスターなんちゃら〜」とかありますね。たぶんそれなんでしょうね。