From dea4a57387db28791fc306155a48f00716c4e2fc Mon Sep 17 00:00:00 2001 From: tiramisu_oTATo Date: Sun, 24 Sep 2023 15:29:33 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20`audioPlayer.ts`=E3=82=92=E6=96=B0?= =?UTF-8?q?=E8=A6=8F=E4=BD=9C=E6=88=90=E3=81=97`audio.ts`=E3=81=AE?= =?UTF-8?q?=E4=B8=80=E9=83=A8=E6=A9=9F=E8=83=BD=E3=82=92=E7=A7=BB=E8=BB=A2?= =?UTF-8?q?=20(#1579)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/audio.ts | 112 ------------------------------- src/store/audioPlayer.ts | 122 ++++++++++++++++++++++++++++++++++ src/store/index.ts | 5 ++ src/store/type.ts | 61 ++++++++++------- tests/unit/store/Vuex.spec.ts | 4 ++ 5 files changed, 167 insertions(+), 137 deletions(-) create mode 100644 src/store/audioPlayer.ts diff --git a/src/store/audio.ts b/src/store/audio.ts index 4e5af1bc1f..ccf8e679ca 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -248,16 +248,6 @@ export function applyAudioPresetToAudioItem( } const audioBlobCache: Record = {}; -// ユニットテストが落ちるのを回避するための遅延読み込み -const getAudioElement = (() => { - let audioElement: HTMLAudioElement | undefined = undefined; - return () => { - if (audioElement == undefined) { - audioElement = new Audio(); - } - return audioElement; - }; -})(); export const audioStoreState: AudioStoreState = { characterInfos: {}, @@ -268,7 +258,6 @@ export const audioStoreState: AudioStoreState = { audioStates: {}, // audio elementの再生オフセット audioPlayStartPoint: undefined, - nowPlayingAudioKey: undefined, nowPlayingContinuously: false, }; @@ -305,24 +294,6 @@ export const audioStore = createPartialStore({ }, }, - NOW_PLAYING: { - getter(state, getters) { - const activeAudioKey = getters.ACTIVE_AUDIO_KEY; - return ( - activeAudioKey != undefined && - activeAudioKey === state.nowPlayingAudioKey - ); - }, - }, - - ACTIVE_AUDIO_ELEM_CURRENT_TIME: { - getter: (state) => { - return state._activeAudioKey !== undefined - ? getAudioElement().currentTime - : undefined; - }, - }, - LOAD_CHARACTER: { action: createUILockAction(async ({ commit, dispatch }, { engineId }) => { const speakers = await dispatch("INSTANTIATE_ENGINE_CONNECTOR", { @@ -574,15 +545,6 @@ export const audioStore = createPartialStore({ }, }, - SET_AUDIO_NOW_PLAYING: { - mutation( - state, - { audioKey, nowPlaying }: { audioKey: AudioKey; nowPlaying: boolean } - ) { - state.nowPlayingAudioKey = nowPlaying ? audioKey : undefined; - }, - }, - SET_AUDIO_NOW_GENERATING: { mutation( state, @@ -1797,13 +1759,6 @@ export const audioStore = createPartialStore({ ), }, - // NOTE: リファクタリング中、別ファイルに移動予定 - SET_AUDIO_SOURCE: { - mutation(_, { audioBlob }: { audioBlob: Blob }) { - getAudioElement().src = URL.createObjectURL(audioBlob); - }, - }, - PLAY_AUDIO_BLOB: { action: createUILockAction( async ( @@ -1831,73 +1786,6 @@ export const audioStore = createPartialStore({ ), }, - // NOTE: リファクタリング中、別ファイルに移動予定 - PLAY_AUDIO_PLAYER: { - async action( - { state, commit }, - { offset, audioKey }: { offset?: number; audioKey?: AudioKey } - ) { - const audioElement = getAudioElement(); - - if (offset !== undefined) { - audioElement.currentTime = offset; - } - - // 一部ブラウザではsetSinkIdが実装されていないので、その環境では無視する - if (audioElement.setSinkId) { - audioElement - .setSinkId(state.savingSetting.audioOutputDevice) - .catch((err) => { - const stop = () => { - audioElement.pause(); - audioElement.removeEventListener("canplay", stop); - }; - audioElement.addEventListener("canplay", stop); - window.electron.showMessageDialog({ - type: "error", - title: "エラー", - message: "再生デバイスが見つかりません", - }); - throw new Error(err); - }); - } - - // 再生終了時にresolveされるPromiseを返す - const played = async () => { - if (audioKey) { - commit("SET_AUDIO_NOW_PLAYING", { audioKey, nowPlaying: true }); - } - }; - audioElement.addEventListener("play", played); - - let paused: () => void; - const audioPlayPromise = new Promise((resolve) => { - paused = () => { - resolve(audioElement.ended); - }; - audioElement.addEventListener("pause", paused); - }).finally(async () => { - audioElement.removeEventListener("play", played); - audioElement.removeEventListener("pause", paused); - if (audioKey) { - commit("SET_AUDIO_NOW_PLAYING", { audioKey, nowPlaying: false }); - } - }); - - audioElement.play(); - - return audioPlayPromise; - }, - }, - - STOP_AUDIO: { - // 停止中でも呼び出して問題ない - action() { - // PLAY_ でonpause時の処理が設定されているため、pauseするだけで良い - getAudioElement().pause(); - }, - }, - SET_AUDIO_PRESET_KEY: { mutation( state, diff --git a/src/store/audioPlayer.ts b/src/store/audioPlayer.ts new file mode 100644 index 0000000000..966a6f1429 --- /dev/null +++ b/src/store/audioPlayer.ts @@ -0,0 +1,122 @@ +/** + * HTMLAudioElement周りの音声再生・停止などを担当する。 + */ +import { createPartialStore } from "./vuex"; +import { AudioPlayerStoreState, AudioPlayerStoreTypes } from "./type"; +import { AudioKey } from "@/type/preload"; + +// ユニットテストが落ちるのを回避するための遅延読み込み +const getAudioElement = (() => { + let audioElement: HTMLAudioElement | undefined = undefined; + return () => { + if (audioElement == undefined) { + audioElement = new Audio(); + } + return audioElement; + }; +})(); + +export const audioPlayerStoreState: AudioPlayerStoreState = { + nowPlayingAudioKey: undefined, +}; + +export const audioPlayerStore = createPartialStore({ + ACTIVE_AUDIO_ELEM_CURRENT_TIME: { + getter: (state) => { + return state._activeAudioKey !== undefined + ? getAudioElement().currentTime + : undefined; + }, + }, + + NOW_PLAYING: { + getter(state, getters) { + const activeAudioKey = getters.ACTIVE_AUDIO_KEY; + return ( + activeAudioKey != undefined && + activeAudioKey === state.nowPlayingAudioKey + ); + }, + }, + + SET_AUDIO_NOW_PLAYING: { + mutation( + state, + { audioKey, nowPlaying }: { audioKey: AudioKey; nowPlaying: boolean } + ) { + state.nowPlayingAudioKey = nowPlaying ? audioKey : undefined; + }, + }, + + SET_AUDIO_SOURCE: { + mutation(_, { audioBlob }: { audioBlob: Blob }) { + getAudioElement().src = URL.createObjectURL(audioBlob); + }, + }, + + PLAY_AUDIO_PLAYER: { + async action( + { state, commit }, + { offset, audioKey }: { offset?: number; audioKey?: AudioKey } + ) { + const audioElement = getAudioElement(); + + if (offset !== undefined) { + audioElement.currentTime = offset; + } + + // 一部ブラウザではsetSinkIdが実装されていないので、その環境では無視する + if (audioElement.setSinkId) { + audioElement + .setSinkId(state.savingSetting.audioOutputDevice) + .catch((err) => { + const stop = () => { + audioElement.pause(); + audioElement.removeEventListener("canplay", stop); + }; + audioElement.addEventListener("canplay", stop); + window.electron.showMessageDialog({ + type: "error", + title: "エラー", + message: "再生デバイスが見つかりません", + }); + throw new Error(err); + }); + } + + // 再生終了時にresolveされるPromiseを返す + const played = async () => { + if (audioKey) { + commit("SET_AUDIO_NOW_PLAYING", { audioKey, nowPlaying: true }); + } + }; + audioElement.addEventListener("play", played); + + let paused: () => void; + const audioPlayPromise = new Promise((resolve) => { + paused = () => { + resolve(audioElement.ended); + }; + audioElement.addEventListener("pause", paused); + }).finally(async () => { + audioElement.removeEventListener("play", played); + audioElement.removeEventListener("pause", paused); + if (audioKey) { + commit("SET_AUDIO_NOW_PLAYING", { audioKey, nowPlaying: false }); + } + }); + + audioElement.play(); + + return audioPlayPromise; + }, + }, + + STOP_AUDIO: { + // 停止中でも呼び出して問題ない + action() { + // PLAY_ でonpause時の処理が設定されているため、pauseするだけで良い + getAudioElement().pause(); + }, + }, +}); diff --git a/src/store/index.ts b/src/store/index.ts index fb99766866..eca0e6dbba 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -17,6 +17,7 @@ import { audioCommandStoreState, getCharacterInfo, } from "./audio"; +import { audioPlayerStoreState, audioPlayerStore } from "./audioPlayer"; import { projectStoreState, projectStore } from "./project"; import { uiStoreState, uiStore } from "./ui"; import { settingStoreState, settingStore } from "./setting"; @@ -349,6 +350,7 @@ export const store = createStore({ state: { ...uiStoreState, ...audioStoreState, + ...audioPlayerStoreState, ...commandStoreState, ...engineStoreState, ...projectStoreState, @@ -363,6 +365,7 @@ export const store = createStore({ getters: { ...uiStore.getters, ...audioStore.getters, + ...audioPlayerStore.getters, ...commandStore.getters, ...engineStore.getters, ...projectStore.getters, @@ -377,6 +380,7 @@ export const store = createStore({ mutations: { ...uiStore.mutations, ...audioStore.mutations, + ...audioPlayerStore.mutations, ...commandStore.mutations, ...engineStore.mutations, ...projectStore.mutations, @@ -391,6 +395,7 @@ export const store = createStore({ actions: { ...uiStore.actions, ...audioStore.actions, + ...audioPlayerStore.actions, ...engineStore.actions, ...commandStore.actions, ...projectStore.actions, diff --git a/src/store/type.ts b/src/store/type.ts index 4712bfb041..48cbbc368e 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -131,7 +131,6 @@ export type AudioStoreState = { _activeAudioKey?: AudioKey; _selectedAudioKeys?: AudioKey[]; audioPlayStartPoint?: number; - nowPlayingAudioKey?: AudioKey; nowPlayingContinuously: boolean; }; @@ -152,14 +151,6 @@ export type AudioStoreTypes = { getter(audioKey: AudioKey): boolean; }; - NOW_PLAYING: { - getter: boolean; - }; - - ACTIVE_AUDIO_ELEM_CURRENT_TIME: { - getter: number | undefined; - }; - LOAD_CHARACTER: { action(payload: { engineId: EngineId }): void; }; @@ -207,10 +198,6 @@ export type AudioStoreTypes = { action(payload: { startPoint?: number }): void; }; - SET_AUDIO_NOW_PLAYING: { - mutation: { audioKey: AudioKey; nowPlaying: boolean }; - }; - SET_AUDIO_NOW_GENERATING: { mutation: { audioKey: AudioKey; nowGenerating: boolean }; }; @@ -457,22 +444,10 @@ export type AudioStoreTypes = { action(payload: { audioKey: AudioKey }): boolean; }; - SET_AUDIO_SOURCE: { - mutation: { audioBlob: Blob }; - }; - PLAY_AUDIO_BLOB: { action(payload: { audioBlob: Blob; audioKey?: AudioKey }): boolean; }; - PLAY_AUDIO_PLAYER: { - action(payload: { offset?: number; audioKey?: AudioKey }): Promise; - }; - - STOP_AUDIO: { - action(): void; - }; - SET_AUDIO_PRESET_KEY: { mutation: { audioKey: AudioKey; @@ -701,6 +676,40 @@ export type AudioCommandStoreTypes = { }; }; +/* + * Audio Player Store Types + */ + +export type AudioPlayerStoreState = { + nowPlayingAudioKey?: AudioKey; +}; + +export type AudioPlayerStoreTypes = { + ACTIVE_AUDIO_ELEM_CURRENT_TIME: { + getter: number | undefined; + }; + + NOW_PLAYING: { + getter: boolean; + }; + + SET_AUDIO_NOW_PLAYING: { + mutation: { audioKey: AudioKey; nowPlaying: boolean }; + }; + + SET_AUDIO_SOURCE: { + mutation: { audioBlob: Blob }; + }; + + PLAY_AUDIO_PLAYER: { + action(payload: { offset?: number; audioKey?: AudioKey }): Promise; + }; + + STOP_AUDIO: { + action(): void; + }; +}; + /* * Command Store Types */ @@ -1477,6 +1486,7 @@ export type ProxyStoreTypes = { */ export type State = AudioStoreState & + AudioPlayerStoreState & AudioCommandStoreState & CommandStoreState & EngineStoreState & @@ -1489,6 +1499,7 @@ export type State = AudioStoreState & ProxyStoreState; type AllStoreTypes = AudioStoreTypes & + AudioPlayerStoreTypes & AudioCommandStoreTypes & CommandStoreTypes & EngineStoreTypes & diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts index dbb8a654e9..c32a665624 100644 --- a/tests/unit/store/Vuex.spec.ts +++ b/tests/unit/store/Vuex.spec.ts @@ -5,6 +5,7 @@ import { createStore } from "@/store/vuex"; import { AllActions, AllGetters, AllMutations, State } from "@/store/type"; import { commandStore } from "@/store/command"; import { audioStore, audioCommandStore } from "@/store/audio"; +import { audioPlayerStore } from "@/store/audioPlayer"; import { projectStore } from "@/store/project"; import { uiStore } from "@/store/ui"; import { settingStore } from "@/store/setting"; @@ -150,6 +151,7 @@ describe("store/vuex.js test", () => { getters: { ...uiStore.getters, ...audioStore.getters, + ...audioPlayerStore.getters, ...commandStore.getters, ...engineStore.getters, ...projectStore.getters, @@ -163,6 +165,7 @@ describe("store/vuex.js test", () => { mutations: { ...uiStore.mutations, ...audioStore.mutations, + ...audioPlayerStore.mutations, ...commandStore.mutations, ...engineStore.mutations, ...projectStore.mutations, @@ -176,6 +179,7 @@ describe("store/vuex.js test", () => { actions: { ...uiStore.actions, ...audioStore.actions, + ...audioPlayerStore.actions, ...commandStore.actions, ...engineStore.actions, ...projectStore.actions,