YouTubeなしではもう生きられません、火頭です。
それほどにYouTubeは私の生活に根付いております。
そう言った手前、文句を言うのははばかれるのですが、今回直したいことは以下。
動画を自動再生にして流していると、動画によって音量が違うので、たまに耳に痛かったりします。あとは動画の最中に盛り上がってワッと音量上がったり、うるさいジングルを採用していたり...イヤホンしていることが多いので、結構困るんですよね。
自作の拡張機能で、新しく動画を開いた時は音量を70%くらいに設定するようにしているのですが、動画の最中のうるさい瞬間に関してはそれでは対応できません。
ということで「動画の音量を監視して、一定より音量が上がった場合に自動で音量を抑えてくれる機能」を作りたい。
まず「音量を監視する」について。
いきなり難関です。結局はWeb Audio APIを採用することに。この機能、自分のサイトで使っているのですが、拡張機能では初めて、いやな予感しかしません。
ってか普通Web Audio APIは自分でシンセ的なものを作って音を出すか、ファイルを読み込んでそれを加工する目的で使われます。ブラウザ内で流れてる音をどうやってWeb Audio APIに繋げるのか、そこからして初めての試みです、でも拡張機能でEQ機能とかあるもんなぁ。
結局は<audio>とか<video>タグをgetElementして、それをWeb Audio APIに繋げると出来るらしい。なんと荒っぽいというか...ホンマに出来るんかいな、って試してみたら出来ました 笑。YouTubeは当然<video>タグの方でした。
とりあえず、ブラウザ内の音をWeb Audio APIでいじれるようになりました。
音量監視はAnalyserノードを使います。使い方をアレコレ調べて回ったのですが、そういえば自分のサイトですでに使ってました、音量を数値化して音楽に合わせて映像を出すビジュアライザーを自作したのでした。それをコピペしてとりあえず音量監視はクリア。
さて、これで動画を流したらsetInterval関数などで指定したタイミングで音量が取得できる。
次「一定より音量が上がった場合」について。
一定より音量が上がった場合って何? ってことですよブラザー。
スレッショルドを決めてそれを上回った場合、ということですが、そのスレッショルドをどうやって決めましょう?
ちなみに音量は数値でズラズラと流れてきます。30 32 38 40 43...といった感じでしょうか。
簡単なところでは、ある期間、音量を監視して、その平均を出す、それをその動画のその部分の平均的な音量として、その少し上くらいをスレッショルドとして設定し、スレッショルドは随時更新する。無音があったりする動画には弱そうですが、切捨てすれば良いでしょう。こんな所でいかがでしょう。
「自動で音量を抑えてくれる機能」の部分。
上記のスレッショルドを上回る音量が飛んで来たら、gainNodeでボリュームを下げる、これで良い、実際試してみたら動作確認できました。
で、次の課題。
・下げた音量はいつ戻すか
・音量はスムーズに下げたり上げたりしたい
下げた音量をいつ戻すか、について。
普通に考えてスレッショルドを下回るまで下げ続ける、という感じですが。ここで問題。gainNodeで音量を下げると当然、監視している音量に影響するんですよね。
動作の様子はこんな感じ ↓
スレッショルド = 50として
44 48 52 ←拡張機能作動! 48 41 37 33...
こうなるので、すぐにスレッショルドを下回るんですよね。
これで音量を戻してしまうと実際の動画ではまだうるさい部分の最中なわけです。
どうしましょう?
効果は一定時間にするか、それとも下げる前の音量を監視できるように調整するか?
後者のが良いですが、面倒です。とりあえず一定時間で。
というか実際はスレッショルドを越える時、数回そういう数値が飛んでくるので、何度も機能がトリガーされてしまいます。
音量の変化を繰り返さないよう、そこはフラグで対応するとして、トリガーされるたびに音量平均の計算を0の位置に戻してやれば、計算は最初からになり数秒かかります(そういう設定にしている)数秒間は音量が下がった状態が続きます、平均計算が終わった時に、音量を戻すように設定してやれば、大体良い感じに動作します。
元の音量を監視しているわけではないので、戻ればまたトリガーされる可能性のあるザル仕様ですが、とりあえずこれで。
音量をスムーズに上げ下げしたい。
結構苦労しました。gainNodeはループ回してちょっとずつ音量を下げても結果はドンと下がるだけ、まぁこれは予想通り。
再帰関数作ってはどうか。これもダメ、やっぱドンと下がるのみ。
setTimeoutとかsetIntevalか?と思い調べてみたところ。
var gainNode.gain.exponentialRampToValueAtTime
という謎メソッドがヒットしました。これでスムーズに狙った音量に、狙った時間をかけて移動できます。良いメソッドです、自分のサイトで採用してそうなもんだけど知らんかったなぁ。
これくらいで最低限の動作が確保されました。
気になるのは、analyserNodeによる音量の計算のところで、実際の音量というよりも音圧によって数値が上がってるなぁ、という印象。
BGMが入ったり、複数人数がしゃべってる時に音量の数値が高く計算される感じ、一人が大声出すより、二人がわいわいしてる方が数値が高いイメージ、もうちょいなんとかしたい。
あとはインターフェイス揃えて、エフェクターソフトのような使用感にするべきですね、そういうの面倒で嫌なんですけど。
でも、スレショルドの高さ、音量の押さえ具合、平均値の計算にかかる時間(現状ではこれがリリースのタイミング)など私が独断で決めてますからね、そりゃマズイです。
調整ができたら公開します。
あとそういえばWeb Audio APIにはコンプレッサー機能があるんですよね、今回の拡張機能はまさにコンプなので、最初からこちらを採用すれば良かったのでは?