Plugin

VolumeBooster

Allows you to set the user and stream volume above the default maximum

Voice Utility
index.ts
Download

Source

src/plugins/volumeBooster/index.ts
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { definePluginSettings } from "@api/Settings";
8import { Devs } from "@utils/constants";
9import definePlugin, { makeRange, OptionType } from "@utils/types";
10
11const settings = definePluginSettings({
12 multiplier: {
13 description: "Volume Multiplier",
14 type: OptionType.SLIDER,
15 markers: makeRange(1, 5, 0.5),
16 default: 2,
17 stickToMarkers: true,
18 }
19});
20
21interface StreamData {
22 audioContext: AudioContext,
23 audioElement: HTMLAudioElement,
24 emitter: any,
25 // added by this plugin
26 gainNode?: GainNode,
27 id: string,
28 levelNode: AudioWorkletNode,
29 sinkId: string | "default",
30 stream: MediaStream,
31 streamSourceNode?: MediaStreamAudioSourceNode,
32 videoStreamId: string,
33 _mute: boolean,
34 _speakingFlags: number,
35 _volume: number;
36}
37
38export default definePlugin({
39 name: "VolumeBooster",
40 authors: [Devs.Nuckyz, Devs.sadan],
41 description: "Allows you to set the user and stream volume above the default maximum",
42 tags: ["Voice", "Utility"],
43 settings,
44
45 patches: [
46 // Change the max volume for sliders to allow for values above 200
47 {
48 find: "#{intl::USER_VOLUME}",
49 replacement: {
50 match: /(?<=maxValue:)\i\.isPlatformEmbedded\?(\i\.\i):\i\.\i(?=,)/,
51 replace: (_, higherMaxVolume) => `${higherMaxVolume}*$self.settings.store.multiplier`
52 }
53 },
54 // Change the max volume for sliders to allow for values above 200
55 {
56 find: "currentVolume:",
57 replacement: {
58 match: /(?<=maxValue:)\i\.\i\?(\d+?):\d+?(?=,)/,
59 replace: (_, higherMaxVolume) => `${higherMaxVolume}*$self.settings.store.multiplier`
60 }
61 },
62 // Patches needed for web/vesktop
63 {
64 find: "streamSourceNode",
65 predicate: () => !IS_DISCORD_DESKTOP,
66 group: true,
67 replacement: [
68 // Remove rounding algorithm
69 {
70 match: /Math\.max.{0,30}\)\)/,
71 replace: "arguments[0]"
72 },
73 // Fix streams not playing audio until you update them
74 {
75 match: /\}return"video"/,
76 replace: "this.updateAudioElement();$&"
77 },
78 // Patch the volume
79 {
80 match: /\.volume=this\._volume\/100;/,
81 replace: ".volume=0.00;$self.patchVolume(this);"
82 }
83 ]
84 },
85 // Prevent Audio Context Settings sync from trying to sync with values above 200, changing them to 200 before we send to Discord
86 {
87 find: "AudioContextSettingsMigrated",
88 replacement: [
89 {
90 match: /(?<=isLocalMute\(\i,\i\),volume:(\i).+?\(0,\i\.\i\)\(\i,\i,\{volume:)\1(?=\}\))/,
91 replace: "$&>200?200:$&"
92 },
93 {
94 match: /(?<=Object\.entries\(\i\.localMutes\).+?volume:).+?(?=,)/,
95 replace: "$&>200?200:$&"
96 },
97 {
98 match: /(?<=Object\.entries\(\i\.localVolumes\).+?volume:).+?(?=})/,
99 replace: "$&>200?200:$&"
100 }
101 ]
102 },
103 // Prevent the MediaEngineStore from overwriting our LocalVolumes above 200 with the ones the Discord Audio Context Settings sync sends
104 {
105 find: &#039;="MediaEngineStore",&#039;,
106 replacement: [
107 {
108 match: /(\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/,
109 replace: (_, rest1, localVolume, syncVolume, rest2) => rest1
110 + `(${localVolume}>200?void 0:${localVolume}=${syncVolume})`
111 + rest2
112 + `${localVolume}??${syncVolume})`
113 }
114 ]
115 }
116 ],
117
118 patchVolume(data: StreamData) {
119 if (data.stream.getAudioTracks().length === 0) return;
120
121 data.streamSourceNode ??= data.audioContext.createMediaStreamSource(data.stream);
122
123 if (!data.gainNode) {
124 const gain = data.gainNode = data.audioContext.createGain();
125 data.streamSourceNode.connect(gain);
126 gain.connect(data.audioContext.destination);
127 }
128
129 // @ts-expect-error
130 if (data.sinkId != null && data.sinkId !== data.audioContext.sinkId && "setSinkId" in AudioContext.prototype) {
131 // @ts-expect-error https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/setSinkId
132 data.audioContext.setSinkId(data.sinkId === "default" ? "" : data.sinkId);
133 }
134
135 data.gainNode.gain.value = data._mute
136 ? 0
137 : data._volume / 100;
138 }
139});
140