From a135efff036de8ee9172d615947ca29edb3c2e72 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:25:57 +0900 Subject: [PATCH 001/110] add runEngineAll --- src/background.ts | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/background.ts b/src/background.ts index 7bec5bb805..eae9bc10c7 100644 --- a/src/background.ts +++ b/src/background.ts @@ -348,10 +348,24 @@ const store = new Store<{ }); // engine -let willQuitEngine = false; -let engineProcess: ChildProcess; -async function runEngine() { - const engineInfo = engineInfos[0]; // TODO: 複数エンジン対応 +type EngineProcessContainer = { + willQuitEngine: boolean; + engineProcess?: ChildProcess; +} + +const engineProcessContainers: Record = {}; + +async function runEngineAll() { + log.info(`Starting ${engineInfos.length} engines...`); + + for (const engineInfo of engineInfos) { + log.info(`ENGINE ${engineInfo.key}: Start launching`); + await runEngine(engineInfo.key); + } +} + +async function runEngine(engineKey: string) { + const engineInfo = engineInfos.find((engineInfo) => engineInfo.key === engineKey) if (!engineInfo) throw new Error(`No such engineInfo registered: index == 0`); if (!engineInfo.executionEnabled) { @@ -364,7 +378,14 @@ async function runEngine() { return; } - willQuitEngine = false; + if (!(engineKey in engineProcessContainers)) { + engineProcessContainers[engineKey] = { + willQuitEngine: false, + }; + } + + const engineProcessContainer = engineProcessContainers[engineKey]; + engineProcessContainer.willQuitEngine = false; // 最初のエンジンモード if (!store.has("useGpu")) { @@ -399,9 +420,10 @@ async function runEngine() { log.info(`ENGINE path: ${enginePath}`); log.info(`ENGINE args: ${JSON.stringify(args)}`); - engineProcess = spawn(enginePath, args, { + const engineProcess = spawn(enginePath, args, { cwd: path.dirname(enginePath), }); + engineProcessContainer.engineProcess = engineProcess; engineProcess.stdout?.on("data", (data) => { log.info(`ENGINE: ${data.toString("utf-8")}`); @@ -415,7 +437,7 @@ async function runEngine() { log.info(`ENGINE: process terminated due to receipt of signal ${signal}`); log.info(`ENGINE: process exited with code ${code}`); - if (!willQuitEngine) { + if (!engineProcessContainer.willQuitEngine) { ipcMainSend(win, "DETECTED_ENGINE_ERROR"); dialog.showErrorBox( "音声合成エンジンエラー", From f81a77430d3f80a5262c12b448361e8fa9515afd Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:29:41 +0900 Subject: [PATCH 002/110] fix log --- src/background.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/background.ts b/src/background.ts index eae9bc10c7..de3e4826f5 100644 --- a/src/background.ts +++ b/src/background.ts @@ -378,6 +378,8 @@ async function runEngine(engineKey: string) { return; } + log.info(`Starting ENGINE ${engineKey} process`); + if (!(engineKey in engineProcessContainers)) { engineProcessContainers[engineKey] = { willQuitEngine: false, @@ -407,8 +409,7 @@ async function runEngine(engineKey: string) { } const useGpu = store.get("useGpu"); - log.info(`Starting ENGINE`); - log.info(`ENGINE mode: ${useGpu ? "GPU" : "CPU"}`); + log.info(`ENGINE ${engineKey} mode: ${useGpu ? "GPU" : "CPU"}`); // エンジンプロセスの起動 const enginePath = path.resolve( @@ -417,8 +418,8 @@ async function runEngine(engineKey: string) { ); const args = useGpu ? ["--use_gpu"] : []; - log.info(`ENGINE path: ${enginePath}`); - log.info(`ENGINE args: ${JSON.stringify(args)}`); + log.info(`ENGINE ${engineKey} path: ${enginePath}`); + log.info(`ENGINE ${engineKey} args: ${JSON.stringify(args)}`); const engineProcess = spawn(enginePath, args, { cwd: path.dirname(enginePath), @@ -426,16 +427,16 @@ async function runEngine(engineKey: string) { engineProcessContainer.engineProcess = engineProcess; engineProcess.stdout?.on("data", (data) => { - log.info(`ENGINE: ${data.toString("utf-8")}`); + log.info(`ENGINE ${engineKey} STDOUT: ${data.toString("utf-8")}`); }); engineProcess.stderr?.on("data", (data) => { - log.error(`ENGINE: ${data.toString("utf-8")}`); + log.error(`ENGINE ${engineKey} STDERR: ${data.toString("utf-8")}`); }); engineProcess.on("close", (code, signal) => { - log.info(`ENGINE: process terminated due to receipt of signal ${signal}`); - log.info(`ENGINE: process exited with code ${code}`); + log.info(`ENGINE ${engineKey} process terminated due to receipt of signal ${signal}`); + log.info(`ENGINE ${engineKey} process exited with code ${code}`); if (!engineProcessContainer.willQuitEngine) { ipcMainSend(win, "DETECTED_ENGINE_ERROR"); From 22e0168bf343ae075f55b5a6f8f466e71c13b39a Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:36:14 +0900 Subject: [PATCH 003/110] fix log --- src/background.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/background.ts b/src/background.ts index de3e4826f5..3901cf5550 100644 --- a/src/background.ts +++ b/src/background.ts @@ -351,7 +351,7 @@ const store = new Store<{ type EngineProcessContainer = { willQuitEngine: boolean; engineProcess?: ChildProcess; -} +}; const engineProcessContainers: Record = {}; @@ -365,20 +365,22 @@ async function runEngineAll() { } async function runEngine(engineKey: string) { - const engineInfo = engineInfos.find((engineInfo) => engineInfo.key === engineKey) - if (!engineInfo) throw new Error(`No such engineInfo registered: index == 0`); + const engineInfo = engineInfos.find( + (engineInfo) => engineInfo.key === engineKey + ); + if (!engineInfo) throw new Error(`No such engineInfo registered: key == ${engineKey}`); if (!engineInfo.executionEnabled) { - log.info("Skipped engineInfo execution: disabled"); + log.info(`ENGINE ${engineKey}: Skipped engineInfo execution: disabled`); return; } if (!engineInfo.executionFilePath) { - log.info("Skipped engineInfo execution: empty executionFilePath"); + log.info(`ENGINE ${engineKey}: Skipped engineInfo execution: empty executionFilePath`); return; } - log.info(`Starting ENGINE ${engineKey} process`); + log.info(`ENGINE ${engineKey}: Starting process`); if (!(engineKey in engineProcessContainers)) { engineProcessContainers[engineKey] = { @@ -435,8 +437,10 @@ async function runEngine(engineKey: string) { }); engineProcess.on("close", (code, signal) => { - log.info(`ENGINE ${engineKey} process terminated due to receipt of signal ${signal}`); - log.info(`ENGINE ${engineKey} process exited with code ${code}`); + log.info( + `ENGINE ${engineKey}: Process terminated due to receipt of signal ${signal}` + ); + log.info(`ENGINE ${engineKey}: Process exited with code ${code}`); if (!engineProcessContainer.willQuitEngine) { ipcMainSend(win, "DETECTED_ENGINE_ERROR"); From c5202dd3cb09e6edce41d4d7f617b13dd2368e6f Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:38:36 +0900 Subject: [PATCH 004/110] add killEngineAll --- src/background.ts | 64 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/background.ts b/src/background.ts index 3901cf5550..bc9f420040 100644 --- a/src/background.ts +++ b/src/background.ts @@ -368,7 +368,8 @@ async function runEngine(engineKey: string) { const engineInfo = engineInfos.find( (engineInfo) => engineInfo.key === engineKey ); - if (!engineInfo) throw new Error(`No such engineInfo registered: key == ${engineKey}`); + if (!engineInfo) + throw new Error(`No such engineInfo registered: key == ${engineKey}`); if (!engineInfo.executionEnabled) { log.info(`ENGINE ${engineKey}: Skipped engineInfo execution: disabled`); @@ -376,7 +377,9 @@ async function runEngine(engineKey: string) { } if (!engineInfo.executionFilePath) { - log.info(`ENGINE ${engineKey}: Skipped engineInfo execution: empty executionFilePath`); + log.info( + `ENGINE ${engineKey}: Skipped engineInfo execution: empty executionFilePath` + ); return; } @@ -452,39 +455,86 @@ async function runEngine(engineKey: string) { }); } +function killEngineAll({ + onFirstKillStart, + onAllKilled, + onError, +}: { + onFirstKillStart?: VoidFunction; + onAllKilled?: VoidFunction; + onError?: (engineKey: string, message: unknown) => void; +}) { + let anyKillStart = false; + + const numEngineProcess = Object.keys(engineProcessContainers).length; + let numEngineProcessKilled = 0; + + for (const [engineKey] of Object.entries(engineProcessContainers)) { + killEngine({ + engineKey, + onKillStart: () => { + if (!anyKillStart) { + anyKillStart = true; + onFirstKillStart?.(); + } + }, + onKilled: () => { + numEngineProcessKilled++; + log.info( + `ENGINE ${numEngineProcessKilled} / ${numEngineProcess} processes killed` + ); + + if (numEngineProcessKilled === numEngineProcess) { + onAllKilled?.(); + } + }, + onError: (message) => onError?.(engineKey, message), + }); + } +} + function killEngine({ + engineKey, onKillStart, onKilled, onError, }: { + engineKey: string; onKillStart?: VoidFunction; onKilled?: VoidFunction; onError?: (error: unknown) => void; }) { + const engineProcessContainer = engineProcessContainers[engineKey]; + if (!engineProcessContainer) { + onError?.(`No such engineProcessContainer: ${engineKey}`); + return; + } + + const engineProcess = engineProcessContainer.engineProcess; if (engineProcess == undefined) { // nop if no process started (already killed or not started yet) - log.info(`ENGINE process not started`); + log.info(`ENGINE ${engineKey}: Process not started`); return; } // considering the case that ENGINE process killed after checking process status engineProcess.once("close", () => { - log.info("ENGINE process closed"); + log.info(`ENGINE ${engineKey}: Process closed`); onKilled?.(); }); log.info( - `ENGINE last exit code: ${engineProcess.exitCode}, signal: ${engineProcess.signalCode}` + `ENGINE ${engineKey}: last exit code: ${engineProcess.exitCode}, signal: ${engineProcess.signalCode}` ); const engineNotExited = engineProcess.exitCode === null; const engineNotKilled = engineProcess.signalCode === null; if (engineNotExited && engineNotKilled) { - log.info(`Killing ENGINE process (PID=${engineProcess.pid})...`); + log.info(`ENGINE ${engineKey}: Killing process (PID=${engineProcess.pid})`); onKillStart?.(); - willQuitEngine = true; + engineProcessContainer.willQuitEngine = true; try { engineProcess.pid != undefined && treeKill(engineProcess.pid); } catch (error: unknown) { From 6e46ad9cab64c8833caca35f0fd4788c4bc650d0 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:39:46 +0900 Subject: [PATCH 005/110] fix log --- src/background.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background.ts b/src/background.ts index bc9f420040..29808f0b68 100644 --- a/src/background.ts +++ b/src/background.ts @@ -506,7 +506,7 @@ function killEngine({ }) { const engineProcessContainer = engineProcessContainers[engineKey]; if (!engineProcessContainer) { - onError?.(`No such engineProcessContainer: ${engineKey}`); + onError?.(`No such engineProcessContainer: key == ${engineKey}`); return; } From 31bfe9f8b9bf2b7fbed5a9b140aa2b47b98e0d7a Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:40:23 +0900 Subject: [PATCH 006/110] fix log --- src/background.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/background.ts b/src/background.ts index 29808f0b68..47cc19003c 100644 --- a/src/background.ts +++ b/src/background.ts @@ -538,10 +538,11 @@ function killEngine({ try { engineProcess.pid != undefined && treeKill(engineProcess.pid); } catch (error: unknown) { + log.error(`ENGINE ${engineKey}: Error during killing process`); onError?.(error); } } else { - log.info("ENGINE process already closed"); + log.info(`ENGINE ${engineKey}: Process already closed`); } } From eb919bd624ffafd9b102c861ef74a0bf15bce30c Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:53:00 +0900 Subject: [PATCH 007/110] add restartEngineAll --- src/background.ts | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/background.ts b/src/background.ts index 47cc19003c..b6f0b52974 100644 --- a/src/background.ts +++ b/src/background.ts @@ -546,45 +546,58 @@ function killEngine({ } } -async function restartEngine() { +async function restartEngineAll() { + for (const engineInfo of engineInfos) { + await restartEngine(engineInfo.key); + } +} + +async function restartEngine(engineKey: string) { await new Promise((resolve, reject) => { + const engineProcessContainer: EngineProcessContainer | undefined = + engineProcessContainers[engineKey]; + const engineProcess = engineProcessContainer?.engineProcess; + log.info( - `Restarting ENGINE (last exit code: ${engineProcess.exitCode}, signal: ${engineProcess.signalCode})` + `ENGINE ${engineKey}: Restarting process (last exit code: ${engineProcess?.exitCode}, signal: ${engineProcess?.signalCode})` ); // エンジンのプロセスがすでに終了している、またはkillされている場合 - const engineExited = engineProcess.exitCode !== null; - const engineKilled = engineProcess.signalCode !== null; + const engineExited = engineProcess?.exitCode !== null; + const engineKilled = engineProcess?.signalCode !== null; + // engineProcess === undefinedの場合true if (engineExited || engineKilled) { log.info( - "ENGINE process is not started yet or already killed. Starting ENGINE..." + `ENGINE ${engineKey}: Process is not started yet or already killed. Starting process...` ); - runEngine(); + runEngine(engineKey); resolve(); return; } // エンジンエラー時のエラーウィンドウ抑制用。 - willQuitEngine = true; + engineProcessContainer.willQuitEngine = true; // 「killに使用するコマンドが終了するタイミング」と「OSがプロセスをkillするタイミング」が違うので単純にtreeKillのコールバック関数でrunEngine()を実行すると失敗します。 // closeイベントはexitイベントよりも後に発火します。 const restartEngineOnProcessClosedCallback = () => { - log.info("ENGINE process killed. Restarting ENGINE..."); + log.info(`ENGINE ${engineKey}: Process killed. Restarting process...`); - runEngine(); + runEngine(engineKey); resolve(); }; engineProcess.once("close", restartEngineOnProcessClosedCallback); // treeKillのコールバック関数はコマンドが終了した時に呼ばれます。 - log.info(`Killing current ENGINE process (PID=${engineProcess.pid})...`); + log.info( + `ENGINE ${engineKey}: Killing current process (PID=${engineProcess.pid})...` + ); treeKill(engineProcess.pid, (error) => { // error変数の値がundefined以外であればkillコマンドが失敗したことを意味します。 if (error != null) { - log.error("Failed to kill ENGINE"); + log.error(`ENGINE ${engineKey}: Failed to kill process`); log.error(error); // killに失敗したとき、closeイベントが発生せず、once listenerが消費されない From 39b81df81d3b3f4e3246d351bf7b31eb9332a000 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 04:59:20 +0900 Subject: [PATCH 008/110] call killEngineAll --- src/background.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/background.ts b/src/background.ts index b6f0b52974..97813d9443 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1002,8 +1002,12 @@ ipcMainHandle("ENGINE_INFOS", () => { * エンジンを再起動する。 * エンジンの起動が開始したらresolve、起動が失敗したらreject。 */ -ipcMainHandle("RESTART_ENGINE", async () => { - await restartEngine(); +ipcMainHandle("RESTART_ENGINE_ALL", async () => { + await restartEngineAll(); +}); + +ipcMainHandle("RESTART_ENGINE", async (_, { engineKey }) => { + await restartEngine(engineKey); }); ipcMainHandle("SAVING_SETTING", (_, { newData }) => { @@ -1150,20 +1154,20 @@ app.on("before-quit", (event) => { return; } - killEngine({ - onKillStart: () => { + log.info("Checking ENGINE status before app quit"); + killEngineAll({ + onFirstKillStart: () => { // executed synchronously to cancel before-quit event log.info("Interrupt app quit to kill ENGINE"); event.preventDefault(); }, - onKilled: () => { - // executed asynchronously to catch process closed event - log.info("ENGINE killed. Quitting app"); + onAllKilled: () => { + // executed asynchronously to catch all process closed event + log.info("All ENGINE killed. Quitting app"); app.quit(); // attempt to quit app again }, - onError: (error: unknown) => { - log.error("Error during killing ENGINE process"); - log.error(error); + onError: (engineKey, message) => { + console.error(`ENGINE ${engineKey}: Error during killing process: ${message}`); }, }); }); @@ -1191,7 +1195,7 @@ app.on("ready", async () => { } } - createWindow().then(() => runEngine()); + createWindow().then(() => runEngineAll()); }); app.on("second-instance", () => { From c8a7c22c137c65efd397af6183cea62a7c6f9815 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 05:00:59 +0900 Subject: [PATCH 009/110] fix log unit --- src/background.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background.ts b/src/background.ts index 97813d9443..fa125f3107 100644 --- a/src/background.ts +++ b/src/background.ts @@ -356,7 +356,7 @@ type EngineProcessContainer = { const engineProcessContainers: Record = {}; async function runEngineAll() { - log.info(`Starting ${engineInfos.length} engines...`); + log.info(`Starting ${engineInfos.length} engine/s...`); for (const engineInfo of engineInfos) { log.info(`ENGINE ${engineInfo.key}: Start launching`); From 977883198095638545f7d213fc39226d80d10256 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 05:47:26 +0900 Subject: [PATCH 010/110] npm run fmt --- src/background.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/background.ts b/src/background.ts index fa125f3107..17da4a6159 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1167,7 +1167,9 @@ app.on("before-quit", (event) => { app.quit(); // attempt to quit app again }, onError: (engineKey, message) => { - console.error(`ENGINE ${engineKey}: Error during killing process: ${message}`); + console.error( + `ENGINE ${engineKey}: Error during killing process: ${message}` + ); }, }); }); From 7d7b41c50e96ae34e9e337e2d5eeba31abccac87 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 05:57:49 +0900 Subject: [PATCH 011/110] add START_WAITING_ENGINE_ALL, RESTART_ENGINE_ALL --- src/components/MenuBar.vue | 2 +- src/components/SettingDialog.vue | 8 ++++---- src/electron/preload.ts | 8 ++++++-- src/store/audio.ts | 30 ++++++++++++++++++++++++------ src/store/type.ts | 10 +++++++++- src/type/preload.d.ts | 3 ++- src/views/Home.vue | 2 +- 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index 85bc801916..3499877857 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -271,7 +271,7 @@ export default defineComponent({ type: "button", label: "再起動", onClick: () => { - store.dispatch("RESTART_ENGINE"); + store.dispatch("RESTART_ENGINE_ALL"); }, }, ], diff --git a/src/components/SettingDialog.vue b/src/components/SettingDialog.vue index e7f0412c75..fc33657fb6 100644 --- a/src/components/SettingDialog.vue +++ b/src/components/SettingDialog.vue @@ -601,7 +601,7 @@ export default defineComponent({ const change = async () => { await store.dispatch("SET_USE_GPU", { useGpu }); - store.dispatch("RESTART_ENGINE"); + store.dispatch("RESTART_ENGINE_ALL"); $q.dialog({ title: "エンジンの起動モードを変更しました", @@ -669,8 +669,8 @@ export default defineComponent({ }); }; - const restartEngineProcess = () => { - store.dispatch("RESTART_ENGINE"); + const restartAllEngineProcess = () => { + store.dispatch("RESTART_ENGINE_ALL"); }; const savingSetting = computed(() => store.state.savingSetting); @@ -729,7 +729,7 @@ export default defineComponent({ availableAudioOutputDevices, changeinheritAudioInfo, changeExperimentalSetting, - restartEngineProcess, + restartAllEngineProcess, savingSetting, handleSavingSettingChange, openFileExplore, diff --git a/src/electron/preload.ts b/src/electron/preload.ts index f0c9afb6ab..0bbeca8b5a 100644 --- a/src/electron/preload.ts +++ b/src/electron/preload.ts @@ -188,8 +188,12 @@ const api: Sandbox = { return ipcRendererInvoke("ENGINE_INFOS"); }, - restartEngine: () => { - return ipcRendererInvoke("RESTART_ENGINE"); + restartEngineAll: () => { + return ipcRendererInvoke("RESTART_ENGINE_ALL"); + }, + + restartEngine: (engineKey: string) => { + return ipcRendererInvoke("RESTART_ENGINE", { engineKey }); }, savingSetting: (newData) => { diff --git a/src/store/audio.ts b/src/store/audio.ts index 4d49f88c7a..f3a2712701 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -468,11 +468,22 @@ export const audioStore: VoiceVoxStoreOptions< }, actions: { + START_WAITING_ENGINE_ALL: createUILockAction( + async ({ state, dispatch }) => { + const engineInfos = state.engineInfos; + + for (const engineInfo of engineInfos) { + await dispatch("START_WAITING_ENGINE", { + engineKey: engineInfo.key, + }); + } + } + ), START_WAITING_ENGINE: createUILockAction( - async ({ state, commit, dispatch }) => { - const engineInfo = state.engineInfos[0]; // TODO: 複数エンジン対応 + async ({ state, commit, dispatch }, { engineKey }) => { + const engineInfo = state.engineInfos.find((engineInfo)=> engineInfo.key === engineKey); if (!engineInfo) - throw new Error(`No such engineInfo registered: index == 0`); + throw new Error(`No such engineInfo registered: key == ${engineKey}`); let engineState = state.engineState; for (let i = 0; i < 100; i++) { @@ -1418,11 +1429,18 @@ export const audioStore: VoiceVoxStoreOptions< commit("SET_ENGINE_STATE", { engineState: "ERROR" }); } }, - async RESTART_ENGINE({ dispatch, commit }) { + async RESTART_ENGINE_ALL({ dispatch, commit }) { + await commit("SET_ENGINE_STATE", { engineState: "STARTING" }); + window.electron + .restartEngineAll() + .then(() => dispatch("START_WAITING_ENGINE_ALL")) + .catch(() => dispatch("DETECTED_ENGINE_ERROR")); + }, + async RESTART_ENGINE({ dispatch, commit }, { engineKey }) { await commit("SET_ENGINE_STATE", { engineState: "STARTING" }); window.electron - .restartEngine() - .then(() => dispatch("START_WAITING_ENGINE")) + .restartEngine(engineKey) + .then(() => dispatch("START_WAITING_ENGINE", { engineKey })) .catch(() => dispatch("DETECTED_ENGINE_ERROR")); }, CHECK_FILE_EXISTS(_, { file }: { file: string }) { diff --git a/src/store/type.ts b/src/store/type.ts index 6b892c7ac0..95520e3aea 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -103,12 +103,20 @@ type AudioStoreTypes = { getter: number | undefined; }; + START_WAITING_ENGINE_ALL: { + action(): void; + }; + START_WAITING_ENGINE: { + action(payload: { engineKey: string }): void; + }; + + RESTART_ENGINE_ALL: { action(): void; }; RESTART_ENGINE: { - action(): void; + action(payload: { engineKey: string }): void; }; DETECTED_ENGINE_ERROR: { diff --git a/src/type/preload.d.ts b/src/type/preload.d.ts index 2f1e7e2a03..6cc206456c 100644 --- a/src/type/preload.d.ts +++ b/src/type/preload.d.ts @@ -57,7 +57,8 @@ export interface Sandbox { logError(...params: unknown[]): void; logInfo(...params: unknown[]): void; engineInfos(): Promise; - restartEngine(): Promise; + restartEngineAll(): Promise; + restartEngine(engineKey: string): Promise; savingSetting(newData?: SavingSetting): Promise; hotkeySettings(newData?: HotkeySetting): Promise; toolbarSetting(newData?: ToolbarSetting): Promise; diff --git a/src/views/Home.vue b/src/views/Home.vue index ae10f12190..29bd6e0f4b 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -404,7 +404,7 @@ export default defineComponent({ onMounted(async () => { await store.dispatch("GET_ENGINE_INFOS"); - await store.dispatch("START_WAITING_ENGINE"); + await store.dispatch("START_WAITING_ENGINE_ALL"); await store.dispatch("LOAD_CHARACTER"); await store.dispatch("LOAD_USER_CHARACTER_ORDER"); await store.dispatch("LOAD_DEFAULT_STYLE_IDS"); From e0198fe546ed9884a3053824753509db009f3d97 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 05:58:39 +0900 Subject: [PATCH 012/110] npm run fmt --- src/store/audio.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index f3a2712701..696ac6624a 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -481,7 +481,9 @@ export const audioStore: VoiceVoxStoreOptions< ), START_WAITING_ENGINE: createUILockAction( async ({ state, commit, dispatch }, { engineKey }) => { - const engineInfo = state.engineInfos.find((engineInfo)=> engineInfo.key === engineKey); + const engineInfo = state.engineInfos.find( + (engineInfo) => engineInfo.key === engineKey + ); if (!engineInfo) throw new Error(`No such engineInfo registered: key == ${engineKey}`); From 5f33a0779b681384145bda6c5356534cae1b14a8 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:12:50 +0900 Subject: [PATCH 013/110] DETECTED_ENGINE_ERROR engineKey --- src/background.ts | 2 +- src/plugins/ipcMessageReceiverPlugin.ts | 4 +- src/store/audio.ts | 57 +++++++++++++++---------- src/store/type.ts | 12 ++++-- src/type/ipc.d.ts | 2 +- 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/background.ts b/src/background.ts index 17da4a6159..d857522951 100644 --- a/src/background.ts +++ b/src/background.ts @@ -446,7 +446,7 @@ async function runEngine(engineKey: string) { log.info(`ENGINE ${engineKey}: Process exited with code ${code}`); if (!engineProcessContainer.willQuitEngine) { - ipcMainSend(win, "DETECTED_ENGINE_ERROR"); + ipcMainSend(win, "DETECTED_ENGINE_ERROR", { engineKey }); dialog.showErrorBox( "音声合成エンジンエラー", "音声合成エンジンが異常終了しました。エンジンを再起動してください。" diff --git a/src/plugins/ipcMessageReceiverPlugin.ts b/src/plugins/ipcMessageReceiverPlugin.ts index fa38763444..7d5ddcde8c 100644 --- a/src/plugins/ipcMessageReceiverPlugin.ts +++ b/src/plugins/ipcMessageReceiverPlugin.ts @@ -22,8 +22,8 @@ export const ipcMessageReceiver: Plugin = { options.store.dispatch("DETECT_UNMAXIMIZED") ); - window.electron.onReceivedIPCMsg("DETECTED_ENGINE_ERROR", () => - options.store.dispatch("DETECTED_ENGINE_ERROR") + window.electron.onReceivedIPCMsg("DETECTED_ENGINE_ERROR", (_, { engineKey }) => + options.store.dispatch("DETECTED_ENGINE_ERROR", { engineKey }) ); window.electron.onReceivedIPCMsg("DETECT_PINNED", () => { diff --git a/src/store/audio.ts b/src/store/audio.ts index 696ac6624a..e7063e0bfe 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -140,7 +140,7 @@ const audioBlobCache: Record = {}; const audioElements: Record = {}; export const audioStoreState: AudioStoreState = { - engineState: "STARTING", + engineStates: {}, audioItems: {}, audioKeys: [], audioStates: {}, @@ -167,8 +167,16 @@ export const audioStore: VoiceVoxStoreOptions< IS_ACTIVE: (state) => (audioKey: string) => { return state._activeAudioKey === audioKey; }, - IS_ENGINE_READY: (state) => { - return state.engineState === "READY"; + IS_ALL_ENGINE_READY: (state) => { + for (const engineState of Object.values(state.engineStates)) { + if (engineState !== "READY") return false; + } + + return true; + }, + IS_ENGINE_READY: (state) => (engineKey) => { + const engineState = state.engineStates[engineKey]; + return engineState === "READY"; }, ACTIVE_AUDIO_ELEM_CURRENT_TIME: (state) => { return state._activeAudioKey !== undefined @@ -186,8 +194,8 @@ export const audioStore: VoiceVoxStoreOptions< }, mutations: { - SET_ENGINE_STATE(state, { engineState }: { engineState: EngineState }) { - state.engineState = engineState; + SET_ENGINE_STATE(state, { engineKey, engineState }: { engineKey: string, engineState: EngineState }) { + state.engineStates[engineKey] = engineState; }, SET_CHARACTER_INFOS( state, @@ -487,9 +495,10 @@ export const audioStore: VoiceVoxStoreOptions< if (!engineInfo) throw new Error(`No such engineInfo registered: key == ${engineKey}`); - let engineState = state.engineState; + let engineState = state.engineStates[engineKey]; + for (let i = 0; i < 100; i++) { - engineState = state.engineState; + engineState = state.engineStates[engineKey]; if (engineState === "FAILED_STARTING") { break; } @@ -507,12 +516,12 @@ export const audioStore: VoiceVoxStoreOptions< continue; } engineState = "READY"; - commit("SET_ENGINE_STATE", { engineState }); + commit("SET_ENGINE_STATE", { engineKey, engineState }); break; } if (engineState !== "READY") { - commit("SET_ENGINE_STATE", { engineState: "FAILED_STARTING" }); + commit("SET_ENGINE_STATE", { engineKey, engineState: "FAILED_STARTING" }); } } ), @@ -632,7 +641,9 @@ export const audioStore: VoiceVoxStoreOptions< ) ].defaultStyleId; const baseAudioItem = payload.baseAudioItem; - const query = getters.IS_ENGINE_READY + + // TODO: 接続先のエンジンがREADYであることを判定する + const query = getters.IS_ALL_ENGINE_READY ? await dispatch("FETCH_AUDIO_QUERY", { text, styleId, @@ -1419,31 +1430,33 @@ export const audioStore: VoiceVoxStoreOptions< OPEN_TEXT_EDIT_CONTEXT_MENU() { window.electron.openTextEditContextMenu(); }, - DETECTED_ENGINE_ERROR({ state, commit }) { + DETECTED_ENGINE_ERROR({ state, commit }, { engineKey }) { switch (state.engineState) { case "STARTING": - commit("SET_ENGINE_STATE", { engineState: "FAILED_STARTING" }); + commit("SET_ENGINE_STATE", { engineKey, engineState: "FAILED_STARTING" }); break; case "READY": - commit("SET_ENGINE_STATE", { engineState: "ERROR" }); + commit("SET_ENGINE_STATE", { engineKey, engineState: "ERROR" }); break; default: - commit("SET_ENGINE_STATE", { engineState: "ERROR" }); + commit("SET_ENGINE_STATE", { engineKey, engineState: "ERROR" }); } }, - async RESTART_ENGINE_ALL({ dispatch, commit }) { - await commit("SET_ENGINE_STATE", { engineState: "STARTING" }); - window.electron - .restartEngineAll() - .then(() => dispatch("START_WAITING_ENGINE_ALL")) - .catch(() => dispatch("DETECTED_ENGINE_ERROR")); + async RESTART_ENGINE_ALL({ state, dispatch }) { + const engineInfos = state.engineInfos; + + for (const engineInfo of engineInfos) { + await dispatch("RESTART_ENGINE", { + engineKey: engineInfo.key, + }); + } }, async RESTART_ENGINE({ dispatch, commit }, { engineKey }) { - await commit("SET_ENGINE_STATE", { engineState: "STARTING" }); + await commit("SET_ENGINE_STATE", { engineKey, engineState: "STARTING" }); window.electron .restartEngine(engineKey) .then(() => dispatch("START_WAITING_ENGINE", { engineKey })) - .catch(() => dispatch("DETECTED_ENGINE_ERROR")); + .catch(() => dispatch("DETECTED_ENGINE_ERROR", { engineKey })); }, CHECK_FILE_EXISTS(_, { file }: { file: string }) { return window.electron.checkFileExists(file); diff --git a/src/store/type.ts b/src/store/type.ts index 95520e3aea..bfef128668 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -72,7 +72,7 @@ export type QuasarDialog = QVueGlobals["dialog"]; */ export type AudioStoreState = { - engineState: EngineState; + engineStates: Record; characterInfos?: CharacterInfo[]; audioItems: Record; audioKeys: string[]; @@ -95,10 +95,14 @@ type AudioStoreTypes = { getter(audioKey: string): boolean; }; - IS_ENGINE_READY: { + IS_ALL_ENGINE_READY: { getter: boolean; }; + IS_ENGINE_READY: { + getter(engineKey: string): boolean; + }; + ACTIVE_AUDIO_ELEM_CURRENT_TIME: { getter: number | undefined; }; @@ -120,11 +124,11 @@ type AudioStoreTypes = { }; DETECTED_ENGINE_ERROR: { - action(): void; + action(payload: { engineKey: string }): void; }; SET_ENGINE_STATE: { - mutation: { engineState: EngineState }; + mutation: { engineKey: string, engineState: EngineState }; }; LOAD_CHARACTER: { diff --git a/src/type/ipc.d.ts b/src/type/ipc.d.ts index 67a2491737..149775a94e 100644 --- a/src/type/ipc.d.ts +++ b/src/type/ipc.d.ts @@ -293,7 +293,7 @@ type IpcSOData = { }; DETECTED_ENGINE_ERROR: { - args: []; + args: [obj: { engineKey: string }]; return: void; }; From e23e9fbc5763ea5c4d8410ab6e9f757e9b8991c0 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:14:00 +0900 Subject: [PATCH 014/110] npm run fmt --- src/plugins/ipcMessageReceiverPlugin.ts | 6 ++++-- src/store/audio.ts | 18 +++++++++++++++--- src/store/type.ts | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/plugins/ipcMessageReceiverPlugin.ts b/src/plugins/ipcMessageReceiverPlugin.ts index 7d5ddcde8c..c807ab49c2 100644 --- a/src/plugins/ipcMessageReceiverPlugin.ts +++ b/src/plugins/ipcMessageReceiverPlugin.ts @@ -22,8 +22,10 @@ export const ipcMessageReceiver: Plugin = { options.store.dispatch("DETECT_UNMAXIMIZED") ); - window.electron.onReceivedIPCMsg("DETECTED_ENGINE_ERROR", (_, { engineKey }) => - options.store.dispatch("DETECTED_ENGINE_ERROR", { engineKey }) + window.electron.onReceivedIPCMsg( + "DETECTED_ENGINE_ERROR", + (_, { engineKey }) => + options.store.dispatch("DETECTED_ENGINE_ERROR", { engineKey }) ); window.electron.onReceivedIPCMsg("DETECT_PINNED", () => { diff --git a/src/store/audio.ts b/src/store/audio.ts index e7063e0bfe..9aff20162c 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -194,7 +194,13 @@ export const audioStore: VoiceVoxStoreOptions< }, mutations: { - SET_ENGINE_STATE(state, { engineKey, engineState }: { engineKey: string, engineState: EngineState }) { + SET_ENGINE_STATE( + state, + { + engineKey, + engineState, + }: { engineKey: string; engineState: EngineState } + ) { state.engineStates[engineKey] = engineState; }, SET_CHARACTER_INFOS( @@ -521,7 +527,10 @@ export const audioStore: VoiceVoxStoreOptions< } if (engineState !== "READY") { - commit("SET_ENGINE_STATE", { engineKey, engineState: "FAILED_STARTING" }); + commit("SET_ENGINE_STATE", { + engineKey, + engineState: "FAILED_STARTING", + }); } } ), @@ -1433,7 +1442,10 @@ export const audioStore: VoiceVoxStoreOptions< DETECTED_ENGINE_ERROR({ state, commit }, { engineKey }) { switch (state.engineState) { case "STARTING": - commit("SET_ENGINE_STATE", { engineKey, engineState: "FAILED_STARTING" }); + commit("SET_ENGINE_STATE", { + engineKey, + engineState: "FAILED_STARTING", + }); break; case "READY": commit("SET_ENGINE_STATE", { engineKey, engineState: "ERROR" }); diff --git a/src/store/type.ts b/src/store/type.ts index bfef128668..33547dbe25 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -128,7 +128,7 @@ type AudioStoreTypes = { }; SET_ENGINE_STATE: { - mutation: { engineKey: string, engineState: EngineState }; + mutation: { engineKey: string; engineState: EngineState }; }; LOAD_CHARACTER: { From 9d593e05a19dc35ec65eba59f9f1ed3e260642b9 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:20:54 +0900 Subject: [PATCH 015/110] fix check all ready logic --- src/store/audio.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index 9aff20162c..d9b3c9d380 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -168,10 +168,12 @@ export const audioStore: VoiceVoxStoreOptions< return state._activeAudioKey === audioKey; }, IS_ALL_ENGINE_READY: (state) => { - for (const engineState of Object.values(state.engineStates)) { - if (engineState !== "READY") return false; + for (const engineInfo of state.engineInfos) { + const engineState = state.engineStates[engineInfo.key]; + if (engineState !== "READY") { + return false; + } } - return true; }, IS_ENGINE_READY: (state) => (engineKey) => { From 4f0c2ee32323a7eb7ede780052ec3855361a5699 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:31:49 +0900 Subject: [PATCH 016/110] fix engineState --- src/store/audio.ts | 5 ++++- src/views/Home.vue | 24 +++++++++++++++++++----- tests/unit/store/Vuex.spec.ts | 4 +++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index d9b3c9d380..6a2991ff1b 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -1442,7 +1442,10 @@ export const audioStore: VoiceVoxStoreOptions< window.electron.openTextEditContextMenu(); }, DETECTED_ENGINE_ERROR({ state, commit }, { engineKey }) { - switch (state.engineState) { + const engineState: EngineState | undefined = + state.engineStates[engineKey]; + + switch (engineState) { case "STARTING": commit("SET_ENGINE_STATE", { engineKey, diff --git a/src/views/Home.vue b/src/views/Home.vue index 29bd6e0f4b..9d63306274 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -6,15 +6,16 @@ +
{{ - engineState === "STARTING" + allEngineState === "STARTING" ? "エンジン起動中・・・" : "データ準備中・・・" }} @@ -170,7 +171,7 @@ import CharacterOrderDialog from "@/components/CharacterOrderDialog.vue"; import AcceptRetrieveTelemetryDialog from "@/components/AcceptRetrieveTelemetryDialog.vue"; import AcceptTermsDialog from "@/components/AcceptTermsDialog.vue"; import DictionaryManageDialog from "@/components/DictionaryManageDialog.vue"; -import { AudioItem } from "@/store/type"; +import { AudioItem, EngineState } from "@/store/type"; import { QResizeObserver } from "quasar"; import path from "path"; import { HotkeyAction, HotkeyReturnType } from "@/type/preload"; @@ -451,7 +452,20 @@ export default defineComponent({ }); // エンジン待機 - const engineState = computed(() => store.state.engineState); + // TODO: 複数エンジン対応 + const allEngineState = computed(() => { + const engineStates = store.state.engineStates; + + for (const engineInfo of store.state.engineInfos) { + const engineState: EngineState | undefined = + engineStates[engineInfo.key]; + if (engineState !== "STARTING") { + return engineState; // FIXME: 暫定的に1つのエンジンの状態を返す + } + } + + return "STARTING"; + }); // ライセンス表示 const isHelpDialogOpenComputed = computed({ @@ -583,7 +597,7 @@ export default defineComponent({ audioDetailPaneMinHeight, audioDetailPaneMaxHeight, isCompletedInitialStartup, - engineState, + allEngineState, isHelpDialogOpenComputed, isSettingDialogOpenComputed, isHotkeySettingDialogOpenComputed, diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts index 31fd0b2714..4a9f941950 100644 --- a/tests/unit/store/Vuex.spec.ts +++ b/tests/unit/store/Vuex.spec.ts @@ -118,7 +118,9 @@ describe("store/vuex.js test", () => { assert.exists(store); assert.isObject(store); assert.isObject(store.state); - assert.equal(store.state.engineState, "STARTING"); + assert.equal(store.state.engineStates, { + "88022f86-c823-436e-85a3-500c629749c4": "STARTING", + }); assert.isArray(store.state.defaultStyleIds); assert.isObject(store.state.audioItems); assert.isEmpty(store.state.audioItems); From c9a2272c25007d4b91d84fe380b24d6da426aa25 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:33:05 +0900 Subject: [PATCH 017/110] fix test --- tests/unit/store/Vuex.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts index 4a9f941950..84bb104338 100644 --- a/tests/unit/store/Vuex.spec.ts +++ b/tests/unit/store/Vuex.spec.ts @@ -14,10 +14,12 @@ const isDevelopment = process.env.NODE_ENV == "development"; // TODO: Swap external files to Mock describe("store/vuex.js test", () => { - it("creaete store", () => { + it("create store", () => { const store = createStore({ state: { - engineState: "STARTING", + engineStates: { + "88022f86-c823-436e-85a3-500c629749c4": "STARTING", + }, defaultStyleIds: [], userCharacterOrder: [], audioItems: {}, From 23bdd068feee6c9f02059ef1e646693f3b1f0d71 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:36:45 +0900 Subject: [PATCH 018/110] type with undefined --- src/store/audio.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index 6a2991ff1b..573fc8fbc7 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -169,7 +169,8 @@ export const audioStore: VoiceVoxStoreOptions< }, IS_ALL_ENGINE_READY: (state) => { for (const engineInfo of state.engineInfos) { - const engineState = state.engineStates[engineInfo.key]; + const engineState: EngineState | undefined = + state.engineStates[engineInfo.key]; if (engineState !== "READY") { return false; } @@ -177,7 +178,8 @@ export const audioStore: VoiceVoxStoreOptions< return true; }, IS_ENGINE_READY: (state) => (engineKey) => { - const engineState = state.engineStates[engineKey]; + const engineState: EngineState | undefined = + state.engineStates[engineKey]; return engineState === "READY"; }, ACTIVE_AUDIO_ELEM_CURRENT_TIME: (state) => { @@ -503,7 +505,8 @@ export const audioStore: VoiceVoxStoreOptions< if (!engineInfo) throw new Error(`No such engineInfo registered: key == ${engineKey}`); - let engineState = state.engineStates[engineKey]; + let engineState: EngineState | undefined = + state.engineStates[engineKey]; for (let i = 0; i < 100; i++) { engineState = state.engineStates[engineKey]; From 8272997bd562fa0ab65edaef59aa630eb6fb534a Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:54:08 +0900 Subject: [PATCH 019/110] fix allEngineState logic --- src/views/Home.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/views/Home.vue b/src/views/Home.vue index 9d63306274..140357c1cb 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -456,15 +456,20 @@ export default defineComponent({ const allEngineState = computed(() => { const engineStates = store.state.engineStates; + let lastEngineState: EngineState | undefined = undefined; for (const engineInfo of store.state.engineInfos) { const engineState: EngineState | undefined = engineStates[engineInfo.key]; - if (engineState !== "STARTING") { - return engineState; // FIXME: 暫定的に1つのエンジンの状態を返す + + // FIXME: 1つでも接続テストに成功していないエンジンがあれば、暫定的に起動中とする + if (engineState === "STARTING") { + return engineState; } + + lastEngineState = engineState; } - return "STARTING"; + return lastEngineState; // FIXME: 暫定的に1つのエンジンの状態を返す }); // ライセンス表示 From 63c5816f1b091470fa6da48910fe05fccc373300 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 06:58:01 +0900 Subject: [PATCH 020/110] fix test --- tests/unit/store/Vuex.spec.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts index 84bb104338..a24894980f 100644 --- a/tests/unit/store/Vuex.spec.ts +++ b/tests/unit/store/Vuex.spec.ts @@ -17,9 +17,7 @@ describe("store/vuex.js test", () => { it("create store", () => { const store = createStore({ state: { - engineStates: { - "88022f86-c823-436e-85a3-500c629749c4": "STARTING", - }, + engineStates: {}, defaultStyleIds: [], userCharacterOrder: [], audioItems: {}, @@ -120,9 +118,7 @@ describe("store/vuex.js test", () => { assert.exists(store); assert.isObject(store); assert.isObject(store.state); - assert.equal(store.state.engineStates, { - "88022f86-c823-436e-85a3-500c629749c4": "STARTING", - }); + assert.isEmpty(store.state.engineStates); assert.isArray(store.state.defaultStyleIds); assert.isObject(store.state.audioItems); assert.isEmpty(store.state.audioItems); From 4754d9fd21f57b09d48bad1407dd04fc65afa2f1 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 07:00:53 +0900 Subject: [PATCH 021/110] add comment --- src/store/audio.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index 573fc8fbc7..94f9c847c1 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -175,7 +175,7 @@ export const audioStore: VoiceVoxStoreOptions< return false; } } - return true; + return true; // state.engineStatesが空のときはtrue }, IS_ENGINE_READY: (state) => (engineKey) => { const engineState: EngineState | undefined = From f3777c83cf5af5238b9c13ada7959a173f765378 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 07:28:09 +0900 Subject: [PATCH 022/110] multiple engine: SET_CHARACTER_INFOS, USER_ORDERED_CHARACTER_INFOS --- src/store/audio.ts | 170 +++++++++++++++++++++++++-------------------- src/store/type.ts | 10 ++- 2 files changed, 102 insertions(+), 78 deletions(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index 94f9c847c1..95e53fdbe8 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -188,12 +188,22 @@ export const audioStore: VoiceVoxStoreOptions< : undefined; }, USER_ORDERED_CHARACTER_INFOS: (state) => { - const characterInfos = state.characterInfos?.slice(); - return characterInfos?.sort( - (a, b) => - state.userCharacterOrder.indexOf(a.metas.speakerUuid) - - state.userCharacterOrder.indexOf(b.metas.speakerUuid) - ); + let characterInfoList: CharacterInfo[] = []; + for (const engineInfo of state.engineInfos) { + const engineCharacterInfos: CharacterInfo[] | undefined = + state.characterInfos[engineInfo.key]; + if (engineCharacterInfos === undefined) continue; + + characterInfoList = characterInfoList.concat(engineCharacterInfos); + } + + return characterInfoList.length !== 0 + ? characterInfoList.sort( + (a, b) => + state.userCharacterOrder.indexOf(a.metas.speakerUuid) - + state.userCharacterOrder.indexOf(b.metas.speakerUuid) + ) + : undefined; }, }, @@ -209,9 +219,12 @@ export const audioStore: VoiceVoxStoreOptions< }, SET_CHARACTER_INFOS( state, - { characterInfos }: { characterInfos: CharacterInfo[] } + { + engineKey, + characterInfos, + }: { engineKey: string; characterInfos: CharacterInfo[] } ) { - state.characterInfos = characterInfos; + state.characterInfos[engineKey] = characterInfos; }, SET_ACTIVE_AUDIO_KEY(state, { audioKey }: { audioKey?: string }) { state._activeAudioKey = audioKey; @@ -539,84 +552,91 @@ export const audioStore: VoiceVoxStoreOptions< } } ), - LOAD_CHARACTER: createUILockAction(async ({ state, commit, dispatch }) => { - const engineInfo = state.engineInfos[0]; // TODO: 複数エンジン対応 - if (!engineInfo) - throw new Error(`No such engineInfo registered: index == 0`); - - const speakers = await dispatch("INVOKE_ENGINE_CONNECTOR", { - engineKey: engineInfo.key, - action: "speakersSpeakersGet", - // 連想配列が第一引数になければ失敗する - payload: [{}], - }) - .then(toDispatchResponse("speakersSpeakersGet")) - .catch((error) => { - window.electron.logError(error, `Failed to get speakers.`); - throw error; - }); - const base64ToUrl = function (base64: string, type: string) { - const buffer = Buffer.from(base64, "base64"); - const iconBlob = new Blob([buffer.buffer], { type: type }); - return URL.createObjectURL(iconBlob); - }; - const getStyles = function (speaker: Speaker, speakerInfo: SpeakerInfo) { - const styles: StyleInfo[] = new Array(speaker.styles.length); - speaker.styles.forEach((style, i) => { - const styleInfo = speakerInfo.styleInfos.find( - (styleInfo) => style.id === styleInfo.id - ); - if (!styleInfo) - throw new Error( - `Not found the style id "${style.id}" of "${speaker.name}". ` - ); - const voiceSamples = styleInfo.voiceSamples.map((voiceSample) => { - return base64ToUrl(voiceSample, "audio/wav"); - }); - styles[i] = { - styleName: style.name, - styleId: style.id, - iconPath: base64ToUrl(styleInfo.icon, "image/png"), - voiceSamplePaths: voiceSamples, - }; - }); - return styles; - }; - const getSpeakerInfo = async function (speaker: Speaker) { - const engineInfo = state.engineInfos[0]; // TODO: 複数エンジン対応 + LOAD_CHARACTER: createUILockAction( + async ({ state, commit, dispatch }, { engineKey }) => { + const engineInfo = state.engineInfos.find( + (engineInfo) => engineInfo.key === engineKey + ); if (!engineInfo) throw new Error(`No such engineInfo registered: index == 0`); - const speakerInfo = await dispatch("INVOKE_ENGINE_CONNECTOR", { + const speakers = await dispatch("INVOKE_ENGINE_CONNECTOR", { engineKey: engineInfo.key, - action: "speakerInfoSpeakerInfoGet", - payload: [{ speakerUuid: speaker.speakerUuid }], + action: "speakersSpeakersGet", + // 連想配列が第一引数になければ失敗する + payload: [{}], }) - .then(toDispatchResponse("speakerInfoSpeakerInfoGet")) + .then(toDispatchResponse("speakersSpeakersGet")) .catch((error) => { window.electron.logError(error, `Failed to get speakers.`); throw error; }); - const styles = getStyles(speaker, speakerInfo); - const characterInfo: CharacterInfo = { - portraitPath: base64ToUrl(speakerInfo.portrait, "image/png"), - metas: { - speakerUuid: speaker.speakerUuid, - speakerName: speaker.name, - styles: styles, - policy: speakerInfo.policy, - }, + const base64ToUrl = function (base64: string, type: string) { + const buffer = Buffer.from(base64, "base64"); + const iconBlob = new Blob([buffer.buffer], { type: type }); + return URL.createObjectURL(iconBlob); }; - return characterInfo; - }; - const characterInfos: CharacterInfo[] = await Promise.all( - speakers.map(async (speaker) => { - return await getSpeakerInfo(speaker); - }) - ); + const getStyles = function ( + speaker: Speaker, + speakerInfo: SpeakerInfo + ) { + const styles: StyleInfo[] = new Array(speaker.styles.length); + speaker.styles.forEach((style, i) => { + const styleInfo = speakerInfo.styleInfos.find( + (styleInfo) => style.id === styleInfo.id + ); + if (!styleInfo) + throw new Error( + `Not found the style id "${style.id}" of "${speaker.name}". ` + ); + const voiceSamples = styleInfo.voiceSamples.map((voiceSample) => { + return base64ToUrl(voiceSample, "audio/wav"); + }); + styles[i] = { + styleName: style.name, + styleId: style.id, + iconPath: base64ToUrl(styleInfo.icon, "image/png"), + voiceSamplePaths: voiceSamples, + }; + }); + return styles; + }; + const getSpeakerInfo = async function (speaker: Speaker) { + const engineInfo = state.engineInfos[0]; // TODO: 複数エンジン対応 + if (!engineInfo) + throw new Error(`No such engineInfo registered: index == 0`); - commit("SET_CHARACTER_INFOS", { characterInfos }); - }), + const speakerInfo = await dispatch("INVOKE_ENGINE_CONNECTOR", { + engineKey: engineInfo.key, + action: "speakerInfoSpeakerInfoGet", + payload: [{ speakerUuid: speaker.speakerUuid }], + }) + .then(toDispatchResponse("speakerInfoSpeakerInfoGet")) + .catch((error) => { + window.electron.logError(error, `Failed to get speakers.`); + throw error; + }); + const styles = getStyles(speaker, speakerInfo); + const characterInfo: CharacterInfo = { + portraitPath: base64ToUrl(speakerInfo.portrait, "image/png"), + metas: { + speakerUuid: speaker.speakerUuid, + speakerName: speaker.name, + styles: styles, + policy: speakerInfo.policy, + }, + }; + return characterInfo; + }; + const characterInfos: CharacterInfo[] = await Promise.all( + speakers.map(async (speaker) => { + return await getSpeakerInfo(speaker); + }) + ); + + commit("SET_CHARACTER_INFOS", { engineKey, characterInfos }); + } + ), GENERATE_AUDIO_KEY() { const audioKey = uuidv4(); audioElements[audioKey] = new Audio(); diff --git a/src/store/type.ts b/src/store/type.ts index 33547dbe25..4194e58baa 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -73,7 +73,7 @@ export type QuasarDialog = QVueGlobals["dialog"]; export type AudioStoreState = { engineStates: Record; - characterInfos?: CharacterInfo[]; + characterInfos: Record; audioItems: Record; audioKeys: string[]; audioStates: Record; @@ -131,12 +131,16 @@ type AudioStoreTypes = { mutation: { engineKey: string; engineState: EngineState }; }; - LOAD_CHARACTER: { + LOAD_CHARACTER_ALL: { action(): void; }; + LOAD_CHARACTER: { + action(payload: { engineKey: string }): void; + }; + SET_CHARACTER_INFOS: { - mutation: { characterInfos: CharacterInfo[] }; + mutation: { engineKey: string; characterInfos: CharacterInfo[] }; }; USER_ORDERED_CHARACTER_INFOS: { From 67591d6c2e3d1b9f699494142d701a27b1462471 Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 07:30:33 +0900 Subject: [PATCH 023/110] multiple engine: LOAD_CHARACTER_ALL --- src/store/audio.ts | 7 +++++++ src/views/Home.vue | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index 95e53fdbe8..8ecdf38f56 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -552,6 +552,13 @@ export const audioStore: VoiceVoxStoreOptions< } } ), + LOAD_CHARACTER_ALL: createUILockAction( + async ({ state, dispatch }) => { + for (const engineInfo of state.engineInfos) { + await dispatch("LOAD_CHARACTER", { engineKey: engineInfo.key }); + } + } + ), LOAD_CHARACTER: createUILockAction( async ({ state, commit, dispatch }, { engineKey }) => { const engineInfo = state.engineInfos.find( diff --git a/src/views/Home.vue b/src/views/Home.vue index 140357c1cb..22016a76c0 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -406,7 +406,7 @@ export default defineComponent({ await store.dispatch("GET_ENGINE_INFOS"); await store.dispatch("START_WAITING_ENGINE_ALL"); - await store.dispatch("LOAD_CHARACTER"); + await store.dispatch("LOAD_CHARACTER_ALL"); await store.dispatch("LOAD_USER_CHARACTER_ORDER"); await store.dispatch("LOAD_DEFAULT_STYLE_IDS"); From a97a3c6f25b456fe68ab803730d7e210039eef0a Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 07:56:02 +0900 Subject: [PATCH 024/110] fix buildFileName logic --- src/components/CharacterPortrait.vue | 15 ++++++- src/store/audio.ts | 64 +++++++++++++++++++--------- src/store/type.ts | 5 +++ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/components/CharacterPortrait.vue b/src/components/CharacterPortrait.vue index 3ba0c15e08..6c46fcdadf 100644 --- a/src/components/CharacterPortrait.vue +++ b/src/components/CharacterPortrait.vue @@ -16,15 +16,26 @@ export default defineComponent({ const store = useStore(); const characterInfo = computed(() => { - const characterInfos = store.state.characterInfos || []; const activeAudioKey: string | undefined = store.getters.ACTIVE_AUDIO_KEY; const audioItem = activeAudioKey ? store.state.audioItems[activeAudioKey] : undefined; + + const engineId = audioItem?.engineId; const styleId = audioItem?.styleId; + if (engineId === undefined || styleId === undefined) return undefined; + + const engineInfo = store.state.engineInfos.filter( + (engineInfo) => engineInfo.key === engineId + ); // FIXME: 暫定的にengineKeyをengineIdとして使う + + const flattenCharacterInfos = store.state.engineInfos.flatMap( + (engineInfo) => store.state.characterInfos[engineInfo.key] || [] + ); + return styleId !== undefined - ? characterInfos.find((info) => + ? flattenCharacterInfos.find((info) => info.metas.styles.find((style) => style.styleId === styleId) ) : undefined; diff --git a/src/store/audio.ts b/src/store/audio.ts index 8ecdf38f56..f4621e676c 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -51,6 +51,7 @@ async function generateUniqueIdAndQuery( JSON.stringify([ audioItem.text, audioQuery, + audioItem.engineId, audioItem.styleId, state.experimentalSetting.enableInterrogativeUpspeak, // このフラグが違うと、同じAudioQueryで違う音声が生成されるので追加 ]) @@ -103,22 +104,25 @@ function parseTextFile( function buildFileName(state: State, audioKey: string) { const index = state.audioKeys.indexOf(audioKey); const audioItem = state.audioItems[audioKey]; - let styleName: string | undefined = ""; - const character = state.characterInfos?.find((info) => { - const result = info.metas.styles.findIndex( - (style) => style.styleId === audioItem.styleId - ); - - if (result > -1) { - styleName = info.metas.styles[result].styleName; - } - return result > -1; - }); + if (audioItem.engineId === undefined) + throw new Error("audioItem.engineId === undefined"); + if (audioItem.styleId === undefined) + throw new Error("audioItem.styleId === undefined"); - if (character === undefined) { - throw new Error(); - } + const character = getCharacterInfo( + state, + audioItem.engineId, + audioItem.styleId + ); + if (character === undefined) throw new Error("character === undefined"); + + const style = character.metas.styles.find( + (style) => style.styleId === audioItem.styleId + ); + if (style === undefined) throw new Error("style === undefined"); + + const styleName: string | undefined = style.styleName; const characterName = sanitizeFileName(character.metas.speakerName); let text = sanitizeFileName(audioItem.text); @@ -127,7 +131,7 @@ function buildFileName(state: State, audioKey: string) { } const preFileName = (index + 1).toString().padStart(3, "0"); - // デフォルトのスタイルだとstyleIdが定義されていないのでundefinedになる。なのでファイル名に入れてしまうことを回避する目的で分岐させています。 + // デフォルトのスタイルだとstyleNameが定義されていないのでundefinedになる。なのでファイル名に入れてしまうことを回避する目的で分岐させています。 if (styleName === undefined) { return preFileName + `_${characterName}_${text}.wav`; } @@ -136,11 +140,28 @@ function buildFileName(state: State, audioKey: string) { return preFileName + `_${characterName}(${sanitizedStyleName})_${text}.wav`; } +function getCharacterInfo( + state: State, + engineId: string, + styleId: number +): CharacterInfo | undefined { + const engineKey = engineId; // FIXME: 暫定的にengineKey == engineIdとして使う + const engineCharacterInfos = state.characterInfos[engineKey]; + + // (engineId, styleId)で「スタイル付きキャラクター」は一意である + return engineCharacterInfos.find((characterInfo) => + characterInfo.metas.styles.find( + (characterStyle) => characterStyle.styleId === styleId + ) + ); +} + const audioBlobCache: Record = {}; const audioElements: Record = {}; export const audioStoreState: AudioStoreState = { engineStates: {}, + characterInfos: {}, audioItems: {}, audioKeys: [], audioStates: {}, @@ -187,6 +208,9 @@ export const audioStore: VoiceVoxStoreOptions< ? audioElements[state._activeAudioKey]?.currentTime : undefined; }, + CHARACTER_INFO: (state) => (engineId, styleId) => { + return getCharacterInfo(state, engineId, styleId); + }, USER_ORDERED_CHARACTER_INFOS: (state) => { let characterInfoList: CharacterInfo[] = []; for (const engineInfo of state.engineInfos) { @@ -552,13 +576,11 @@ export const audioStore: VoiceVoxStoreOptions< } } ), - LOAD_CHARACTER_ALL: createUILockAction( - async ({ state, dispatch }) => { - for (const engineInfo of state.engineInfos) { - await dispatch("LOAD_CHARACTER", { engineKey: engineInfo.key }); - } + LOAD_CHARACTER_ALL: createUILockAction(async ({ state, dispatch }) => { + for (const engineInfo of state.engineInfos) { + await dispatch("LOAD_CHARACTER", { engineKey: engineInfo.key }); } - ), + }), LOAD_CHARACTER: createUILockAction( async ({ state, commit, dispatch }, { engineKey }) => { const engineInfo = state.engineInfos.find( diff --git a/src/store/type.ts b/src/store/type.ts index 4194e58baa..8c8331097b 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -31,6 +31,7 @@ import { QVueGlobals } from "quasar"; export type AudioItem = { text: string; + engineId?: string; styleId?: number; query?: AudioQuery; presetKey?: string; @@ -143,6 +144,10 @@ type AudioStoreTypes = { mutation: { engineKey: string; characterInfos: CharacterInfo[] }; }; + CHARACTER_INFO: { + getter(engineId: string, styleId: number): CharacterInfo | undefined; + }; + USER_ORDERED_CHARACTER_INFOS: { getter: CharacterInfo[] | undefined; }; From d0bad7b54e779e6de01acb80f97b6c7a65ef8e9d Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 08:14:35 +0900 Subject: [PATCH 025/110] fix default style id --- src/store/audio.ts | 4 ++-- src/store/index.ts | 29 ++++++++++++++++++++--------- src/views/Home.vue | 23 +++++++++++++++-------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/store/audio.ts b/src/store/audio.ts index f4621e676c..64907edc00 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -140,7 +140,7 @@ function buildFileName(state: State, audioKey: string) { return preFileName + `_${characterName}(${sanitizedStyleName})_${text}.wav`; } -function getCharacterInfo( +export function getCharacterInfo( state: State, engineId: string, styleId: number @@ -150,7 +150,7 @@ function getCharacterInfo( // (engineId, styleId)で「スタイル付きキャラクター」は一意である return engineCharacterInfos.find((characterInfo) => - characterInfo.metas.styles.find( + characterInfo.metas.styles.some( (characterStyle) => characterStyle.styleId === styleId ) ); diff --git a/src/store/index.ts b/src/store/index.ts index b73afa1ebe..6bfa6e00ff 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -19,6 +19,7 @@ import { audioStore, audioCommandStore, audioCommandStoreState, + getCharacterInfo, } from "./audio"; import { projectStoreState, projectStore } from "./project"; import { uiStoreState, uiStore } from "./ui"; @@ -53,12 +54,18 @@ export const indexStore: VoiceVoxStoreOptions< if (state.audioKeys.length === 1) { const audioItem = state.audioItems[state.audioKeys[0]]; if (audioItem.text === "") { - const characterInfo = state.characterInfos?.find( - (info) => - info.metas.styles.find( - (style) => style.styleId == audioItem.styleId - ) != undefined + if (audioItem.engineId == undefined) + throw new Error("audioItem.engineId == undefined"); + + if (audioItem.styleId == undefined) + throw new Error("audioItem.styleId == undefined"); + + const characterInfo = getCharacterInfo( + state, + audioItem.engineId, + audioItem.styleId ); + if (characterInfo == undefined) throw new Error("characterInfo == undefined"); @@ -121,10 +128,12 @@ export const indexStore: VoiceVoxStoreOptions< await window.electron.setUserCharacterOrder(userCharacterOrder); }, GET_NEW_CHARACTERS({ state }) { - if (!state.characterInfos) throw new Error("characterInfos is undefined"); + const flattenCharacterInfos = state.engineInfos.flatMap( + (engineInfo) => state.characterInfos[engineInfo.key] ?? [] + ); // キャラクター表示順序に含まれていなければ新規キャラとみなす - const allSpeakerUuid = state.characterInfos.map( + const allSpeakerUuid = flattenCharacterInfos.map( (characterInfo) => characterInfo.metas.speakerUuid ); const newSpeakerUuid = allSpeakerUuid.filter( @@ -138,11 +147,13 @@ export const indexStore: VoiceVoxStoreOptions< async LOAD_DEFAULT_STYLE_IDS({ commit, state }) { let defaultStyleIds = await window.electron.getDefaultStyleIds(); - if (!state.characterInfos) throw new Error("characterInfos is undefined"); + const flattenCharacterInfos = state.engineInfos.flatMap( + (engineInfo) => state.characterInfos[engineInfo.key] ?? [] + ); // デフォルトスタイルが設定されていない場合は0をセットする // FIXME: 保存しているものとstateのものが異なってしまうので良くない。デフォルトスタイルが未設定の場合はAudioCellsを表示しないようにすべき - const unsetCharacterInfos = state.characterInfos.filter( + const unsetCharacterInfos = flattenCharacterInfos.filter( (characterInfo) => !defaultStyleIds.some( (styleId) => styleId.speakerUuid == characterInfo.metas.speakerUuid diff --git a/src/views/Home.vue b/src/views/Home.vue index 22016a76c0..9c443dea7a 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -174,7 +174,7 @@ import DictionaryManageDialog from "@/components/DictionaryManageDialog.vue"; import { AudioItem, EngineState } from "@/store/type"; import { QResizeObserver } from "quasar"; import path from "path"; -import { HotkeyAction, HotkeyReturnType } from "@/type/preload"; +import { CharacterInfo, HotkeyAction, HotkeyReturnType } from "@/type/preload"; import { parseCombo, setHotkeyFunctions } from "@/store/setting"; export default defineComponent({ @@ -416,13 +416,19 @@ export default defineComponent({ // スタイルが複数あって未選択なキャラがいる場合はデフォルトスタイル選択ダイアログを表示 let isUnsetDefaultStyleIds = false; - if (characterInfos.value == undefined) throw new Error(); - for (const info of characterInfos.value) { - isUnsetDefaultStyleIds ||= - info.metas.styles.length > 1 && - (await store.dispatch("IS_UNSET_DEFAULT_STYLE_ID", { - speakerUuid: info.metas.speakerUuid, - })); + + for (const engineInfo of engineInfos.value) { + const engineCharacterInfos: CharacterInfo[] | undefined = + characterInfos.value[engineInfo.key]; + if (engineCharacterInfos === undefined) throw new Error(); + + for (const info of engineCharacterInfos) { + isUnsetDefaultStyleIds ||= + info.metas.styles.length > 1 && + (await store.dispatch("IS_UNSET_DEFAULT_STYLE_ID", { + speakerUuid: info.metas.speakerUuid, + })); + } } isDefaultStyleSelectDialogOpenComputed.value = isUnsetDefaultStyleIds; @@ -514,6 +520,7 @@ export default defineComponent({ }); // キャラクター並び替え + const engineInfos = computed(() => store.state.engineInfos); const characterInfos = computed(() => store.state.characterInfos); const isCharacterOrderDialogOpenComputed = computed({ get: () => From 9b3defa1d833b621baf06f827bfbb9d70cd23bab Mon Sep 17 00:00:00 2001 From: aoirint Date: Wed, 9 Mar 2022 09:23:13 +0900 Subject: [PATCH 026/110] fix characterInfos from vue --- src/background.ts | 2 +- src/components/AudioCell.vue | 3 +- src/components/CharacterOrderDialog.vue | 28 +++- src/components/CharacterPortrait.vue | 4 - src/components/DefaultStyleSelectDialog.vue | 19 ++- src/components/DictionaryManageDialog.vue | 12 +- src/store/audio.ts | 153 ++++++++++++++++---- src/store/project.ts | 20 +++ src/store/type.ts | 17 ++- src/views/Home.vue | 6 +- 10 files changed, 213 insertions(+), 51 deletions(-) diff --git a/src/background.ts b/src/background.ts index d857522951..5863afccb6 100644 --- a/src/background.ts +++ b/src/background.ts @@ -369,7 +369,7 @@ async function runEngine(engineKey: string) { (engineInfo) => engineInfo.key === engineKey ); if (!engineInfo) - throw new Error(`No such engineInfo registered: key == ${engineKey}`); + throw new Error(`No such engineInfo registered: engineKey == ${engineKey}`); if (!engineInfo.executionEnabled) { log.info(`ENGINE ${engineKey}: Skipped engineInfo execution: disabled`); diff --git a/src/components/AudioCell.vue b/src/components/AudioCell.vue index 3862443ba8..6beff3f651 100644 --- a/src/components/AudioCell.vue +++ b/src/components/AudioCell.vue @@ -245,9 +245,10 @@ export default defineComponent({ } }; - const changeStyleId = (styleId: number) => { + const changeStyleId = (engineKey: string, styleId: number) => { store.dispatch("COMMAND_CHANGE_STYLE_ID", { audioKey: props.audioKey, + engineKey, styleId, }); }; diff --git a/src/components/CharacterOrderDialog.vue b/src/components/CharacterOrderDialog.vue index db31ee34e1..540408e474 100644 --- a/src/components/CharacterOrderDialog.vue +++ b/src/components/CharacterOrderDialog.vue @@ -193,7 +193,7 @@ import { defineComponent, computed, ref, PropType, watch } from "vue"; import draggable from "vuedraggable"; import { useStore } from "@/store"; -import { CharacterInfo, StyleInfo } from "@/type/preload"; +import { CharacterInfo, EngineInfo, StyleInfo } from "@/type/preload"; export default defineComponent({ name: "CharacterOrderDialog", @@ -206,8 +206,12 @@ export default defineComponent({ type: Boolean, required: true, }, + engineInfos: { + type: Object as PropType, + required: true, + }, characterInfos: { - type: Object as PropType, + type: Object as PropType>, required: true, }, }, @@ -220,9 +224,15 @@ export default defineComponent({ set: (val) => emit("update:modelValue", val), }); + const flattenCharacterInfos = computed(() => { + return props.engineInfos.flatMap( + (engineInfo) => store.state.characterInfos[engineInfo.key] || [] + ); + }); + const characterInfosMap = computed(() => { const map: { [key: string]: CharacterInfo } = {}; - props.characterInfos.forEach((characterInfo) => { + flattenCharacterInfos.value.forEach((characterInfo) => { map[characterInfo.metas.speakerUuid] = characterInfo; }); return map; @@ -238,7 +248,7 @@ export default defineComponent({ // 選択中のスタイル const selectedStyleIndexes = ref( Object.fromEntries( - props.characterInfos.map((characterInfo) => [ + flattenCharacterInfos.value.map((characterInfo) => [ characterInfo.metas.speakerUuid, 0, ]) @@ -246,7 +256,7 @@ export default defineComponent({ ); const selectedStyles = computed(() => { const map: { [key: string]: StyleInfo } = {}; - props.characterInfos.forEach((characterInfo) => { + flattenCharacterInfos.value.forEach((characterInfo) => { map[characterInfo.metas.speakerUuid] = characterInfo.metas.styles[ selectedStyleIndexes.value[characterInfo.metas.speakerUuid] @@ -256,7 +266,9 @@ export default defineComponent({ }); // 選択中のキャラクター - const selectedCharacter = ref(props.characterInfos[0].metas.speakerUuid); + const selectedCharacter = ref( + flattenCharacterInfos.value[0].metas.speakerUuid + ); // FIXME: no character const selectCharacter = (speakerUuid: string) => { selectedCharacter.value = speakerUuid; }; @@ -275,7 +287,7 @@ export default defineComponent({ // サンプルの順番、新しいキャラクターは上に sampleCharacterOrder.value = [ ...newCharacters.value, - ...props.characterInfos + ...flattenCharacterInfos.value .filter( (info) => !newCharacters.value.includes(info.metas.speakerUuid) ) @@ -290,7 +302,7 @@ export default defineComponent({ ); // 含まれていないキャラクターを足す - const notIncludesCharacterInfos = props.characterInfos.filter( + const notIncludesCharacterInfos = flattenCharacterInfos.value.filter( (characterInfo) => !characterOrder.value.find( (characterInfoInList) => diff --git a/src/components/CharacterPortrait.vue b/src/components/CharacterPortrait.vue index 6c46fcdadf..27f4093e18 100644 --- a/src/components/CharacterPortrait.vue +++ b/src/components/CharacterPortrait.vue @@ -26,10 +26,6 @@ export default defineComponent({ if (engineId === undefined || styleId === undefined) return undefined; - const engineInfo = store.state.engineInfos.filter( - (engineInfo) => engineInfo.key === engineId - ); // FIXME: 暫定的にengineKeyをengineIdとして使う - const flattenCharacterInfos = store.state.engineInfos.flatMap( (engineInfo) => store.state.characterInfos[engineInfo.key] || [] ); diff --git a/src/components/DefaultStyleSelectDialog.vue b/src/components/DefaultStyleSelectDialog.vue index 75c5fad898..c007091e16 100644 --- a/src/components/DefaultStyleSelectDialog.vue +++ b/src/components/DefaultStyleSelectDialog.vue @@ -177,7 +177,12 @@