diff --git a/src/components/AudioDetail.vue b/src/components/AudioDetail.vue index 9f92258e99..48c23008c1 100644 --- a/src/components/AudioDetail.vue +++ b/src/components/AudioDetail.vue @@ -176,11 +176,11 @@ const hotkeyMap = new Map HotkeyReturnType>([ if ( !uiLocked.value && store.getters.ACTIVE_AUDIO_KEY && - store.state.audioPlayStartPoint !== undefined + store.getters.AUDIO_PLAY_START_POINT !== undefined ) { store.dispatch("COMMAND_RESET_SELECTED_MORA_PITCH_AND_LENGTH", { audioKey: store.getters.ACTIVE_AUDIO_KEY, - accentPhraseIndex: store.state.audioPlayStartPoint, + accentPhraseIndex: store.getters.AUDIO_PLAY_START_POINT, }); } }, @@ -221,7 +221,7 @@ const activePointScrollMode = computed(() => store.state.activePointScrollMode); // 再生開始アクセント句 const startPoint = computed({ get: () => { - return store.state.audioPlayStartPoint; + return store.getters.AUDIO_PLAY_START_POINT; }, set: (startPoint) => { store.dispatch("SET_AUDIO_PLAY_START_POINT", { startPoint }); diff --git a/src/store/audio.ts b/src/store/audio.ts index ccf8e679ca..14d9b22f77 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -256,8 +256,6 @@ export const audioStoreState: AudioStoreState = { audioItems: {}, audioKeys: [], audioStates: {}, - // audio elementの再生オフセット - audioPlayStartPoint: undefined, nowPlayingContinuously: false, }; @@ -294,6 +292,29 @@ export const audioStore = createPartialStore({ }, }, + /** + * audio elementの再生オフセット。 + * 選択+削除 や 挿入+選択+元に戻す などを行った場合でも範囲外にならないようにクランプする。 + * ACTIVE_AUDIO_KEYがundefinedのときはundefinedを返す。 + */ + AUDIO_PLAY_START_POINT: { + getter(state, getters) { + const audioPlayStartPoint = state._audioPlayStartPoint; + if ( + audioPlayStartPoint == undefined || + getters.ACTIVE_AUDIO_KEY == undefined + ) { + return undefined; + } + const length = + state.audioItems[getters.ACTIVE_AUDIO_KEY].query?.accentPhrases.length; + if (length == undefined) { + return undefined; + } + return Math.max(0, Math.min(length - 1, audioPlayStartPoint)); + }, + }, + LOAD_CHARACTER: { action: createUILockAction(async ({ commit, dispatch }, { engineId }) => { const speakers = await dispatch("INSTANTIATE_ENGINE_CONNECTOR", { @@ -538,7 +559,7 @@ export const audioStore = createPartialStore({ SET_AUDIO_PLAY_START_POINT: { mutation(state, { startPoint }: { startPoint?: number }) { - state.audioPlayStartPoint = startPoint; + state._audioPlayStartPoint = startPoint; }, action({ commit }, { startPoint }: { startPoint?: number }) { commit("SET_AUDIO_PLAY_START_POINT", { startPoint }); @@ -1762,7 +1783,7 @@ export const audioStore = createPartialStore({ PLAY_AUDIO_BLOB: { action: createUILockAction( async ( - { state, commit, dispatch }, + { getters, commit, dispatch }, { audioBlob, audioKey }: { audioBlob: Blob; audioKey?: AudioKey } ) => { commit("SET_AUDIO_SOURCE", { audioBlob }); @@ -1774,7 +1795,8 @@ export const audioStore = createPartialStore({ }); if (accentPhraseOffsets.length === 0) throw new Error("accentPhraseOffsets.length === 0"); - const startTime = accentPhraseOffsets[state.audioPlayStartPoint ?? 0]; + const startTime = + accentPhraseOffsets[getters.AUDIO_PLAY_START_POINT ?? 0]; if (startTime === undefined) throw Error("startTime === undefined"); // 小さい値が切り捨てられることでフォーカスされるアクセントフレーズが一瞬元に戻るので、 // 再生に影響のない程度かつ切り捨てられない値を加算する @@ -1803,9 +1825,9 @@ export const audioStore = createPartialStore({ }, PLAY_CONTINUOUSLY_AUDIO: { - action: createUILockAction(async ({ state, commit, dispatch }) => { + action: createUILockAction(async ({ state, getters, commit, dispatch }) => { const currentAudioKey = state._activeAudioKey; - const currentAudioPlayStartPoint = state.audioPlayStartPoint; + const currentAudioPlayStartPoint = getters.AUDIO_PLAY_START_POINT; let index = 0; if (currentAudioKey !== undefined) { diff --git a/src/store/type.ts b/src/store/type.ts index 48cbbc368e..b9e8e881f9 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -130,7 +130,7 @@ export type AudioStoreState = { audioStates: Record; _activeAudioKey?: AudioKey; _selectedAudioKeys?: AudioKey[]; - audioPlayStartPoint?: number; + _audioPlayStartPoint?: number; nowPlayingContinuously: boolean; }; @@ -151,6 +151,10 @@ export type AudioStoreTypes = { getter(audioKey: AudioKey): boolean; }; + AUDIO_PLAY_START_POINT: { + getter: number | undefined; + }; + LOAD_CHARACTER: { action(payload: { engineId: EngineId }): void; }; diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts index c32a665624..b6863137a5 100644 --- a/tests/unit/store/Vuex.spec.ts +++ b/tests/unit/store/Vuex.spec.ts @@ -35,7 +35,6 @@ describe("store/vuex.js test", () => { audioItems: {}, audioKeys: [], audioStates: {}, - audioPlayStartPoint: 0, uiLockCount: 0, dialogLockCount: 0, reloadingLock: false, @@ -209,7 +208,6 @@ describe("store/vuex.js test", () => { assert.isEmpty(store.state.audioKeys); assert.isObject(store.state.audioStates); assert.isEmpty(store.state.audioStates); - assert.equal(store.state.audioPlayStartPoint, 0); assert.equal(store.state.uiLockCount, 0); assert.equal(store.state.nowPlayingContinuously, false); assert.isArray(store.state.undoCommands);