バックグラウンドタブ再生を生かすため低周波音をOBSで永続再生?
概要
AudioContext
経由で作ったOscillatorNode
を使い、機械的(周波数云々)な音を作成&再生。AudioContext
経由で作ったAudioBuffer
に周波数を模した情報を書き込み、サウンドを作成&再生(+オマケで.wav
をDL)
経緯(いきさつ)
とあるライブストリーミングサービスで自分の配信をモニタ(視聴)中、タブをバックグラウンドにすると視聴遅延が発生する。
どうやら維持判定が「オーディオが再生し続けている事」っぽい雰囲気を感じたので、
聞こえない周波数を流し続ける処理を視聴者が DevTool 上に走らせることで 維持できないかと試してみた
…が、結局うまくいかなかったので、最終的に"配信者の立場"としてOBSから不可聴周波数音を流し続ける方法にした ( そっちのがスマート )
波形再生の名残
ボリューム実数範囲が 1~0 なので、0.1
つまり、 100~0 換算で言えば 10 相当まで下げています。
周波数は 480Hz の設定で、時間は 5秒 再生。
- 実際は不可聴周波数で運用
例えば 1Hz とかで運用。
遊びで 16,000Hz とかにして モスキート音 も作れますゾ~~ 👂
(()=>{
const audioCtx = new window.AudioContext();
const gNode = audioCtx.createGain();
gNode.gain.value = 0.1; // ボリューム範囲 1~0
const osc = audioCtx.createOscillator();
osc.type = "sine"; // サイン波
osc.frequency.setValueAtTime(480, audioCtx.currentTime); // 周波数。480Hz
osc.connect(gNode).connect(audioCtx.destination);
console.log(osc);
osc.start();
osc.stop( audioCtx.currentTime + 5 ); // 5秒間再生
})();
ですが、…折角調べて書いたのに、結局この方法では
バックグラウンドタブでの配信視聴を維持(=ラグ無効化)できませんでした 😭
同性質の wav を書き出してOBSで使うか…
という訳で、手付で波形を作成して、.wav
でDL する方法へシフト。
※ 実行時音量注意 ( 音量調節部分はノータッチ )
(()=>{
const audioCtx = new window.AudioContext();
const ch = 1; // 1:モノラル,2:ステレオ
const sec = 2; // 秒数
const freq = 480; // 周波数。 不可聴目的なら 1Hz とか
const frameCount = audioCtx.sampleRate * sec;
const myBuffer = audioCtx.createBuffer( ch , frameCount , audioCtx.sampleRate );
console.log( "サンプルレート" , audioCtx.sampleRate );
{
for(let nowCh=0; nowCh < ch; nowCh++){
const nowBuff = myBuffer.getChannelData(nowCh);
for(let i=0; i<frameCount; i++){
// 周波数に合わせた情報を作成
const t = i / audioCtx.sampleRate;
nowBuff[i] = Math.sin(2 * Math.PI * freq * t);
}
}
}
// 作ったバッファを再生するだけ。DLだけ必要であれば省いてOK
{
const source = audioCtx.createBufferSource();
source.buffer = myBuffer;
source.connect(audioCtx.destination);
source.start();
}
// 変態処理をお借りしました
toWav=w=>((
{numberOfChannels:c,sampleRate:r},l4=x=>[x,x>>>8,x>>>16,x>>>24],l2=x=>[x,x>>>8],
x=(x=>[...Array(x[0].length)].flatMap((_,i)=>x.flatMap(y=>l2(y[i]*0x7fff))))([...Array(c)].map((_,i)=>w.getChannelData(i)))
)=>new Uint8Array([82,73,70,70,l4(36+x.length),87,65,86,69,102,109,116,32,16,0,0,0,1,0,l2(c),l4(r),l4(r*(c*=2)),l2(c),16,0,100,97,116,97,l4(x.length),x].flat()).buffer)(w);
// 16bitの.wav用データへ変換 → Blob化 → DL準備 → DL → 後片付け
{
const resultArrBuf = toWav(myBuffer);
const blob = new Blob( [resultArrBuf] , { type:"audio/wave" });
const dlUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = dlUrl;
a.download = `${ch}ch_${freq}Hz_${sec}sec.wav`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(dlUrl);
}
})();
上記設定を 1chモノラル , 1hz , 5秒 に調整したうえで、DL作成した .wav
をOBS内部にループ再生配置。
OBS内の設定音量を -42.0 dB
くらいにすると、普段使っているストリーミングサイトの視聴用タブがギリギリ音再生判定となった
( ※ タブにスピーカーマークがつく 🔊 )
1Hz とかにしておけば、当然人の耳には聞こえない。
-
波形作成周り参考
https://developer.mozilla.org/ja/docs/Web/API/BaseAudioContext/createBuffer https://www.programmingmat.jp/webhtml_lab/webaudioapi_sq.html
https://bombrary.github.io/blog/posts/web-audio-api-note01/ -
.wav
変換処理
https://qiita.com/McbeEringi/items/14b05233e8288bac5bea
↑ 一番ビビった変態コード ( 褒めてる )
おまけメモ・覚書
( ※ 執筆時の GoogleChrome バージョンは 129 系 )
雑に言うと、バックグラウンドタブに制限が掛かるのは、
リソース節約のために処理の間引きや休眠を行うChromeの機能が影響している模様。
「タブスロットリング」なる機能やら「オクルージョントラッキング」がそれらしい。
2020/11/18 窓の杜 - 「Google Chrome 87」が安定版に ~タブスロットリングやオクルージョントラッキングで高速化
2022/07/12 窓の杜 - 「Google Chrome」の省電力技術を極めた試験フラグがCanary/Dev版に導入
- Chrome 90
chrome://#intensive-wake-up-throttling
が廃止
https://chromeenterprise.google/intl/ja_jp/policies/#IntensiveWakeUpThrottlingEnabled
色々な遍歴を経て、現在は WindowOcclusionEnabled と呼ばれる機能項目になっている模様?
( しかもこれ、一度廃止されたのち再び復活したらしい )
-
Calculate window occlusion on Windows ( = WindowOcclusionEnabled )
chrome://flags/#calculate-native-win-occlusion
Calculate window occlusion on Windows will be used in the future to throttle and potentially unload foreground tabs in occluded windows – Windows
Windows でウィンドウのオクルージョンを計算する機能は、将来、オクルードされたウィンドウのフォアグラウンドタブをスロットルし、アンロードするために使用される予定です。
https://chromeenterprise.google/intl/ja_jp/policies/#WindowOcclusionEnabled
で、今回対象の配信プラットフォームでは、これを Dsabled にしても視聴遅延が発生してた、というオチ(仕様)でした orz
不可聴音のループ再生部分は制限されず、肝心の配信映像視聴部分が制限されてた、って事なんだと認識しています。