mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Audio: consolidate sound system into SoundManager, fix bootstrap + master toggle
- Move sound catalog registration off PageScene mount and into a single SoundManager.install() call in GlobalSettingsManager so sounds exist at app boot (previously silent until PageScene mounted). - Honor the play_sounds master toggle in all five event playback sites via SoundManager.playEvent helpers. Settings preview keeps firing regardless (handled by SoundManager.playPreview). - Replace per-callsite GetAppPreferences + SoundRegistry boilerplate in the four generation event hooks and the file-deleted handler with one-line SoundManager.* calls. - Dedupe SOUND_OPTIONS: AudioSettingsPane now reads SoundManager.OPTIONS, derived from a single declarative SOUNDS array (label-bearing entries are user-selectable; others are still registered for code-driven use). - Narrow the soundboard public barrel to SoundManager + SoundOption only; SoundRegistry and SoundEffect are now lib-internal.
This commit is contained in:
1 parent
2b5e509a29
commit
bb7e631834
22 files changed
+183
-185
No files matched your search
@@ -13,6 +13,7 @@ import EnvironmentVariables from "~/Classes/EnvironmentVariables";
|
||||
import { pageHeight, pageWidth, persistLogin } from "~/signals";
|
||||
import { SyncStorytellerApiConfig } from "./api/SyncStorytellerApiConfig";
|
||||
import { posthog } from "posthog-js";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
|
||||
config.autoAddCss = false; /* eslint-disable import/first */
|
||||
|
||||
@@ -84,6 +85,10 @@ const GlobalSettingsManager = ({ env }: { env: Record<string, string> }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
SoundManager.install();
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { SoundRegistry, SoundEffect } from "@storyteller/soundboard";
|
||||
|
||||
// TODO: This is maintained in two places. Here and MiscSettingsPane.
|
||||
export const InstallSounds = () => {
|
||||
const r = SoundRegistry.getInstance();
|
||||
if (r.hasSound("decline_normal")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Good for simple menu choices
|
||||
r.setSoundOnce("click", new SoundEffect("resources/sound/smrpg_click.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("scifi_menu_beep_1", new SoundEffect("resources/sound/metroidprime_UI_15.wav", { defaultVolume: 0.3 }));
|
||||
r.setSoundOnce("scifi_menu_beep_2", new SoundEffect("resources/sound/metroidprime_UI_14.wav", { defaultVolume: 0.3 }));
|
||||
r.setSoundOnce("scifi_menu_select", new SoundEffect("resources/sound/metroidprime_UI_18.wav", { defaultVolume: 0.3 }));
|
||||
|
||||
// Good for simple immediate enqueue success
|
||||
r.setSoundOnce("done", new SoundEffect("resources/sound/oot_dialogue_done.wav", { defaultVolume: 0.4 })); // DEFAULT
|
||||
|
||||
// Good for simple immediate failure
|
||||
r.setSoundOnce("error_chirp", new SoundEffect("resources/sound/goldensun_135.wav", { defaultVolume: 0.4 }));
|
||||
r.setSoundOnce("spike_throw", new SoundEffect("resources/sound/smrpg_enemy_spikethrow.wav", { defaultVolume: 0.1 })); // DEFAULT
|
||||
r.setSoundOnce("giant_shell_kick", new SoundEffect("resources/sound/smrpg_mario_giantshellkick.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("wrong", new SoundEffect("resources/sound/smrpg_wrong.wav", { defaultVolume: 0.4 }));
|
||||
|
||||
// Good for simple async success
|
||||
r.setSoundOnce("special_flower", new SoundEffect("resources/sound/smrpg_specialflower.wav", { defaultVolume: 0.2 })); // DEFAULT
|
||||
r.setSoundOnce("extra_power", new SoundEffect("resources/sound/smrpg_character_extrapower.wav", { defaultVolume: 0.2 })); // DEFAULT
|
||||
|
||||
// Good for async errors
|
||||
r.setSoundOnce("crumble", new SoundEffect("resources/sound/smrpg_drybones_crumble.wav", { defaultVolume: 0.1 })); // DEFAULT
|
||||
r.setSoundOnce("ghost", new SoundEffect("resources/sound/smrpg_ghost.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("special_alert", new SoundEffect("resources/sound/goldensun_214.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("scifi_alert", new SoundEffect("resources/sound/metroidprime_UI_52.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("scifi_shrill_alert", new SoundEffect("resources/sound/metroidprime_UI_51.wav", { defaultVolume: 0.2 }));
|
||||
|
||||
// Good for menus
|
||||
r.setSoundOnce("next", new SoundEffect("resources/sound/oot_dialogue_next.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("select", new SoundEffect("resources/sound/goldensun_111.wav", { defaultVolume: 0.4 }));
|
||||
r.setSoundOnce("scifi_menu_open", new SoundEffect("resources/sound/metroidprime_UI_12.wav", { defaultVolume: 0.3 }));
|
||||
r.setSoundOnce("scifi_menu_close", new SoundEffect("resources/sound/metroidprime_UI_13.wav", { defaultVolume: 0.3 }));
|
||||
|
||||
// Good for special reward / celebration
|
||||
r.setSoundOnce("correct", new SoundEffect("resources/sound/smrpg_correct.wav", { defaultVolume: 0.1 }));
|
||||
r.setSoundOnce("flower", new SoundEffect("resources/sound/smrpg_flower.wav", { defaultVolume: 0.1 }));
|
||||
|
||||
// File delete, trash, etc.
|
||||
r.setSoundOnce("trash", new SoundEffect("resources/sound/oot_scrub_crumble.wav", { defaultVolume: 0.4 }));
|
||||
|
||||
// Misc / ungrouped
|
||||
r.setSoundOnce("accept_chirp", new SoundEffect("resources/sound/goldensun_101.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("accept_normal_level_1", new SoundEffect("resources/sound/goldensun_173.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("accept_normal_level_2", new SoundEffect("resources/sound/goldensun_174.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("accept_normal_level_3", new SoundEffect("resources/sound/goldensun_175.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("decline_chirp", new SoundEffect("resources/sound/goldensun_102.wav", { defaultVolume: 0.2 }));
|
||||
r.setSoundOnce("decline_normal", new SoundEffect("resources/sound/goldensun_113.wav", { defaultVolume: 0.2 }));
|
||||
|
||||
(window as any).sounds = r;
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { useEffect, useState } from "react";
|
||||
import * as gpu from "detect-gpu";
|
||||
import { UsersApi } from "~/Classes/ApiManager";
|
||||
import { PrecisionSelector } from "./comps/PrecisionSelector/PrecisionSelector";
|
||||
import { InstallSounds } from "~/pages/PageScene/InstallSounds";
|
||||
import { PageEditor } from "~/pages/PageScene/PageEditor";
|
||||
import { GalleryDragComponent } from "@storyteller/ui-gallery-modal";
|
||||
import {
|
||||
@@ -31,8 +30,7 @@ import { useGenerationEnqueueSuccessEvent } from "@storyteller/tauri-events";
|
||||
import { useGenerationFailedEvent } from "@storyteller/tauri-events";
|
||||
import { useTextToImageGenerationCompleteEvent } from "@storyteller/tauri-events";
|
||||
import { useTextToImageStore } from "~/pages/PageImage/TextToImageStore";
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
|
||||
export const PageScene = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
useSignals();
|
||||
@@ -110,20 +108,10 @@ export const PageScene = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
|
||||
useMediaFileDeletedEvent(async (event) => {
|
||||
console.log("Media file deleted event received:", event);
|
||||
const prefs = await GetAppPreferences();
|
||||
const soundName = prefs.preferences?.delete_file_sound;
|
||||
if (soundName !== undefined) {
|
||||
const registry = SoundRegistry.getInstance();
|
||||
registry.playSound(soundName);
|
||||
}
|
||||
await SoundManager.playFileDeleted();
|
||||
toast.error("File deleted.");
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log("installing event listeners");
|
||||
InstallSounds();
|
||||
});
|
||||
|
||||
const currentReminderModalProps = actionReminderProps.value;
|
||||
|
||||
return (
|
||||
|
||||
@@ -28,23 +28,14 @@
|
||||
"noEmit": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../libs/components/pagination"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/search"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/badge"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button-icon"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button-icon-select"
|
||||
"path": "../../libs/components/label"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/label"
|
||||
"path": "../../libs/components/button-icon-select"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button-dropdown"
|
||||
@@ -56,11 +47,14 @@
|
||||
"path": "../../libs/components/login-modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/soundboard"
|
||||
"path": "../../libs/components/badge"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/slider-v2"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/soundboard"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/pagedraw"
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
|
||||
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
|
||||
@@ -2,6 +2,9 @@
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common"
|
||||
},
|
||||
{
|
||||
"path": "../slider-v2"
|
||||
},
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../slider-v2/tsconfig.lib.json"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { faPlay } from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
@@ -11,35 +11,6 @@ import { Select, SelectValue } from "@storyteller/ui-select";
|
||||
import { Switch } from "@storyteller/ui-switch";
|
||||
import { Label } from "@storyteller/ui-label";
|
||||
|
||||
// TODO: This is maintained in two places. Here and InstallSounds.
|
||||
const SOUND_OPTIONS = [
|
||||
{ value: "none", label: "None (Silent)" }, // NB: MUST BE FIRST
|
||||
|
||||
// Other options
|
||||
{ label: "Click", value: "click" },
|
||||
{ label: "Correct", value: "correct" },
|
||||
{ label: "Crumble", value: "crumble" },
|
||||
{ label: "Dialog Done", value: "done" },
|
||||
{ label: "Dialog Next", value: "next" },
|
||||
{ label: "Error Chirp", value: "error_chirp" },
|
||||
{ label: "Flower", value: "flower" },
|
||||
{ label: "Ghost", value: "ghost" },
|
||||
{ label: "Sci-Fi Alert", value: "scifi_alert" },
|
||||
{ label: "Sci-Fi Menu Beep 1", value: "scifi_menu_beep_1" },
|
||||
{ label: "Sci-Fi Menu Beep 2", value: "scifi_menu_beep_2" },
|
||||
{ label: "Sci-Fi Menu Close", value: "scifi_menu_close" },
|
||||
{ label: "Sci-Fi Menu Open", value: "scifi_menu_open" },
|
||||
{ label: "Sci-Fi Menu Select", value: "scifi_menu_select" },
|
||||
{ label: "Sci-Fi Shrill Alert", value: "scifi_shrill_alert" },
|
||||
{ label: "Select", value: "select" },
|
||||
{ label: "Shell Kick", value: "giant_shell_kick" },
|
||||
{ label: "Special Alert", value: "special_alert" },
|
||||
{ label: "Special Flower", value: "special_flower" },
|
||||
{ label: "Spike Throw", value: "spike_throw" },
|
||||
{ label: "Trash", value: "trash" },
|
||||
{ label: "Wrong", value: "wrong" },
|
||||
];
|
||||
|
||||
interface AudioSettingsPaneProps {}
|
||||
|
||||
export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
@@ -83,7 +54,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
preference: PreferenceName.DeleteFileSound,
|
||||
value: sendVal,
|
||||
});
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
await reloadPreferences();
|
||||
};
|
||||
|
||||
@@ -93,7 +64,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
preference: PreferenceName.EnqueueSuccessSound,
|
||||
value: sendVal,
|
||||
});
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
await reloadPreferences();
|
||||
};
|
||||
|
||||
@@ -103,7 +74,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
preference: PreferenceName.EnqueueFailureSound,
|
||||
value: sendVal,
|
||||
});
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
await reloadPreferences();
|
||||
};
|
||||
|
||||
@@ -113,7 +84,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
preference: PreferenceName.GenerationSuccessSound,
|
||||
value: sendVal,
|
||||
});
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
await reloadPreferences();
|
||||
};
|
||||
|
||||
@@ -123,13 +94,13 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
preference: PreferenceName.GenerationFailureSound,
|
||||
value: sendVal,
|
||||
});
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
await reloadPreferences();
|
||||
};
|
||||
|
||||
const playSound = (val?: string) => {
|
||||
if (val !== undefined && val !== "none") {
|
||||
SoundRegistry.getInstance().playSound(val);
|
||||
SoundManager.playPreview(val);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,7 +121,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
id="success-sound"
|
||||
value={deleteFileSound}
|
||||
onChange={(val: SelectValue) => setDeleteFileSound(val as string)}
|
||||
options={SOUND_OPTIONS}
|
||||
options={SoundManager.OPTIONS}
|
||||
className="grow"
|
||||
/>
|
||||
<Button
|
||||
@@ -169,7 +140,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
id="success-sound"
|
||||
value={enqueueSuccessSound}
|
||||
onChange={(val: SelectValue) => setEnqueueSuccessSound(val as string)}
|
||||
options={SOUND_OPTIONS}
|
||||
options={SoundManager.OPTIONS}
|
||||
className="grow"
|
||||
/>
|
||||
<Button
|
||||
@@ -188,7 +159,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
id="success-sound"
|
||||
value={enqueueFailureSound}
|
||||
onChange={(val: SelectValue) => setEnqueueFailureSound(val as string)}
|
||||
options={SOUND_OPTIONS}
|
||||
options={SoundManager.OPTIONS}
|
||||
className="grow"
|
||||
/>
|
||||
<Button
|
||||
@@ -207,7 +178,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
id="success-sound"
|
||||
value={generationSuccessSound}
|
||||
onChange={(val: SelectValue) => setSuccessSound(val as string)}
|
||||
options={SOUND_OPTIONS}
|
||||
options={SoundManager.OPTIONS}
|
||||
className="grow"
|
||||
/>
|
||||
<Button
|
||||
@@ -226,7 +197,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => {
|
||||
id="failure-sound"
|
||||
value={generationFailureSound}
|
||||
onChange={(val: SelectValue) => setFailureSound(val as string)}
|
||||
options={SOUND_OPTIONS}
|
||||
options={SoundManager.OPTIONS}
|
||||
className="grow"
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -2,18 +2,15 @@
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../analytics/google-analytics"
|
||||
},
|
||||
{
|
||||
"path": "../modal"
|
||||
},
|
||||
{
|
||||
"path": "../../tauri-utils"
|
||||
},
|
||||
{
|
||||
"path": "../../model-list"
|
||||
},
|
||||
{
|
||||
"path": "../promptbox"
|
||||
},
|
||||
{
|
||||
"path": "../../state/subscription"
|
||||
},
|
||||
@@ -33,10 +30,10 @@
|
||||
"path": "../../soundboard"
|
||||
},
|
||||
{
|
||||
"path": "../../tauri-events"
|
||||
"path": "../input"
|
||||
},
|
||||
{
|
||||
"path": "../input"
|
||||
"path": "../../tauri-events"
|
||||
},
|
||||
{
|
||||
"path": "../login-modal"
|
||||
@@ -45,7 +42,7 @@
|
||||
"path": "../../api"
|
||||
},
|
||||
{
|
||||
"path": "../button"
|
||||
"path": "../../analytics/google-analytics"
|
||||
},
|
||||
{
|
||||
"path": "../label"
|
||||
@@ -53,6 +50,12 @@
|
||||
{
|
||||
"path": "../../tauri-api"
|
||||
},
|
||||
{
|
||||
"path": "../button"
|
||||
},
|
||||
{
|
||||
"path": "../modal"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
|
||||
@@ -43,18 +43,15 @@
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../analytics/google-analytics/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../tauri-utils/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../model-list/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../promptbox/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../state/subscription/tsconfig.lib.json"
|
||||
},
|
||||
@@ -74,10 +71,10 @@
|
||||
"path": "../../soundboard/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../tauri-events/tsconfig.lib.json"
|
||||
"path": "../input/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../input/tsconfig.lib.json"
|
||||
"path": "../../tauri-events/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../login-modal/tsconfig.lib.json"
|
||||
@@ -86,13 +83,19 @@
|
||||
"path": "../../api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../button/tsconfig.lib.json"
|
||||
"path": "../../analytics/google-analytics/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../label/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../tauri-api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../button/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../modal/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
|
||||
export * from './lib/SoundEffect';
|
||||
export * from './lib/SoundRegistry';
|
||||
export { SoundManager } from './lib/SoundManager';
|
||||
export type { SoundOption } from './lib/SoundManager';
|
||||
@@ -1,3 +1,4 @@
|
||||
// Internal to @storyteller/soundboard. Consumers go through SoundManager.
|
||||
import {Howl} from 'howler';
|
||||
|
||||
export interface Args {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundEffect } from "./SoundEffect";
|
||||
import { SoundRegistry } from "./SoundRegistry";
|
||||
|
||||
type SoundDef = {
|
||||
key: string;
|
||||
path: string;
|
||||
volume: number;
|
||||
// Present iff the sound is user-selectable in Settings → Alerts.
|
||||
// Sounds without a label are still registered and can be played by key.
|
||||
label?: string;
|
||||
};
|
||||
|
||||
const SOUNDS: readonly SoundDef[] = [
|
||||
// Menu choices
|
||||
{ key: "click", path: "resources/sound/smrpg_click.wav", volume: 0.2, label: "Click" },
|
||||
{ key: "scifi_menu_beep_1", path: "resources/sound/metroidprime_UI_15.wav", volume: 0.3, label: "Sci-Fi Menu Beep 1" },
|
||||
{ key: "scifi_menu_beep_2", path: "resources/sound/metroidprime_UI_14.wav", volume: 0.3, label: "Sci-Fi Menu Beep 2" },
|
||||
{ key: "scifi_menu_select", path: "resources/sound/metroidprime_UI_18.wav", volume: 0.3, label: "Sci-Fi Menu Select" },
|
||||
// Immediate enqueue success
|
||||
{ key: "done", path: "resources/sound/oot_dialogue_done.wav", volume: 0.4, label: "Dialog Done" },
|
||||
// Immediate failure
|
||||
{ key: "error_chirp", path: "resources/sound/goldensun_135.wav", volume: 0.4, label: "Error Chirp" },
|
||||
{ key: "spike_throw", path: "resources/sound/smrpg_enemy_spikethrow.wav", volume: 0.1, label: "Spike Throw" },
|
||||
{ key: "giant_shell_kick", path: "resources/sound/smrpg_mario_giantshellkick.wav", volume: 0.2, label: "Shell Kick" },
|
||||
{ key: "wrong", path: "resources/sound/smrpg_wrong.wav", volume: 0.4, label: "Wrong" },
|
||||
// Async success
|
||||
{ key: "special_flower", path: "resources/sound/smrpg_specialflower.wav", volume: 0.2, label: "Special Flower" },
|
||||
{ key: "extra_power", path: "resources/sound/smrpg_character_extrapower.wav", volume: 0.2 },
|
||||
// Async errors
|
||||
{ key: "crumble", path: "resources/sound/smrpg_drybones_crumble.wav", volume: 0.1, label: "Crumble" },
|
||||
{ key: "ghost", path: "resources/sound/smrpg_ghost.wav", volume: 0.2, label: "Ghost" },
|
||||
{ key: "special_alert", path: "resources/sound/goldensun_214.wav", volume: 0.2, label: "Special Alert" },
|
||||
{ key: "scifi_alert", path: "resources/sound/metroidprime_UI_52.wav", volume: 0.2, label: "Sci-Fi Alert" },
|
||||
{ key: "scifi_shrill_alert", path: "resources/sound/metroidprime_UI_51.wav", volume: 0.2, label: "Sci-Fi Shrill Alert" },
|
||||
// Menus
|
||||
{ key: "next", path: "resources/sound/oot_dialogue_next.wav", volume: 0.2, label: "Dialog Next" },
|
||||
{ key: "select", path: "resources/sound/goldensun_111.wav", volume: 0.4, label: "Select" },
|
||||
{ key: "scifi_menu_open", path: "resources/sound/metroidprime_UI_12.wav", volume: 0.3, label: "Sci-Fi Menu Open" },
|
||||
{ key: "scifi_menu_close", path: "resources/sound/metroidprime_UI_13.wav", volume: 0.3, label: "Sci-Fi Menu Close" },
|
||||
// Reward / celebration
|
||||
{ key: "correct", path: "resources/sound/smrpg_correct.wav", volume: 0.1, label: "Correct" },
|
||||
{ key: "flower", path: "resources/sound/smrpg_flower.wav", volume: 0.1, label: "Flower" },
|
||||
// Trash / delete
|
||||
{ key: "trash", path: "resources/sound/oot_scrub_crumble.wav", volume: 0.4, label: "Trash" },
|
||||
// Misc — registered but not user-selectable
|
||||
{ key: "accept_chirp", path: "resources/sound/goldensun_101.wav", volume: 0.2 },
|
||||
{ key: "accept_normal_level_1", path: "resources/sound/goldensun_173.wav", volume: 0.2 },
|
||||
{ key: "accept_normal_level_2", path: "resources/sound/goldensun_174.wav", volume: 0.2 },
|
||||
{ key: "accept_normal_level_3", path: "resources/sound/goldensun_175.wav", volume: 0.2 },
|
||||
{ key: "decline_chirp", path: "resources/sound/goldensun_102.wav", volume: 0.2 },
|
||||
{ key: "decline_normal", path: "resources/sound/goldensun_113.wav", volume: 0.2 },
|
||||
];
|
||||
|
||||
export type SoundOption = { value: string; label: string };
|
||||
|
||||
type SoundPrefKey =
|
||||
| "delete_file_sound"
|
||||
| "enqueue_success_sound"
|
||||
| "enqueue_failure_sound"
|
||||
| "generation_success_sound"
|
||||
| "generation_failure_sound";
|
||||
|
||||
export class SoundManager {
|
||||
// Dropdown options for AudioSettingsPane.
|
||||
// "None (Silent)" first, then user-selectable sounds A→Z by label.
|
||||
static readonly OPTIONS: SoundOption[] = [
|
||||
{ value: "none", label: "None (Silent)" },
|
||||
...SOUNDS
|
||||
.filter((s): s is SoundDef & { label: string } => !!s.label)
|
||||
.map((s) => ({ value: s.key, label: s.label }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
];
|
||||
|
||||
private static installed = false;
|
||||
|
||||
// Register the sound catalog. Idempotent — safe to call twice.
|
||||
static install() {
|
||||
if (this.installed) return;
|
||||
const r = SoundRegistry.getInstance();
|
||||
for (const s of SOUNDS) {
|
||||
r.setSoundOnce(s.key, new SoundEffect(s.path, { defaultVolume: s.volume }));
|
||||
}
|
||||
this.installed = true;
|
||||
}
|
||||
|
||||
// Settings preview — fires regardless of the master toggle.
|
||||
static playPreview(soundName: string) {
|
||||
if (!soundName || soundName === "none") return;
|
||||
SoundRegistry.getInstance().playSound(soundName);
|
||||
}
|
||||
|
||||
// Event-driven playback — gated on `play_sounds`.
|
||||
static async playFileDeleted() { await this.playEvent("delete_file_sound"); }
|
||||
static async playEnqueueSuccess() { await this.playEvent("enqueue_success_sound"); }
|
||||
static async playEnqueueFailure() { await this.playEvent("enqueue_failure_sound"); }
|
||||
static async playGenerationSuccess() { await this.playEvent("generation_success_sound"); }
|
||||
static async playGenerationFailure() { await this.playEvent("generation_failure_sound"); }
|
||||
|
||||
private static async playEvent(prefKey: SoundPrefKey) {
|
||||
const prefs = (await GetAppPreferences()).preferences;
|
||||
if (!prefs?.play_sounds) return;
|
||||
const soundName = prefs[prefKey];
|
||||
if (!soundName) return;
|
||||
SoundRegistry.getInstance().playSound(soundName);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// Internal to @storyteller/soundboard. Consumers go through SoundManager.
|
||||
import { SoundEffect } from "./SoundEffect";
|
||||
|
||||
export class SoundRegistry {
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../tauri-api"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
|
||||
@@ -44,5 +44,10 @@
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../tauri-api/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
import { toast } from "@storyteller/ui-toaster";
|
||||
import { GenerationAction, GenerationModel, GenerationServiceProvider } from "./common";
|
||||
import { BasicEventWrapper } from "../../common/BasicEventWrapper";
|
||||
@@ -20,12 +19,7 @@ export const useGenerationCompleteEvent = () => {
|
||||
const setup = async () => {
|
||||
unlisten = listen<BasicEventWrapper<GenerationCompleteEvent>>('generation-complete-event', async (event) => {
|
||||
console.log("Generation complete event received:", event);
|
||||
const prefs = await GetAppPreferences();
|
||||
const soundName = prefs.preferences?.generation_success_sound;
|
||||
if (soundName !== undefined) {
|
||||
const registry = SoundRegistry.getInstance();
|
||||
registry.playSound(soundName);
|
||||
}
|
||||
await SoundManager.playGenerationSuccess();
|
||||
const message = makeMessage(event.payload.data);
|
||||
toast.success(message);
|
||||
});
|
||||
|
||||
+2
-8
@@ -1,7 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
import { toast } from "@storyteller/ui-toaster";
|
||||
import { GenerationAction, GenerationModel, GenerationServiceProvider } from "./common";
|
||||
import { BasicEventWrapper } from "../../common/BasicEventWrapper";
|
||||
@@ -21,12 +20,7 @@ export const useGenerationEnqueueFailureEvent = () => {
|
||||
const setup = async () => {
|
||||
unlisten = listen<BasicEventWrapper<GenerationEnqueueFailureEvent>>('generation-enqueue-failure-event', async (event) => {
|
||||
console.log("Generation enqueue failure event received:", event);
|
||||
const prefs = await GetAppPreferences();
|
||||
const soundName = prefs.preferences?.enqueue_failure_sound;
|
||||
if (soundName !== undefined) {
|
||||
const registry = SoundRegistry.getInstance();
|
||||
registry.playSound(soundName);
|
||||
}
|
||||
await SoundManager.playEnqueueFailure();
|
||||
const message = makeMessage(event.payload.data);
|
||||
toast.error(message);
|
||||
});
|
||||
|
||||
+2
-8
@@ -1,7 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
import { toast } from "@storyteller/ui-toaster";
|
||||
import { GenerationAction, GenerationModel, GenerationServiceProvider } from "./common";
|
||||
import { BasicEventWrapper } from "../../common/BasicEventWrapper";
|
||||
@@ -20,12 +19,7 @@ export const useGenerationEnqueueSuccessEvent = () => {
|
||||
const setup = async () => {
|
||||
unlisten = listen<BasicEventWrapper<GenerationEnqueueSuccessEvent>>('generation-enqueue-success-event', async (event) => {
|
||||
console.log("Generation enqueue success event received:", event);
|
||||
const prefs = await GetAppPreferences();
|
||||
const soundName = prefs.preferences?.enqueue_success_sound;
|
||||
if (soundName !== undefined) {
|
||||
const registry = SoundRegistry.getInstance();
|
||||
registry.playSound(soundName);
|
||||
}
|
||||
await SoundManager.playEnqueueSuccess();
|
||||
const message = makeMessage(event.payload.data);
|
||||
toast.success(message);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { GetAppPreferences } from "@storyteller/tauri-api";
|
||||
import { SoundManager } from "@storyteller/soundboard";
|
||||
import { toast } from "@storyteller/ui-toaster";
|
||||
import { GenerationAction, GenerationModel, GenerationServiceProvider } from "./common";
|
||||
import { BasicEventWrapper } from "../../common/BasicEventWrapper";
|
||||
@@ -21,12 +20,7 @@ export const useGenerationFailedEvent = () => {
|
||||
const setup = async () => {
|
||||
unlisten = listen<BasicEventWrapper<GenerationFailedEvent>>('generation-failed-event', async (event) => {
|
||||
console.log("Generation failed event received:", event);
|
||||
const prefs = await GetAppPreferences();
|
||||
const soundName = prefs.preferences?.generation_failure_sound;
|
||||
if (soundName !== undefined) {
|
||||
const registry = SoundRegistry.getInstance();
|
||||
registry.playSound(soundName);
|
||||
}
|
||||
await SoundManager.playGenerationFailure();
|
||||
const message = makeMessage(event.payload.data);
|
||||
toast.error(message);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
{
|
||||
"path": "../components/toaster"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-api"
|
||||
},
|
||||
{
|
||||
"path": "../soundboard"
|
||||
},
|
||||
|
||||
@@ -46,9 +46,6 @@
|
||||
{
|
||||
"path": "../components/toaster/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../soundboard/tsconfig.lib.json"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user