00:56・2020/10/14
カテゴリー
chrome拡張機能 JavaScript worksWeb audio API を使ってChrome拡張機能をつくってみた
10:22・2021/08/09 公開
02:31・2021/08/16 更新
突然ですが、あなたはにじさんじの新人(2021夏時点)ライバーの「レオス・ヴィンセント」をご存じでしょうか?
彼の配信がクッソ面白いので、もっと面白くしようと思ってChrome拡張機能を作りました。
その名も「LEOS_meter(レオスメーター)」。
※名づけのセンスが無い
動画の最大音量というか、今何dBの音が出ているかわかったらまあまあネタにできそうじゃないですか。声がデカい人とか。
— はにぃ (@37_HoneyBee_73) August 3, 2021
概要
「LEOS_meter」はYouTubeで動画を開いたときに、動画の上部に「Web audio API」で取得した音量(正しくは音域毎のgainを足した数値)を表示する拡張機能です。
画面はこんな感じ。
左側にマッドサイエンティスのレオスの研究対象である「まめねこ」を表示、右側に音量を表示しています。
(本当はいい感じにスケーリングしてdBで表示したかった)。
音が止まっているときまめねこは寝ていますが、音が出ているときはまめねこの「まめ」の部分(?)が動きます。
レオスが大きい声を出すと双葉がブンブンします。(これがやりたかった)
使い方
ダウンロード
コード配布用 – Googleドライブ
LEOS_meter – GitHub
お好きな方をどうぞ。
拡張機能の読み込み
「chrome://extensions/」にアクセスし、右上のデベロッパーモードをオン。
「パッケージ化されていない拡張機能を読み込む」から、解凍したzipファイルを読み込むことで使えます。
本当はストアで公開したかったんですが、二次創作ガイドライン的にどうなんだろうとか、ストアの開設にお金がかかるとかで諦めました。
気に入ったら拡散していただけると嬉しいです。
実装について
そんな大層なコードでもないので全文公開します。
Web audio API に慣れるのに3日かかりました。全然理解できん。
manifest.json
{
"name": "LEOS_meter",
"version": "1.0",
"description": "YouTubeオーディオの再生中に音の大きさを表示します。/ Shows the loudness while playing YouTube audio.",
"manifest_version": 2,
"permissions": [
"activeTab",
"tabs"
],
"content_scripts": [
{
"matches": [
"https://www.youtube.com/*"
],
"js": [
"function.js"
]
}
],
"web_accessible_resources": [
"images/*.GIF"
]
}
function.js
'use strict';
function add_container() {//youtube.comのHTMLに表示領域を追加
const wrapper = document.getElementById('primary-inner');
let meter = document.createElement('div');
meter.id = 'LEOS_meter';
meter.style.boxSizing = 'border-box';
meter.style.width = '100%';
meter.style.height = '48px';
meter.style.padding = '0 20px';
meter.style.backgroundColor = '#000';
meter.style.border = '1px solid #454545';
meter.style.display = 'flex';
meter.style.justifyContent = 'space-between';
meter.style.alignItems = 'center';
let animation_box = document.createElement('div');
let animation_gif = document.createElement('img');
animation_gif.id = 'LEOS_gif';
animation_gif.style.maxHeight = '46px';
animation_gif.style.objectFit = 'contain';
animation_gif.setAttribute('sec', chrome.runtime.getURL('images/mameneko.GIF'));
//拡張機能のフォルダにアクセスしてGIFを取得↑
animation_box.appendChild(animation_gif);
meter.appendChild(animation_box);
let int_box = document.createElement('p');
int_box.style.height = '48px';
int_box.style.display = 'flex';
int_box.style.alignItems = 'flex-end';
let int = document.createElement('span');
int.id = 'LEOS_int';
int.style.color = '#454545';
int.style.fontSize = '36px';
let unit = document.createElement('span');
unit.style.color = '#454545';
// unit.textContent = 'dB';
unit.textContent = 'Gain';
unit.style.fontSize = '20px';
unit.style.paddingBottom = '4px';
int_box.appendChild(int); int_box.appendChild(unit);
meter.appendChild(int_box);
wrapper.insertBefore(meter, wrapper.firstChild);
}
function audio() {
let gif_ctrl = document.getElementById('LEOS_gif');
let int = document.getElementById('LEOS_int')
const audioElement = document.getElementsByClassName('html5-main-video')[0];//videoタグ取得
var audioCtx = new AudioContext();//オーディオの作成
let source = audioCtx.createMediaElementSource(audioElement);//videoタグのオーディオを登録
var analyser = audioCtx.createAnalyser();//アナライザの作成
analyser.fftSize = 32; //音域の数
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
var bufferLength = analyser.frequencyBinCount;
//↓の配列に音域ごとの大きさが入る
var dataArray = new Uint8Array(bufferLength);
//↓登録したオーディオをアナライザに流してから、スピーカーに出力
source.connect(analyser).connect(audioCtx.destination);
getAudio();
function getAudio() {
requestAnimationFrame(getAudio);//フレーム毎に繰り返し呼び出し
analyser.getByteFrequencyData(dataArray);
//音域の総和を得る
var dNum = 0;
for (var i = 0; i < dataArray.length; i++) {
dNum += dataArray[i];
}
///////////////////////////////////////////////////////////////////
//dBにスケーリングしようとした残骸
// let inputY = (dNum / 100);
// let xMax = 0;
// let xMin = -30;
// let yMax = 20;
// let yMin = 0;
// let percent = (inputY - yMin) / (yMax - yMin);
// let outputX = percent * (xMax - xMin) + xMin;
// int.textContent = Number.parseFloat(outputX).toFixed(2);
////////////////////////////////////////////////////////////////////////
int.textContent = ((Math.floor(dNum / 10).toFixed(1)) * 10);//一の位が動き過ぎなので切り捨て
if (dNum == 0) {//GIFのスイッチ
if (!gif_ctrl.classList.contains('play')) {
return;
} else {
gif_ctrl.setAttribute('class', '');
gif_ctrl.setAttribute('src', chrome.runtime.getURL('images/mameneko.GIF'));
}
} else if (dNum <= 1000) {
if (gif_ctrl.classList.contains('play')) {
return;
} else {
gif_ctrl.setAttribute('class', '');
gif_ctrl.classList.add('play');
gif_ctrl.setAttribute('src', chrome.runtime.getURL('images/mameneko_play.GIF'));
}
} else {
if (gif_ctrl.classList.contains('fast')) {
return;
} else {
gif_ctrl.setAttribute('class', '');
gif_ctrl.classList.add('fast');
gif_ctrl.setAttribute('src', chrome.runtime.getURL('images/mameneko_fast.GIF'));
}
}
}
}
window.addEventListener('load', () => {
console.log('LEOS_meter is running.');
setTimeout(() => {
add_container(), audio()
}, 500);//youtubeのscriptタグ実行のため0.5秒待つ
})
ディレクトリ構造
- LEOS_meter
- manifest.json
- function.js
- images
- mameneko.GIF
- mameneko_play.GIF
- mameneko_fast.GIF
GIFデータ
↓まめねこのGIF単体はここからDLできます。
LEOS_meter_GIF – GoogleDrive
LEOS_meter以外での使用の際は、ここのコメント欄かTwitterにて必ず教えてください。
※改変・自作発言禁止
参考サイト
【ドット絵制作】
ドット絵エディタ – Dottable
多機能でとても使いやすかった
【コーディング】
Web Audio API
MDNのマニュアル
Web AudioでYoutube、itunes上の曲を元に、WebGL(GLSLシェーダー)でリアルタイムビジュアライズ
APIについて
Chromeの拡張機能を作ってみる備忘録。
拡張機能の画像の読み込み方について
Twitterから「いいね」を消し去るChrome拡張を作る
JSONの参考用
2つの数値範囲間のスケーリング
数値のスケーリングについて
動画の適切な音量はどれくらい?音声・BGM・効果音の音量調整の目安をまとめてみた
スケーリングの参考用
WEB SOUNDER
すごく古いサイトだけどMDNよりわかりやすかった
補足
videoタグからリアルタイムにaudioデータを取る方法が検索してもわからなくて、コードをこねこねしていたら偶然成功しました。
※追記 R3/8/16
音量を良い感じにするオプションを付けたversion1.1をGoogleDriveに公開しました。
LEOS_meter_v1.1を読み込み後、右クリックでメニューが出ますので、オプションから設定を変更してください。
※初回起動時はまめねこも音量調整もOFFになっています。
実装について
【Among Us】エデン組、疑心暗鬼。【レオス・ヴィンセント/にじさんじ】
こちらの動画を参考に、主に1000gainを超える部分を削る感じで実装しました。
(僕は音でビビらせてくる動画は大嫌いなんですねぇ~~~)
//normalize
if (val_gain) {
if (dNum < 500) {
volume = 1.1;
gainNode.gain.value = volume;
}
if (dNum < 900) {
volume = 1;
gainNode.gain.value = volume;
}
if (dNum >= 1000) {
volume = 0.4;
gainNode.gain.value = volume;
}
if (dNum >= 1050) {
volume = 0.3;
gainNode.gain.value = volume;
}
if (dNum >= 1100) {
volume = 0.1;
gainNode.gain.value = volume;
}
if (dNum >= 1150) {
volume = 0.001;
gainNode.gain.value = volume;
}
if (dNum >= 1200) {
volume = 0.00001;
gainNode.gain.value = volume;
}
if (dNum >= 1300) {
volume = 0.000001;
gainNode.gain.value = volume;
}
if (dNum >= 1400) {
volume = 0.0000001;
gainNode.gain.value = volume;
}
}
追記部分こんなんです。
絶対もっといい実装があるはず。
この実装には難点がいくつかありまして、
- 別タブで作業していると音量調整が効かない
- gainを雑に下げているため音がガタガタする
といった感じです。
以上、解散。