diff --git a/src/composables/useDefaultPreset.ts b/src/composables/useDefaultPreset.ts index 4305c6aa5b..5e6043aa9d 100644 --- a/src/composables/useDefaultPreset.ts +++ b/src/composables/useDefaultPreset.ts @@ -6,9 +6,6 @@ export const useDefaultPreset = () => { const store = useStore(); const defaultPresetKeys = computed(() => store.state.defaultPresetKeys); - const defaultPresetKeySets = computed( - () => new Set(Object.values(store.state.defaultPresetKeys)) - ); const getDefaultPresetKeyForVoice = (voice: Voice): string => { const voiceId = VoiceId(voice); @@ -16,7 +13,7 @@ export const useDefaultPreset = () => { }; const isDefaultPresetKey = (presetKey: PresetKey): boolean => { - return defaultPresetKeySets.value.has(presetKey); + return store.getters.DEFAULT_PRESET_KEY_SETS.has(presetKey); }; return { diff --git a/src/store/audio.ts b/src/store/audio.ts index 3faa60e151..99c482a53a 100644 --- a/src/store/audio.ts +++ b/src/store/audio.ts @@ -23,6 +23,7 @@ import { } from "./utility"; import { convertAudioQueryFromEditorToEngine } from "./proxy"; import { createPartialStore } from "./vuex"; +import { determineNextPresetKey } from "./preset"; import { AudioKey, CharacterInfo, @@ -31,12 +32,12 @@ import { EngineId, MoraDataType, MorphingInfo, + Preset, PresetKey, SpeakerId, StyleId, StyleInfo, Voice, - VoiceId, WriteFileErrorResult, } from "@/type/preload"; import { AudioQuery, AccentPhrase, Speaker, SpeakerInfo } from "@/openapi"; @@ -188,69 +189,30 @@ export function getCharacterInfo( } /** - * configを参照して割り当てるべきpresetKeyとそのPresetを適用すべきかどうかを返す + * 与えたAudioItemを元に、Presetを適用した新しいAudioItemを返す */ -export function determineNextPresetKey( - state: State, - voice: Voice, - presetKeyCandidate: PresetKey | undefined, - shouldCopyBaseAudioItem: boolean, - isVoiceChanged = false -): { - nextPresetKey: PresetKey | undefined; - shouldApplyPreset: boolean; -} { - const defaultPresetKeyForCurrentVoice = - state.defaultPresetKeys[VoiceId(voice)]; - - const isDefaultPreset = Object.values(state.defaultPresetKeys).some( - (key) => key === presetKeyCandidate - ); - - // コピーすべきBaseAudioItemがない=初回作成時 - if (!shouldCopyBaseAudioItem) { - return { - nextPresetKey: defaultPresetKeyForCurrentVoice, - shouldApplyPreset: state.experimentalSetting.enablePreset, - }; +export function applyAudioPresetToAudioItem( + audioItem: AudioItem, + presetItem: Preset +): AudioItem { + if (audioItem.query == undefined) { + throw new Error("audioItem.query is undefined"); } - // ボイス切り替え時 - if (isVoiceChanged) { - if (state.experimentalSetting.shouldApplyDefaultPresetOnVoiceChanged) { - // デフォルトプリセットを適用する - return { - nextPresetKey: defaultPresetKeyForCurrentVoice, - shouldApplyPreset: true, - }; - } - - // 引き継ぎ元が他スタイルのデフォルトプリセットだった場合 - // 別キャラのデフォルトプリセットを引き継がないようにする - // それ以外は指定そのまま - return { - nextPresetKey: isDefaultPreset - ? defaultPresetKeyForCurrentVoice - : presetKeyCandidate, - shouldApplyPreset: false, - }; - } + // Filter name property from presetItem in order to extract audioInfos. + const { name: _, morphingInfo, ...presetAudioInfos } = presetItem; - // 以下はAudioItemコピー時 + // Type Assertion + const audioInfos: Omit< + AudioQuery, + "accentPhrases" | "outputSamplingRate" | "outputStereo" | "kana" + > = presetAudioInfos; - if (state.inheritAudioInfo) { - // パラメータ引継ぎがONならそのまま引き継ぐ - return { - nextPresetKey: presetKeyCandidate, - shouldApplyPreset: false, - }; - } + const newAudioItem = { ...audioItem }; + newAudioItem.query = { ...audioItem.query, ...audioInfos }; + newAudioItem.morphingInfo = morphingInfo ? { ...morphingInfo } : undefined; - // それ以外はデフォルトプリセットを割り当て、適用するかはプリセットのON/OFFに依存 - return { - nextPresetKey: defaultPresetKeyForCurrentVoice, - shouldApplyPreset: state.experimentalSetting.enablePreset, - }; + return newAudioItem; } const audioBlobCache: Record = {}; @@ -603,9 +565,9 @@ export const audioStore = createPartialStore({ }).catch(() => undefined) : undefined; - const audioItem: AudioItem = { text, voice }; + const newAudioItem: AudioItem = { text, voice }; if (query != undefined) { - audioItem.query = query; + newAudioItem.query = query; } const presetKeyCandidate = payload.baseAudioItem?.presetKey; @@ -614,37 +576,45 @@ export const audioStore = createPartialStore({ state, voice, presetKeyCandidate, - state.inheritAudioInfo && baseAudioItem !== undefined + baseAudioItem ? "copy" : "generate" ); - audioItem.presetKey = nextPresetKey; + newAudioItem.presetKey = nextPresetKey; + // audioItemに対してプリセットを適用する + if (shouldApplyPreset) { + if (nextPresetKey) { + const preset = state.presetItems[nextPresetKey]; + return applyAudioPresetToAudioItem(newAudioItem, preset); + } + } + + // プリセットを適用しないならパラメータを引き継ぐ if ( state.inheritAudioInfo && baseAudioItem && baseAudioItem.query && - audioItem.query + newAudioItem.query ) { //引数にbaseAudioItemがある場合、話速等のパラメータを引き継いだAudioItemを返す //baseAudioItem.queryが未設定の場合は引き継がない(起動直後等?) - audioItem.query.speedScale = baseAudioItem.query.speedScale; - audioItem.query.pitchScale = baseAudioItem.query.pitchScale; - audioItem.query.intonationScale = baseAudioItem.query.intonationScale; - audioItem.query.volumeScale = baseAudioItem.query.volumeScale; - audioItem.query.prePhonemeLength = baseAudioItem.query.prePhonemeLength; - audioItem.query.postPhonemeLength = + newAudioItem.query.speedScale = baseAudioItem.query.speedScale; + newAudioItem.query.pitchScale = baseAudioItem.query.pitchScale; + newAudioItem.query.intonationScale = + baseAudioItem.query.intonationScale; + newAudioItem.query.volumeScale = baseAudioItem.query.volumeScale; + newAudioItem.query.prePhonemeLength = + baseAudioItem.query.prePhonemeLength; + newAudioItem.query.postPhonemeLength = baseAudioItem.query.postPhonemeLength; - audioItem.query.outputSamplingRate = + newAudioItem.query.outputSamplingRate = baseAudioItem.query.outputSamplingRate; - audioItem.query.outputStereo = baseAudioItem.query.outputStereo; - audioItem.morphingInfo = baseAudioItem.morphingInfo; - } - - // audioItemに対してプリセットを適用する - if (shouldApplyPreset) { - await dispatch("APPLY_AUDIO_PRESET_TO_AUDIO_ITEM", { audioItem }); + newAudioItem.query.outputStereo = baseAudioItem.query.outputStereo; + newAudioItem.morphingInfo = baseAudioItem.morphingInfo + ? { ...baseAudioItem.morphingInfo } + : undefined; } - return audioItem; + return newAudioItem; }, }, @@ -1045,41 +1015,16 @@ export const audioStore = createPartialStore({ }, }, - APPLY_AUDIO_PRESET_TO_AUDIO_ITEM: { - // FIXME: audioItemを直接書き換えないようにするか、関数化する - mutation(state, { audioItem }) { - if ( - audioItem == undefined || - audioItem.presetKey == undefined || - audioItem.query == undefined - ) - return; - const presetItem = state.presetItems[audioItem.presetKey]; - if (presetItem == undefined) return; - - // Filter name property from presetItem in order to extract audioInfos. - const { name: _, morphingInfo, ...presetAudioInfos } = presetItem; - - // Type Assertion - const audioInfos: Omit< - AudioQuery, - "accentPhrases" | "outputSamplingRate" | "outputStereo" | "kana" - > = presetAudioInfos; + APPLY_AUDIO_PRESET: { + mutation(state, { audioKey }: { audioKey: AudioKey }) { + const audioItem = state.audioItems[audioKey]; - audioItem.query = { ...audioItem.query, ...audioInfos }; + if (!audioItem.presetKey) return; - audioItem.morphingInfo = morphingInfo; - }, - action({ commit }, { audioItem }) { - commit("APPLY_AUDIO_PRESET_TO_AUDIO_ITEM", { audioItem }); - }, - }, + const presetItem = state.presetItems[audioItem.presetKey]; + const newAudioItem = applyAudioPresetToAudioItem(audioItem, presetItem); - APPLY_AUDIO_PRESET: { - mutation(state, { audioKey }: { audioKey: AudioKey }) { - audioStore.mutations.APPLY_AUDIO_PRESET_TO_AUDIO_ITEM(state, { - audioItem: state.audioItems[audioKey], - }); + state.audioItems[audioKey] = newAudioItem; }, }, @@ -2120,8 +2065,7 @@ export const audioCommandStore = transformCommandStore( draft, payload.voice, presetKey, - true, - true + "changeVoice" ); audioStore.mutations.SET_AUDIO_PRESET_KEY(draft, { diff --git a/src/store/preset.ts b/src/store/preset.ts index e162c6248f..8acdec0962 100644 --- a/src/store/preset.ts +++ b/src/store/preset.ts @@ -1,7 +1,80 @@ import { v4 as uuidv4 } from "uuid"; import { createPartialStore } from "./vuex"; -import { PresetStoreState, PresetStoreTypes } from "@/store/type"; -import { Preset, PresetKey, VoiceId } from "@/type/preload"; +import { PresetStoreState, PresetStoreTypes, State } from "@/store/type"; +import { Preset, PresetKey, Voice, VoiceId } from "@/type/preload"; + +/** + * configを参照して割り当てるべきpresetKeyとそのPresetを適用すべきかどうかを返す + * + * generate: プロジェクト新規作成時、空のAudioItemを作成する場合 + * copy: 元となるAudioItemがある場合(+ボタンで作成したとき) + * changeVoice: ボイス切り替え時 + */ +export function determineNextPresetKey( + state: Pick< + State, + "defaultPresetKeys" | "experimentalSetting" | "inheritAudioInfo" + >, + voice: Voice, + presetKeyCandidate: PresetKey | undefined, + operation: "generate" | "copy" | "changeVoice" +): { + nextPresetKey: PresetKey | undefined; + shouldApplyPreset: boolean; +} { + const defaultPresetKeyForCurrentVoice = + state.defaultPresetKeys[VoiceId(voice)]; + + switch (operation) { + case "generate": { + // 初回作成時 + return { + nextPresetKey: defaultPresetKeyForCurrentVoice, + shouldApplyPreset: state.experimentalSetting.enablePreset, + }; + } + case "copy": { + // 元となるAudioItemがある場合 + if (state.inheritAudioInfo) { + // パラメータ引継ぎがONならそのまま引き継ぐ + return { + nextPresetKey: presetKeyCandidate, + shouldApplyPreset: false, + }; + } + + // それ以外はデフォルトプリセットを割り当て、適用するかはプリセットのON/OFFに依存 + return { + nextPresetKey: defaultPresetKeyForCurrentVoice, + shouldApplyPreset: state.experimentalSetting.enablePreset, + }; + } + case "changeVoice": { + // ボイス切り替え時 + if (state.experimentalSetting.shouldApplyDefaultPresetOnVoiceChanged) { + // デフォルトプリセットを適用する + return { + nextPresetKey: defaultPresetKeyForCurrentVoice, + shouldApplyPreset: true, + }; + } + + const isDefaultPreset = Object.values(state.defaultPresetKeys).some( + (key) => key === presetKeyCandidate + ); + + // 引き継ぎ元が他スタイルのデフォルトプリセットだった場合 + // 別キャラのデフォルトプリセットを引き継がないようにする + // それ以外は指定そのまま + return { + nextPresetKey: isDefaultPreset + ? defaultPresetKeyForCurrentVoice + : presetKeyCandidate, + shouldApplyPreset: false, + }; + } + } +} export const presetStoreState: PresetStoreState = { presetItems: {}, @@ -10,6 +83,12 @@ export const presetStoreState: PresetStoreState = { }; export const presetStore = createPartialStore({ + DEFAULT_PRESET_KEY_SETS: { + getter: (state) => { + return new Set(Object.values(state.defaultPresetKeys)); + }, + }, + SET_PRESET_ITEMS: { mutation( state, diff --git a/src/store/type.ts b/src/store/type.ts index 88034e8c27..e3252d8090 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -359,11 +359,6 @@ export type AudioStoreTypes = { }; }; - APPLY_AUDIO_PRESET_TO_AUDIO_ITEM: { - mutation: { audioItem: AudioItem }; - action(payload: { audioItem: AudioItem }): void; - }; - APPLY_AUDIO_PRESET: { mutation: { audioKey: AudioKey }; }; @@ -1263,6 +1258,9 @@ export type PresetStoreState = { }; export type PresetStoreTypes = { + DEFAULT_PRESET_KEY_SETS: { + getter: Set; + }; SET_PRESET_ITEMS: { mutation: { presetItems: Record;