From e852b246d3d5b75c21a625ccb3ebd1473aa97f51 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Sun, 5 May 2024 09:36:53 +0700 Subject: [PATCH] Rewrite volume control feature --- src/modules/patcher.ts | 29 ++++++++++++++++++++++ src/utils/bx-exposed.ts | 21 ++++++++++++++++ src/utils/monkey-patches.ts | 48 ++++++++----------------------------- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/modules/patcher.ts b/src/modules/patcher.ts index ea30e25..b02456f 100644 --- a/src/modules/patcher.ts +++ b/src/modules/patcher.ts @@ -459,6 +459,29 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar}); return str; }, + + patchAudioMediaStream(str: string) { + const text = '.srcObject=this.audioMediaStream,'; + if (!str.includes(text)) { + return false; + } + + const newCode = `window.BX_EXPOSED.setupGainNode(arguments[1], this.audioMediaStream),`; + + str = str.replace(text, text + newCode); + return str; + }, + + patchCombinedAudioVideoMediaStream(str: string) { + const text = '.srcObject=this.combinedAudioVideoStream'; + if (!str.includes(text)) { + return false; + } + + const newCode = `,window.BX_EXPOSED.setupGainNode(arguments[0], this.combinedAudioVideoStream)`; + str = str.replace(text, text + newCode); + return str; + }, }; let PATCH_ORDERS: PatchArray = [ @@ -501,6 +524,12 @@ let PLAYING_PATCH_ORDERS: PatchArray = [ 'patchStreamHud', 'playVibration', + // Patch volume control for normal stream + getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream', + // Patch volume control for combined audio+video stream + getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchCombinedAudioVideoMediaStream', + + STATES.hasTouchSupport && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'exposeTouchLayoutManager', STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'off' || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer', diff --git a/src/utils/bx-exposed.ts b/src/utils/bx-exposed.ts index 1ea5075..e63c35f 100644 --- a/src/utils/bx-exposed.ts +++ b/src/utils/bx-exposed.ts @@ -91,5 +91,26 @@ export const BxExposed = { BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY); return titleInfo; + }, + + setupGainNode: ($media: HTMLMediaElement, audioStream: MediaStream) => { + if ($media instanceof HTMLAudioElement) { + $media.muted = true; + $media.addEventListener('playing', e => { + $media.muted = true; + $media.pause(); + }); + } else { + $media.muted = true; + $media.addEventListener('playing', e => { + $media.muted = true; + }); + } + + const audioCtx = STATES.currentStream.audioContext!; + const source = audioCtx.createMediaStreamSource(audioStream); + + const gainNode = audioCtx.createGain(); // call monkey-patched createGain() in BxAudioContext + source.connect(gainNode).connect(audioCtx.destination); } }; diff --git a/src/utils/monkey-patches.ts b/src/utils/monkey-patches.ts index b725886..81ad97e 100644 --- a/src/utils/monkey-patches.ts +++ b/src/utils/monkey-patches.ts @@ -1,7 +1,6 @@ import { BxEvent } from "@utils/bx-event"; import { getPref, PrefKey } from "@utils/preferences"; import { STATES } from "@utils/global"; -import { UserAgent } from "@utils/user-agent"; import { BxLogger } from "@utils/bx-logger"; export function patchVideoApi() { @@ -104,10 +103,6 @@ export function patchRtcPeerConnection() { STATES.currentStream.peerConnection = conn; conn.addEventListener('connectionstatechange', e => { - if (conn.connectionState === 'connecting') { - STATES.currentStream.audioGainNode = null; - } - BxLogger.info('connectionstatechange', conn.connectionState); }); return conn; @@ -115,46 +110,23 @@ export function patchRtcPeerConnection() { } export function patchAudioContext() { - if (UserAgent.isSafari(true)) { - const nativeCreateGain = window.AudioContext.prototype.createGain; - window.AudioContext.prototype.createGain = function() { + const OrgAudioContext = window.AudioContext; + const nativeCreateGain = OrgAudioContext.prototype.createGain; + + // @ts-ignore + window.AudioContext = function(options?: AudioContextOptions | undefined): AudioContext { + const ctx = new OrgAudioContext(options); + BxLogger.info('patchAudioContext', ctx, options); + + ctx.createGain = function() { const gainNode = nativeCreateGain.apply(this); gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100; + STATES.currentStream.audioGainNode = gainNode; return gainNode; } - } - const OrgAudioContext = window.AudioContext; - // @ts-ignore - window.AudioContext = function() { - const ctx = new OrgAudioContext(); STATES.currentStream.audioContext = ctx; - STATES.currentStream.audioGainNode = null; return ctx; } - - const nativePlay = HTMLAudioElement.prototype.play; - HTMLAudioElement.prototype.play = function() { - this.muted = true; - - const promise = nativePlay.apply(this); - if (STATES.currentStream.audioGainNode) { - return promise; - } - - this.addEventListener('playing', e => (e.target as HTMLAudioElement).pause()); - - const audioCtx = STATES.currentStream.audioContext!; - // TOOD: check srcObject - const audioStream = audioCtx.createMediaStreamSource(this.srcObject as any); - const gainNode = audioCtx.createGain(); - - audioStream.connect(gainNode); - gainNode.connect(audioCtx.destination); - gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100; - STATES.currentStream.audioGainNode = gainNode; - - return promise; - } }