diff --git a/apps/stage-tamagotchi/src/main/index.ts b/apps/stage-tamagotchi/src/main/index.ts index b0116fc23..b27f479d8 100644 --- a/apps/stage-tamagotchi/src/main/index.ts +++ b/apps/stage-tamagotchi/src/main/index.ts @@ -1,5 +1,3 @@ -import type { BrowserWindow } from 'electron' - import { env, platform } from 'node:process' import { electronApp, optimizer } from '@electron-toolkit/utils' @@ -79,12 +77,6 @@ app.whenReady().then(async () => { // BeatSync will create a background window to capture and process audio. const beatSync = injeca.provide('windows:beat-sync', () => setupBeatSync()) - const beatSyncDispatchTo = injeca.provide('windows:beat-sync:dispatch-to', { - dependsOn: { beatSync }, - build: async ({ dependsOn }) => (window: BrowserWindow) => { - return dependsOn.beatSync.dispatchTo(window) - }, - }) const chatWindow = injeca.provide('windows:chat', { dependsOn: { widgetsManager }, @@ -92,17 +84,13 @@ app.whenReady().then(async () => { }) const settingsWindow = injeca.provide('windows:settings', { - dependsOn: { widgetsManager, beatSync, beatSyncDispatchTo }, - build: async ({ dependsOn }) => { - return setupSettingsWindowReusableFunc({ ...dependsOn, onWindowCreated: window => dependsOn.beatSyncDispatchTo(window) }) - }, + dependsOn: { widgetsManager, beatSync }, + build: async ({ dependsOn }) => setupSettingsWindowReusableFunc(dependsOn), }) const mainWindow = injeca.provide('windows:main', { - dependsOn: { settingsWindow, chatWindow, widgetsManager, noticeWindow, beatSync, beatSyncDispatchTo }, - build: async ({ dependsOn }) => { - return setupMainWindow({ ...dependsOn, onWindowCreated: window => dependsOn.beatSyncDispatchTo(window) }) - }, + dependsOn: { settingsWindow, chatWindow, widgetsManager, noticeWindow, beatSync }, + build: async ({ dependsOn }) => setupMainWindow(dependsOn), }) const captionWindow = injeca.provide('windows:caption', { diff --git a/apps/stage-tamagotchi/src/main/tray/index.ts b/apps/stage-tamagotchi/src/main/tray/index.ts index c35522d73..6653316b6 100644 --- a/apps/stage-tamagotchi/src/main/tray/index.ts +++ b/apps/stage-tamagotchi/src/main/tray/index.ts @@ -54,7 +54,7 @@ export function setupTray(params: { ...is.dev || env.MAIN_APP_DEBUG || env.APP_DEBUG ? [ { type: 'header', label: 'DevTools' }, - { label: 'Troubleshoot BeatSync...', click: () => params.beatSyncBgWindow.window.webContents.openDevTools() }, + { label: 'Troubleshoot BeatSync...', click: () => params.beatSyncBgWindow.webContents.openDevTools() }, { type: 'separator' }, ] as const // :( : [], diff --git a/apps/stage-tamagotchi/src/main/windows/beat-sync/index.ts b/apps/stage-tamagotchi/src/main/windows/beat-sync/index.ts index b62782bfd..8f30de581 100644 --- a/apps/stage-tamagotchi/src/main/windows/beat-sync/index.ts +++ b/apps/stage-tamagotchi/src/main/windows/beat-sync/index.ts @@ -1,26 +1,8 @@ import { dirname, join, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { defineInvoke, defineInvokeHandler } from '@moeru/eventa' -import { createContext } from '@moeru/eventa/adapters/electron/main' -import { - beatSyncBeatSignaledInvokeEventa, - beatSyncGetInputByteFrequencyDataInvokeEventa, - beatSyncGetStateInvokeEventa, - beatSyncStateChangedInvokeEventa, - beatSyncToggleInvokeEventa, - beatSyncUpdateParametersInvokeEventa, -} from '@proj-airi/stage-shared/beat-sync' -import { BrowserWindow, ipcMain } from 'electron' +import { BrowserWindow } from 'electron' -import { - beatSyncElectronChangeState, - beatSyncElectronGetInputByteFrequencyData, - beatSyncElectronGetState, - beatSyncElectronSignalBeat, - beatSyncElectronToggle, - beatSyncElectronUpdateParameters, -} from '../../../shared/eventa' import { baseUrl, getElectronMainDirname, load } from '../../libs/electron/location' export async function setupBeatSync() { @@ -31,37 +13,7 @@ export async function setupBeatSync() { sandbox: false, }, }) - const context = createContext(ipcMain, window).context - - // TODO(Makito): Bypass here with the broadcast channel-based transport when it is released. - // [main] -> [renderer] beat-sync - const toggle = defineInvoke(context, beatSyncElectronToggle) as (enabled: boolean) => Promise // TODO: Better type - const getState = defineInvoke(context, beatSyncElectronGetState) - const updateParameters = defineInvoke(context, beatSyncElectronUpdateParameters) - const getInputByteFrequencyData = defineInvoke(context, beatSyncElectronGetInputByteFrequencyData) await load(window, baseUrl(resolve(getElectronMainDirname(), '..', 'renderer'), 'beat-sync.html')) - return { - window, - dispatchTo: (window: BrowserWindow) => { - const context = createContext(ipcMain, window).context - - const stateChanged = defineInvoke(context, beatSyncStateChangedInvokeEventa) - const beatSignaled = defineInvoke(context, beatSyncBeatSignaledInvokeEventa) - const removeHandlerFns = [ - // [renderer] beat-sync -> [main] -> [renderer] index (Events) - defineInvokeHandler(context, beatSyncElectronChangeState, async e => stateChanged(e)), - defineInvokeHandler(context, beatSyncElectronSignalBeat, async e => beatSignaled(e)), - - // [renderer] index -> [main] -> [renderer] beat-sync (Functions) - defineInvokeHandler(context, beatSyncToggleInvokeEventa, async enabled => toggle(enabled)), - defineInvokeHandler(context, beatSyncGetStateInvokeEventa, async () => getState()), - defineInvokeHandler(context, beatSyncUpdateParametersInvokeEventa, async params => updateParameters(params)), - defineInvokeHandler(context, beatSyncGetInputByteFrequencyDataInvokeEventa, async () => getInputByteFrequencyData()), - ] - const removeHandlers = () => removeHandlerFns.forEach(fn => fn()) - window.on('closed', () => removeHandlers()) - return removeHandlers - }, - } + return window } diff --git a/apps/stage-tamagotchi/src/renderer/beat-sync.main.ts b/apps/stage-tamagotchi/src/renderer/beat-sync.main.ts index 00e2e5c8a..388f8645d 100644 --- a/apps/stage-tamagotchi/src/renderer/beat-sync.main.ts +++ b/apps/stage-tamagotchi/src/renderer/beat-sync.main.ts @@ -1,24 +1,22 @@ import { defineInvoke, defineInvokeHandler } from '@moeru/eventa' -import { createContext } from '@moeru/eventa/adapters/electron/renderer' import { StageEnvironment } from '@proj-airi/stage-shared' -import { createBeatSyncDetector } from '@proj-airi/stage-shared/beat-sync/browser' - import { - beatSyncElectronChangeState, - beatSyncElectronGetInputByteFrequencyData, - beatSyncElectronGetState, - beatSyncElectronSignalBeat, - beatSyncElectronToggle, - beatSyncElectronUpdateParameters, -} from '../shared/eventa' + beatSyncBeatSignaledInvokeEventa, + beatSyncGetInputByteFrequencyDataInvokeEventa, + beatSyncGetStateInvokeEventa, + beatSyncStateChangedInvokeEventa, + beatSyncToggleInvokeEventa, + beatSyncUpdateParametersInvokeEventa, + createBeatSyncDetector, + createContext, +} from '@proj-airi/stage-shared/beat-sync' const { ipcRenderer } = window.electron -const context = createContext(ipcRenderer).context +const context = createContext() -// [renderer] beat-sync -> [main] -> [renderer] index -const changeState = defineInvoke(context, beatSyncElectronChangeState) -const signalBeat = defineInvoke(context, beatSyncElectronSignalBeat) +const changeState = defineInvoke(context, beatSyncStateChangedInvokeEventa) +const signalBeat = defineInvoke(context, beatSyncBeatSignaledInvokeEventa) function enableLoopbackAudio() { // electron-audio-loopback currently registers this handler internally @@ -43,7 +41,7 @@ detector.on('beat', (e) => { signalBeat(e) }) -defineInvokeHandler(context, beatSyncElectronToggle, async (enabled) => { +defineInvokeHandler(context, beatSyncToggleInvokeEventa, async (enabled) => { // eslint-disable-next-line no-console console.log('[toggle]', enabled) if (enabled) { @@ -53,13 +51,13 @@ defineInvokeHandler(context, beatSyncElectronToggle, async (enabled) => { detector.stop() } }) -defineInvokeHandler(context, beatSyncElectronGetState, async () => detector.state) -defineInvokeHandler(context, beatSyncElectronUpdateParameters, async (params) => { +defineInvokeHandler(context, beatSyncGetStateInvokeEventa, async () => detector.state) +defineInvokeHandler(context, beatSyncUpdateParametersInvokeEventa, async (params) => { // eslint-disable-next-line no-console console.log('[update-params]', params) detector.updateParameters(params) }) -defineInvokeHandler(context, beatSyncElectronGetInputByteFrequencyData, async () => { +defineInvokeHandler(context, beatSyncGetInputByteFrequencyDataInvokeEventa, async () => { // eslint-disable-next-line no-console console.debug('[get-input-byte-frequency-data]') // This could be noisy. return detector.getInputByteFrequencyData() diff --git a/apps/stage-tamagotchi/src/shared/eventa.ts b/apps/stage-tamagotchi/src/shared/eventa.ts index d5b469e08..38183462b 100644 --- a/apps/stage-tamagotchi/src/shared/eventa.ts +++ b/apps/stage-tamagotchi/src/shared/eventa.ts @@ -74,11 +74,4 @@ export const widgetsRemoveEvent = defineEventa<{ id: string }>('eventa:event:ele export const widgetsClearEvent = defineEventa('eventa:event:electron:windows:widgets:clear') export const widgetsUpdateEvent = defineEventa<{ id: string, componentProps?: Record }>('eventa:event:electron:windows:widgets:update') -export const beatSyncElectronSignalBeat = defineInvokeEventa('eventa:event:electron:beat-sync:electron:signal-beat') -export const beatSyncElectronChangeState = defineInvokeEventa('eventa:event:electron:beat-sync:electron:change-state') -export const beatSyncElectronGetState = defineInvokeEventa('eventa:event:electron:beat-sync:electron:get-state') -export const beatSyncElectronToggle = defineInvokeEventa('eventa:event:electron:beat-sync:electron:toggle') -export const beatSyncElectronUpdateParameters = defineInvokeEventa>('eventa:event:electron:beat-sync:electron:update-parameters') -export const beatSyncElectronGetInputByteFrequencyData = defineInvokeEventa>('eventa:event:electron:beat-sync:electron:get-input-byte-frequency-data') - export { electron } from './electron' diff --git a/packages/stage-pages/src/pages/settings/modules/beat-sync.vue b/packages/stage-pages/src/pages/settings/modules/beat-sync.vue index b6ee36458..deeee8ac1 100644 --- a/packages/stage-pages/src/pages/settings/modules/beat-sync.vue +++ b/packages/stage-pages/src/pages/settings/modules/beat-sync.vue @@ -10,7 +10,7 @@ import { listenBeatSyncStateChange, toggleBeatSync, updateBeatSyncParameters, -} from '@proj-airi/stage-shared/beat-sync/browser' +} from '@proj-airi/stage-shared/beat-sync' import { Alert, AudioSpectrumVisualizer } from '@proj-airi/stage-ui/components' import { Button, FieldCheckbox, FieldRange } from '@proj-airi/ui' import { createTimeline } from 'animejs' diff --git a/packages/stage-shared/package.json b/packages/stage-shared/package.json index e2035030c..d5094b038 100644 --- a/packages/stage-shared/package.json +++ b/packages/stage-shared/package.json @@ -17,7 +17,6 @@ "exports": { ".": "./src/index.ts", "./beat-sync": "./src/beat-sync/index.ts", - "./beat-sync/browser": "./src/beat-sync/browser/index.ts", "./electron-renderer": "./src/electron-renderer.d.ts" }, "scripts": { diff --git a/packages/stage-shared/src/beat-sync/browser/eventa.ts b/packages/stage-shared/src/beat-sync/browser/eventa.ts deleted file mode 100644 index 0844f0f6a..000000000 --- a/packages/stage-shared/src/beat-sync/browser/eventa.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext as createWebContext } from '@moeru/eventa' -import { createContext as createElectronRendererContext } from '@moeru/eventa/adapters/electron/renderer' -import { isElectronWindow } from '@proj-airi/stage-shared' - -export function createContext() { - if (isElectronWindow(window)) { - // TODO(Makito): Replace this with the broadcast channel-based transport when it is released. - return createElectronRendererContext(window.electron.ipcRenderer).context - } - else { - return createWebContext() - } -} diff --git a/packages/stage-shared/src/beat-sync/browser/index.ts b/packages/stage-shared/src/beat-sync/browser/index.ts deleted file mode 100644 index 178ff3657..000000000 --- a/packages/stage-shared/src/beat-sync/browser/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './detector' -export * from './eventa' diff --git a/packages/stage-shared/src/beat-sync/browser/detector.ts b/packages/stage-shared/src/beat-sync/detector.ts similarity index 98% rename from packages/stage-shared/src/beat-sync/browser/detector.ts rename to packages/stage-shared/src/beat-sync/detector.ts index aca150a26..caab03900 100644 --- a/packages/stage-shared/src/beat-sync/browser/detector.ts +++ b/packages/stage-shared/src/beat-sync/detector.ts @@ -1,14 +1,14 @@ import type { EventContext } from '@moeru/eventa' import type { Analyser, AnalyserBeatEvent, AnalyserWorkletParameters } from '@nekopaw/tempora' -import type { BeatSyncDetectorEventMap, BeatSyncDetectorState } from '../types' +import type { BeatSyncDetectorEventMap, BeatSyncDetectorState } from './types' import analyserWorklet from '@nekopaw/tempora/worklet?url' import { defineInvoke, defineInvokeHandler } from '@moeru/eventa' import { startAnalyser as startTemporaAnalyser } from '@nekopaw/tempora' -import { isStageTamagotchi, isStageWeb, StageEnvironment } from '../../environment' +import { isStageTamagotchi, isStageWeb, StageEnvironment } from '../environment' import { beatSyncBeatSignaledInvokeEventa, beatSyncGetInputByteFrequencyDataInvokeEventa, @@ -16,8 +16,6 @@ import { beatSyncStateChangedInvokeEventa, beatSyncToggleInvokeEventa, beatSyncUpdateParametersInvokeEventa, -} from '../eventa' -import { createContext, } from './eventa' @@ -260,7 +258,7 @@ export function toggleBeatSync(enabled: boolean) { } if (isStageTamagotchi()) { - const toggleFn = defineInvoke(getContext(), beatSyncToggleInvokeEventa) as (enabled: boolean) => Promise // TODO: Better type + const toggleFn = defineInvoke(getContext(), beatSyncToggleInvokeEventa) return toggleFn(enabled) } diff --git a/packages/stage-shared/src/beat-sync/eventa.ts b/packages/stage-shared/src/beat-sync/eventa.ts index 5f6022a69..0727e1241 100644 --- a/packages/stage-shared/src/beat-sync/eventa.ts +++ b/packages/stage-shared/src/beat-sync/eventa.ts @@ -2,7 +2,9 @@ import type { AnalyserBeatEvent, AnalyserWorkletParameters } from '@nekopaw/temp import type { BeatSyncDetectorState } from './types' -import { defineInvokeEventa } from '@moeru/eventa' +import { createContext as createWebContext, defineInvokeEventa } from '@moeru/eventa' +import { createContext as createBroadcastChannelContext } from '@moeru/eventa/adapters/broadcast-channel' +import { isElectronWindow } from '@proj-airi/stage-shared' // Functions export const beatSyncToggleInvokeEventa = defineInvokeEventa('eventa:invoke:electron:beat-sync:toggle') @@ -13,3 +15,22 @@ export const beatSyncGetInputByteFrequencyDataInvokeEventa = defineInvokeEventa< // Events export const beatSyncStateChangedInvokeEventa = defineInvokeEventa('eventa:event:electron:beat-sync:state-changed') export const beatSyncBeatSignaledInvokeEventa = defineInvokeEventa('eventa:event:electron:beat-sync:beat-signaled') + +let _broadcastChannel: BroadcastChannel | undefined +function getBroadcastChannel() { + if (!_broadcastChannel) { + _broadcastChannel = new BroadcastChannel('airi::beat-sync') + _broadcastChannel.onmessage = (event) => { + } + } + return _broadcastChannel +} + +export function createContext() { + if (isElectronWindow(window)) { + return createBroadcastChannelContext(getBroadcastChannel()).context + } + else { + return createWebContext() + } +} diff --git a/packages/stage-shared/src/beat-sync/index.ts b/packages/stage-shared/src/beat-sync/index.ts index 22c65a82b..2b8284f72 100644 --- a/packages/stage-shared/src/beat-sync/index.ts +++ b/packages/stage-shared/src/beat-sync/index.ts @@ -1,2 +1,3 @@ +export * from './detector' export * from './eventa' export * from './types' diff --git a/packages/stage-ui/src/components/scenes/live2d/Model.vue b/packages/stage-ui/src/components/scenes/live2d/Model.vue index 96923d22f..3813e3bc7 100644 --- a/packages/stage-ui/src/components/scenes/live2d/Model.vue +++ b/packages/stage-ui/src/components/scenes/live2d/Model.vue @@ -3,7 +3,7 @@ import type { Application } from '@pixi/app' import type { PixiLive2DInternalModel } from '../../../composables/live2d' -import { listenBeatSyncBeatSignal } from '@proj-airi/stage-shared/beat-sync/browser' +import { listenBeatSyncBeatSignal } from '@proj-airi/stage-shared/beat-sync' import { useTheme } from '@proj-airi/ui' import { breakpointsTailwind, until, useBreakpoints, useDebounceFn } from '@vueuse/core' import { formatHex } from 'culori' diff --git a/packages/stage-ui/src/composables/use-modules-list.ts b/packages/stage-ui/src/composables/use-modules-list.ts index 04a0f8914..9df48baca 100644 --- a/packages/stage-ui/src/composables/use-modules-list.ts +++ b/packages/stage-ui/src/composables/use-modules-list.ts @@ -1,6 +1,6 @@ import type { BeatSyncDetectorState } from '@proj-airi/stage-shared/beat-sync' -import { getBeatSyncState, listenBeatSyncStateChange } from '@proj-airi/stage-shared/beat-sync/browser' +import { getBeatSyncState, listenBeatSyncStateChange } from '@proj-airi/stage-shared/beat-sync' import { computed, onMounted, onUnmounted, ref } from 'vue' import { useI18n } from 'vue-i18n'