diff --git a/frontend/apps/editor2d/tsconfig.json b/frontend/apps/editor2d/tsconfig.json index 59339b5499..8650ed60c3 100644 --- a/frontend/apps/editor2d/tsconfig.json +++ b/frontend/apps/editor2d/tsconfig.json @@ -25,6 +25,9 @@ { "path": "../../libs/tauri-utils" }, + { + "path": "../../libs/soundboard" + }, { "path": "./tsconfig.app.json" }, diff --git a/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_done.wav b/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_done.wav new file mode 100644 index 0000000000..6a34234919 Binary files /dev/null and b/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_done.wav differ diff --git a/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_next.wav b/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_next.wav new file mode 100644 index 0000000000..33fdd81998 Binary files /dev/null and b/frontend/apps/editor3d/app/public/resources/sound/oot_dialogue_next.wav differ diff --git a/frontend/apps/editor3d/app/public/resources/sound/smrpg_correct.wav b/frontend/apps/editor3d/app/public/resources/sound/smrpg_correct.wav new file mode 100644 index 0000000000..ccb8c51776 Binary files /dev/null and b/frontend/apps/editor3d/app/public/resources/sound/smrpg_correct.wav differ diff --git a/frontend/apps/editor3d/app/public/resources/sound/smrpg_drybones_crumble.wav b/frontend/apps/editor3d/app/public/resources/sound/smrpg_drybones_crumble.wav new file mode 100644 index 0000000000..7513d1f1ac Binary files /dev/null and b/frontend/apps/editor3d/app/public/resources/sound/smrpg_drybones_crumble.wav differ diff --git a/frontend/apps/editor3d/app/public/resources/sound/smrpg_flower.wav b/frontend/apps/editor3d/app/public/resources/sound/smrpg_flower.wav new file mode 100644 index 0000000000..f7b739399d Binary files /dev/null and b/frontend/apps/editor3d/app/public/resources/sound/smrpg_flower.wav differ diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx index 4bc27e6825..9a4b1f3c28 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx @@ -22,6 +22,7 @@ import { precisionSelectorValues, showPrecisionSelector, } from "./signals/precisionSelectorMenu"; +import { SoundRegistry, SoundEffect } from "@storyteller/soundboard"; export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { useSignals(); @@ -87,6 +88,16 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { }); }); + useEffect(() => { + const r = SoundRegistry.getInstance(); + r.setSoundOnce("success", new SoundEffect("resources/sound/smrpg_flower.wav")); + r.setSoundOnce("bingo", new SoundEffect("resources/sound/smrpg_correct.wav")); + r.setSoundOnce("next", new SoundEffect("resources/sound/oot_dialogue_next.wav", 2.0)); + r.setSoundOnce("done", new SoundEffect("resources/sound/oot_dialogue_done.wav", 2.0)); + r.setSoundOnce("fail", new SoundEffect("resources/sound/smrpg_drybones_crumble.wav")); + (window as any).sounds = r; + }); + if (validGpu === "unknown") { return ; } diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx index 16c5fb8186..4a8b061e67 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx @@ -45,6 +45,7 @@ import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; import { PromptsApi } from "@storyteller/api"; +import { SoundRegistry } from "@storyteller/soundboard"; import { cameras, @@ -439,6 +440,9 @@ export const PromptBox = () => { const isDesktop = IsDesktopApp(); console.log("Is this a desktop app?", isDesktop); + const r = SoundRegistry.getInstance(); + r.playSound("test"); + if (isDesktop) { await handleTauriEnqueue(); } else { diff --git a/frontend/apps/editor3d/tsconfig.json b/frontend/apps/editor3d/tsconfig.json index 34f623a1b8..f4535364ef 100644 --- a/frontend/apps/editor3d/tsconfig.json +++ b/frontend/apps/editor3d/tsconfig.json @@ -68,6 +68,9 @@ }, { "path": "../../libs/api" + }, + { + "path": "../../libs/soundboard" } ] } diff --git a/frontend/libs/soundboard/.babelrc b/frontend/libs/soundboard/.babelrc new file mode 100644 index 0000000000..1ea870ead4 --- /dev/null +++ b/frontend/libs/soundboard/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/frontend/libs/soundboard/.eslintrc.json b/frontend/libs/soundboard/.eslintrc.json new file mode 100644 index 0000000000..def19dc990 --- /dev/null +++ b/frontend/libs/soundboard/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": [ + "plugin:@nx/react", + "../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*", + "**/vite.config.*.timestamp*", + "**/vitest.config.*.timestamp*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} diff --git a/frontend/libs/soundboard/README.md b/frontend/libs/soundboard/README.md new file mode 100644 index 0000000000..6298bb5f80 --- /dev/null +++ b/frontend/libs/soundboard/README.md @@ -0,0 +1,7 @@ +# @frontend/soundboard + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test @frontend/soundboard` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/frontend/libs/soundboard/package.json b/frontend/libs/soundboard/package.json new file mode 100644 index 0000000000..333f216be6 --- /dev/null +++ b/frontend/libs/soundboard/package.json @@ -0,0 +1,23 @@ +{ + "name": "@storyteller/soundboard", + "version": "0.0.1", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "development": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "dependencies": { + "howler": "^2.2.4" + }, + "devDependencies": { + "@types/howler": "^2.2.12" + } +} diff --git a/frontend/libs/soundboard/src/index.ts b/frontend/libs/soundboard/src/index.ts new file mode 100644 index 0000000000..2003da994a --- /dev/null +++ b/frontend/libs/soundboard/src/index.ts @@ -0,0 +1,3 @@ + +export * from './lib/SoundEffect'; +export * from './lib/SoundRegistry'; diff --git a/frontend/libs/soundboard/src/lib/SoundEffect.ts b/frontend/libs/soundboard/src/lib/SoundEffect.ts new file mode 100644 index 0000000000..8e2cdda810 --- /dev/null +++ b/frontend/libs/soundboard/src/lib/SoundEffect.ts @@ -0,0 +1,23 @@ +import {Howl} from 'howler'; + +export class SoundEffect { + + readonly soundUrl: string; + readonly howlerSound: Howl; + readonly defaultVolume: number; + + constructor(soundUrl: string, defaultVolume?: number) { + this.soundUrl = soundUrl; + this.defaultVolume = defaultVolume || 1.0; + this.howlerSound = new Howl({ + src: [soundUrl], + autoplay: false, + loop: false, + volume: defaultVolume, + }); + } + + public play() { + this.howlerSound.play(); + } +} diff --git a/frontend/libs/soundboard/src/lib/SoundRegistry.ts b/frontend/libs/soundboard/src/lib/SoundRegistry.ts new file mode 100644 index 0000000000..5108343c95 --- /dev/null +++ b/frontend/libs/soundboard/src/lib/SoundRegistry.ts @@ -0,0 +1,32 @@ +import { SoundEffect } from "./SoundEffect"; + +export class SoundRegistry { + static #instance: SoundRegistry | undefined; + + #sounds: Map; + + private constructor() { + this.#sounds = new Map(); + } + + public static getInstance() { + if (SoundRegistry.#instance === undefined) { + SoundRegistry.#instance = new SoundRegistry(); + } + return SoundRegistry.#instance; + } + + public setSound(key: string, sound: SoundEffect) { + this.#sounds.set(key, sound); + } + + public setSoundOnce(key: string, sound: SoundEffect) { + if (!this.#sounds.has(key)) { + this.#sounds.set(key, sound); + } + } + + public playSound(key: string) { + this.#sounds.get(key)?.play() + } +} diff --git a/frontend/libs/soundboard/src/server.ts b/frontend/libs/soundboard/src/server.ts new file mode 100644 index 0000000000..82a91ac5e2 --- /dev/null +++ b/frontend/libs/soundboard/src/server.ts @@ -0,0 +1 @@ +// This file should be used to export ONLY server-code from the library. \ No newline at end of file diff --git a/frontend/libs/soundboard/src/test-setup.ts b/frontend/libs/soundboard/src/test-setup.ts new file mode 100644 index 0000000000..59f7836d9c --- /dev/null +++ b/frontend/libs/soundboard/src/test-setup.ts @@ -0,0 +1,3 @@ +import { installGlobals } from '@remix-run/node'; +import "@testing-library/jest-dom/matchers"; +installGlobals(); \ No newline at end of file diff --git a/frontend/libs/soundboard/tsconfig.json b/frontend/libs/soundboard/tsconfig.json new file mode 100644 index 0000000000..8c5fc025ff --- /dev/null +++ b/frontend/libs/soundboard/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../tsconfig.base.json" +} diff --git a/frontend/libs/soundboard/tsconfig.lib.json b/frontend/libs/soundboard/tsconfig.lib.json new file mode 100644 index 0000000000..c43e2edbaa --- /dev/null +++ b/frontend/libs/soundboard/tsconfig.lib.json @@ -0,0 +1,48 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts", + "vite/client" + ], + "rootDir": "src", + "jsx": "react-jsx", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo" + }, + "exclude": [ + "out-tsc", + "dist", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs" + ], + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx" + ] +} diff --git a/frontend/libs/soundboard/tsconfig.spec.json b/frontend/libs/soundboard/tsconfig.spec.json new file mode 100644 index 0000000000..c1faced82c --- /dev/null +++ b/frontend/libs/soundboard/tsconfig.spec.json @@ -0,0 +1,34 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/vitest", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ], + "jsx": "react-jsx" + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/frontend/libs/soundboard/vite.config.ts b/frontend/libs/soundboard/vite.config.ts new file mode 100644 index 0000000000..36aa0b79c2 --- /dev/null +++ b/frontend/libs/soundboard/vite.config.ts @@ -0,0 +1,49 @@ +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import dts from 'vite-plugin-dts'; +import * as path from 'path'; + +export default defineConfig(() => ({ +root: __dirname, +cacheDir: '../../node_modules/.vite/libs/soundboard', +plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })], +// Uncomment this if you are using workers. +// worker: { +// plugins: [ nxViteTsPaths() ], +// }, +// Configuration for building your library. +// See: https://vitejs.dev/guide/build.html#library-mode +build: { +outDir: './dist', +emptyOutDir: true, +reportCompressedSize: true, +commonjsOptions: { +transformMixedEsModules: true, +}, +lib: { +// Could also be a dictionary or array of multiple entry points. +entry: 'src/index.ts', +name: '@frontend/soundboard', +fileName: 'index', +// Change this to the formats you want to support. +// Don't forget to update your package.json as well. +formats: ['es' as const] +}, +rollupOptions: { +// External packages that should not be bundled into your library. +external: ['react','react-dom','react/jsx-runtime'] +}, +}, +test: {setupFiles: ['./src/test-setup.ts'], +watch: false, +globals: true, +environment: 'jsdom', +include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], +reporters: ['default'], +coverage: { +reportsDirectory: './test-output/vitest/coverage', +provider: 'v8' as const, +} +}, +})); \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ce9fee89b5..2e333a49b4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2243,6 +2243,16 @@ "name": "@frontend/login", "version": "0.0.1" }, + "libs/soundboard": { + "name": "@storyteller/soundboard", + "version": "0.0.1", + "dependencies": { + "howler": "^2.2.4" + }, + "devDependencies": { + "@types/howler": "^2.2.12" + } + }, "libs/tauri-utils": { "name": "@storyteller/tauri-utils", "version": "0.0.1" @@ -10253,6 +10263,10 @@ "resolved": "libs/api", "link": true }, + "node_modules/@storyteller/soundboard": { + "resolved": "libs/soundboard", + "link": true + }, "node_modules/@storyteller/tauri-utils": { "resolved": "libs/tauri-utils", "link": true @@ -11530,6 +11544,13 @@ "@types/unist": "^2" } }, + "node_modules/@types/howler": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.12.tgz", + "integrity": "sha512-hy769UICzOSdK0Kn1FBk4gN+lswcj1EKRkmiDtMkUGvFfYJzgaDXmVXkSShS2m89ERAatGIPnTUlp2HhfkVo5g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-proxy": { "version": "1.17.16", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", @@ -19392,6 +19413,12 @@ "dev": true, "license": "ISC" }, + "node_modules/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==", + "license": "MIT" + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", diff --git a/frontend/tsconfig.base.json b/frontend/tsconfig.base.json index b9d3fc546e..76fc721f9f 100644 --- a/frontend/tsconfig.base.json +++ b/frontend/tsconfig.base.json @@ -5,7 +5,10 @@ "emitDeclarationOnly": true, "importHelpers": true, "isolatedModules": true, - "lib": ["es2022", "dom"], + "lib": [ + "es2022", + "dom" + ], "module": "esnext", "moduleResolution": "bundler", "noEmitOnError": true, @@ -16,10 +19,17 @@ "skipLibCheck": true, "strict": true, "target": "es2022", - "customConditions": ["development"], + "customConditions": [ + "development" + ], "baseUrl": ".", "paths": { - "@frontend/login": ["libs/login/src/index.ts"] + "@frontend/login": [ + "libs/login/src/index.ts" + ], + "@frontend/soundboard": [ + "libs/soundboard/src/index.ts" + ] } } } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 41932f378f..f44f379a4e 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -59,6 +59,9 @@ }, { "path": "./libs/components/loading-spinner" + }, + { + "path": "./libs/soundboard" } ] }