diff --git a/frontend/__tests__/root/config.spec.ts b/frontend/__tests__/root/config.spec.ts
index f6ceacf8b..f208ede55 100644
--- a/frontend/__tests__/root/config.spec.ts
+++ b/frontend/__tests__/root/config.spec.ts
@@ -19,8 +19,8 @@ describe("Config", () => {
expect(Config.setPlaySoundOnClick("invalid" as any)).toBe(false);
});
it("setSoundVolume", () => {
- expect(Config.setSoundVolume("0.1")).toBe(true);
- expect(Config.setSoundVolume("1.0")).toBe(true);
+ expect(Config.setSoundVolume(0.1)).toBe(true);
+ expect(Config.setSoundVolume(1.0)).toBe(true);
expect(Config.setSoundVolume("invalid" as any)).toBe(false);
});
it("setDifficulty", () => {
diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html
index ab0922570..4cc0680c5 100644
--- a/frontend/src/html/pages/settings.html
+++ b/frontend/src/html/pages/settings.html
@@ -581,10 +581,11 @@
sound volume
Change the volume of the sound effects.
-
diff --git a/frontend/src/ts/commandline/lists/sound-volume.ts b/frontend/src/ts/commandline/lists/sound-volume.ts
index e2ad27552..2cac406b8 100644
--- a/frontend/src/ts/commandline/lists/sound-volume.ts
+++ b/frontend/src/ts/commandline/lists/sound-volume.ts
@@ -10,7 +10,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = {
display: "quiet",
configValue: "0.1",
exec: (): void => {
- UpdateConfig.setSoundVolume("0.1");
+ UpdateConfig.setSoundVolume(0.1);
void SoundController.playClick();
},
},
@@ -19,7 +19,7 @@ const subgroup: MonkeyTypes.CommandsSubgroup = {
display: "medium",
configValue: "0.5",
exec: (): void => {
- UpdateConfig.setSoundVolume("0.5");
+ UpdateConfig.setSoundVolume(0.5);
void SoundController.playClick();
},
},
@@ -28,7 +28,17 @@ const subgroup: MonkeyTypes.CommandsSubgroup = {
display: "loud",
configValue: "1.0",
exec: (): void => {
- UpdateConfig.setSoundVolume("1.0");
+ UpdateConfig.setSoundVolume(1.0);
+ void SoundController.playClick();
+ },
+ },
+ {
+ id: "setSoundVolumeCustom",
+ display: "custom...",
+ input: true,
+ exec: ({ input }): void => {
+ if (input === undefined || input === "") return;
+ UpdateConfig.setSoundVolume(parseFloat(input));
void SoundController.playClick();
},
},
diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts
index 51052033b..6657b6238 100644
--- a/frontend/src/ts/config.ts
+++ b/frontend/src/ts/config.ts
@@ -182,6 +182,11 @@ export function setSoundVolume(
val: ConfigSchemas.SoundVolume,
nosave?: boolean
): boolean {
+ if (val < 0 || val > 1) {
+ Notifications.add("Sound volume must be between 0 and 1", 0);
+ val = 0.5;
+ }
+
if (
!isConfigValueValid("sound volume", val, ConfigSchemas.SoundVolumeSchema)
) {
@@ -2176,6 +2181,10 @@ function replaceLegacyValues(
configObj.liveAccStyle = val;
}
+ if (typeof configObj.soundVolume === "string") {
+ configObj.soundVolume = parseFloat(configObj.soundVolume);
+ }
+
return configObj;
}
diff --git a/frontend/src/ts/constants/default-config.ts b/frontend/src/ts/constants/default-config.ts
index 9b252847b..dccf78541 100644
--- a/frontend/src/ts/constants/default-config.ts
+++ b/frontend/src/ts/constants/default-config.ts
@@ -68,7 +68,7 @@ export default {
capsLockWarning: true,
playSoundOnError: "off",
playSoundOnClick: "off",
- soundVolume: "0.5",
+ soundVolume: 0.5,
startGraphsAtZero: true,
showOutOfFocusWarning: true,
paceCaret: "off",
diff --git a/frontend/src/ts/controllers/sound-controller.ts b/frontend/src/ts/controllers/sound-controller.ts
index 58915da08..04a08777f 100644
--- a/frontend/src/ts/controllers/sound-controller.ts
+++ b/frontend/src/ts/controllers/sound-controller.ts
@@ -81,7 +81,7 @@ async function initErrorSound(): Promise {
},
],
};
- Howler.volume(parseFloat(Config.soundVolume));
+ Howler.volume(Config.soundVolume);
}
async function init(): Promise {
@@ -387,7 +387,7 @@ async function init(): Promise {
},
],
};
- Howler.volume(parseFloat(Config.soundVolume));
+ Howler.volume(Config.soundVolume);
}
export async function previewClick(val: string): Promise {
@@ -591,7 +591,7 @@ function playScale(scale: ValidScales, scaleMeta: ScaleData): void {
const gainNode = audioCtx.createGain();
oscillatorNode.type = "sine";
- gainNode.gain.value = parseFloat(Config.soundVolume) / 10;
+ gainNode.gain.value = Config.soundVolume / 10;
oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillatorNode.frequency.value = currentFrequency;
@@ -626,7 +626,7 @@ export function playNote(
clickSoundIdsToOscillatorType[
Config.playSoundOnClick as DynamicClickSounds
];
- gainNode.gain.value = parseFloat(Config.soundVolume) / 10;
+ gainNode.gain.value = Config.soundVolume / 10;
oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
@@ -695,5 +695,7 @@ function setVolume(val: number): void {
ConfigEvent.subscribe((eventKey, eventValue) => {
if (eventKey === "playSoundOnClick" && eventValue !== "off") void init();
- if (eventKey === "soundVolume") setVolume(parseFloat(eventValue as string));
+ if (eventKey === "soundVolume") {
+ setVolume(parseFloat(eventValue as string));
+ }
});
diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts
index 0aa85e7e7..306fcf549 100644
--- a/frontend/src/ts/pages/settings.ts
+++ b/frontend/src/ts/pages/settings.ts
@@ -250,7 +250,7 @@ async function initGroups(): Promise {
groups["soundVolume"] = new SettingsGroup(
"soundVolume",
UpdateConfig.setSoundVolume,
- "button"
+ "range"
) as SettingsGroup;
groups["playSoundOnError"] = new SettingsGroup(
"playSoundOnError",
diff --git a/packages/contracts/src/schemas/configs.ts b/packages/contracts/src/schemas/configs.ts
index ace674f8b..f3c2e360e 100644
--- a/packages/contracts/src/schemas/configs.ts
+++ b/packages/contracts/src/schemas/configs.ts
@@ -120,7 +120,7 @@ export const PlaySoundOnClickSchema = z.enum([
]);
export type PlaySoundOnClick = z.infer;
-export const SoundVolumeSchema = z.enum(["0.1", "0.5", "1.0"]);
+export const SoundVolumeSchema = z.number().min(0).max(1);
export type SoundVolume = z.infer;
export const PaceCaretSchema = z.enum([