Skip to content

Commit d4ea34e

Browse files
committed
only load needed click sounds
1 parent c29c900 commit d4ea34e

1 file changed

Lines changed: 65 additions & 46 deletions

File tree

frontend/src/ts/controllers/sound-controller.ts

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,25 @@ import {
2020
} from "../constants/sounds";
2121

2222
async function gethowler(): Promise<typeof import("howler")> {
23-
return await import("howler");
23+
return import("howler");
24+
}
25+
26+
let isInit = false;
27+
const loadedBundles: Set<PlaySoundOnClick> = new Set();
28+
const howlers: Record<string, Howl> = {};
29+
30+
async function getHowl(src: string): Promise<Howl> {
31+
const cached = howlers[src];
32+
33+
if (cached !== undefined) return cached;
34+
35+
const Howl = (await gethowler()).Howl;
36+
const howl = new Howl({ src });
37+
howlers[src] = howl;
38+
39+
return howl;
2440
}
2541

26-
type ClickSounds = Record<
27-
string,
28-
{
29-
sounds: Howl[];
30-
counter: number;
31-
}[]
32-
>;
3342
type ErrorSounds = Record<
3443
Exclude<PlaySoundOnError, "off">,
3544
{
@@ -39,26 +48,19 @@ type ErrorSounds = Record<
3948
>;
4049

4150
let errorSounds: ErrorSounds | null = null;
42-
let clickSounds: ClickSounds | null = null;
4351

4452
let timeWarning: Howl | null = null;
4553

4654
let fartReverb: Howl | null = null;
4755

4856
async function initTimeWarning(): Promise<void> {
49-
const Howl = (await gethowler()).Howl;
5057
if (timeWarning !== null) return;
51-
timeWarning = new Howl({
52-
src: "../sound/timeWarning.wav",
53-
});
58+
timeWarning = await getHowl("../sound/timeWarning.wav");
5459
}
5560

5661
async function initFartReverb(): Promise<void> {
57-
const Howl = (await gethowler()).Howl;
5862
if (fartReverb !== null) return;
59-
fartReverb = new Howl({
60-
src: "../sound/fart-reverb.wav",
61-
});
63+
fartReverb = await getHowl("../sound/fart-reverb.wav");
6264
}
6365

6466
async function initErrorSound(): Promise<void> {
@@ -98,43 +100,59 @@ async function initErrorSound(): Promise<void> {
98100
}
99101

100102
async function init(): Promise<void> {
101-
const Howl = (await gethowler()).Howl;
102-
if (clickSounds !== null) return;
103-
clickSounds = Object.fromEntries(
104-
Object.entries(clickSoundConfig).map(([key, value]) => [
105-
key,
106-
value.map((it) => ({
107-
...it,
108-
sounds: it.sounds.map((src) => new Howl({ src })),
109-
})),
110-
]),
111-
);
112-
Howler.volume(Config.soundVolume);
103+
if (!isInit) {
104+
isInit = true;
105+
const howler = await gethowler();
106+
howler.Howler.volume(Config.soundVolume);
107+
}
108+
109+
//preload sounds
110+
const clickId = Config.playSoundOnClick;
111+
if (clickId === "off") return;
112+
113+
if (!loadedBundles.has(clickId)) {
114+
loadedBundles.add(clickId);
115+
116+
const config = clickSoundConfig[clickId];
117+
118+
if (config === undefined) return;
119+
120+
await Promise.all(
121+
config.flatMap((it) => it.sounds).map(async (it) => getHowl(it)),
122+
);
123+
}
124+
125+
//preload error sounds
126+
await initErrorSound();
113127
}
114128

115-
export async function previewClick(val: PlaySoundOnClick): Promise<void> {
116-
if (val === "off") return;
129+
export async function previewClick(clickId: PlaySoundOnClick): Promise<void> {
130+
if (clickId === "off") return;
117131

118-
const config = soundsConfig[val];
132+
const config = soundsConfig[clickId];
119133

120134
if ("oscillatorType" in config) {
121135
playNote({ codeOverride: "KeyQ", oscillatorType: config.oscillatorType });
122136
return;
123137
}
124138

125139
if ("validNotes" in config) {
126-
scaleConfigurations[val]?.preview();
140+
scaleConfigurations[clickId]?.preview();
127141
}
128142

129-
if (clickSounds === null) await init();
130-
131-
const safeClickSounds = clickSounds as ClickSounds;
143+
await init();
132144

133-
const clickSoundIds = Object.keys(safeClickSounds);
134-
if (!clickSoundIds.includes(val)) return;
145+
const safeClickSounds = clickSoundConfig[clickId];
146+
if (
147+
safeClickSounds === undefined ||
148+
safeClickSounds[0]?.sounds[0] === undefined
149+
) {
150+
return;
151+
}
135152

136-
safeClickSounds?.[val]?.[0]?.sounds[0]?.seek(0);
137-
safeClickSounds?.[val]?.[0]?.sounds[0]?.play();
153+
const howl = await getHowl(safeClickSounds[0]?.sounds[0]);
154+
howl.seek(0);
155+
howl.play();
138156
}
139157

140158
export async function previewError(val: PlaySoundOnError): Promise<void> {
@@ -257,7 +275,7 @@ function createPreviewScale(validNotes: ValidNotes[]): () => void {
257275
};
258276

259277
return async () => {
260-
if (clickSounds === null) await init();
278+
await init();
261279
playScale(validNotes, scale);
262280
};
263281
}
@@ -387,14 +405,15 @@ export async function playClick(codeOverride?: string): Promise<void> {
387405
return;
388406
}
389407

390-
if (clickSounds === null) await init();
391-
392-
const sounds = (clickSounds as ClickSounds)[Config.playSoundOnClick];
408+
await init();
393409

410+
const sounds = clickSoundConfig[val];
394411
if (sounds === undefined) throw new Error("Invalid click sound ID");
395-
396412
const randomSound = randomElementFromArray(sounds);
397-
const soundToPlay = randomSound.sounds[randomSound.counter] as Howl;
413+
414+
const src = randomSound.sounds[randomSound.counter];
415+
if (src === undefined) throw new Error("Invalid click sound ID");
416+
const soundToPlay = await getHowl(src);
398417

399418
randomSound.counter++;
400419
if (randomSound.counter === randomSound.sounds.length) {

0 commit comments

Comments
 (0)