diff --git a/src/components/AudioDetail.vue b/src/components/AudioDetail.vue
index 26b1a706d4..9bc252ace2 100644
--- a/src/components/AudioDetail.vue
+++ b/src/components/AudioDetail.vue
@@ -5,8 +5,20 @@
-
-
+
+
@@ -260,7 +272,7 @@ import AudioAccent from "./AudioAccent.vue";
import AudioParameter from "./AudioParameter.vue";
import { HotkeyAction, HotkeyReturnType, MoraDataType } from "@/type/preload";
import { setHotkeyFunctions } from "@/store/setting";
-import { Mora } from "@/openapi/models";
+import { EngineManifest, Mora } from "@/openapi/models";
export default defineComponent({
components: { AudioAccent, AudioParameter, Tip },
@@ -349,6 +361,32 @@ export default defineComponent({
const query = computed(() => audioItem.value?.query);
const accentPhrases = computed(() => query.value?.accentPhrases);
+ const supportedFeatures = computed(
+ () =>
+ (audioItem.value?.engineId &&
+ store.state.engineManifests[audioItem.value?.engineId]
+ .supportedFeatures) as
+ | EngineManifest["supportedFeatures"]
+ | undefined
+ );
+
+ // エンジンが変わったとき、selectedDetailが対応していないものを選択している場合はaccentに戻す
+ // TODO: 連続再生するとアクセントに移動してしまうため、タブの中身を全てdisabledにする、半透明divをかぶせるなど
+ // タブ自体の無効化&移動以外の方法で無効化する
+ watch(
+ supportedFeatures,
+ (newFeatures) => {
+ if (
+ (!newFeatures?.adjustMoraPitch && selectedDetail.value === "pitch") ||
+ (!newFeatures?.adjustPhonemeLength &&
+ selectedDetail.value === "length")
+ ) {
+ selectedDetail.value = "accent";
+ }
+ },
+ { immediate: true }
+ );
+
const activePointScrollMode = computed(
() => store.state.activePointScrollMode
);
@@ -767,6 +805,7 @@ export default defineComponent({
activePoint,
setPlayAndStartPoint,
uiLocked,
+ supportedFeatures,
audioItem,
query,
accentPhrases,
diff --git a/src/components/AudioInfo.vue b/src/components/AudioInfo.vue
index 772faa107c..e35a7cbea8 100644
--- a/src/components/AudioInfo.vue
+++ b/src/components/AudioInfo.vue
@@ -165,7 +165,11 @@
- 話速 {{ speedScaleSlider.state.currentValue.value?.toFixed(2) }}
- 音高 {{ pitchScaleSlider.state.currentValue.value?.toFixed(2) }}
- 抑揚
{{ intonationScaleSlider.state.currentValue.value?.toFixed(2) }}
@@ -232,7 +244,11 @@
/>
- 音量 {{ volumeScaleSlider.state.currentValue.value?.toFixed(2) }}
- 開始無音
{{ prePhonemeLengthSlider.state.currentValue.value?.toFixed(2) }}
@@ -277,7 +297,11 @@
/>
- 終了無音
{{ postPhonemeLengthSlider.state.currentValue.value?.toFixed(2) }}
@@ -310,6 +334,7 @@ import { useStore } from "@/store";
import { Preset } from "@/type/preload";
import { previewSliderHelper } from "@/helpers/previewSliderHelper";
import PresetManageDialog from "./PresetManageDialog.vue";
+import { EngineManifest } from "@/openapi";
export default defineComponent({
name: "AudioInfo",
@@ -333,6 +358,15 @@ export default defineComponent({
);
const query = computed(() => audioItem.value?.query);
+ const supportedFeatures = computed(
+ () =>
+ (audioItem.value?.engineId &&
+ store.state.engineManifests[audioItem.value?.engineId]
+ .supportedFeatures) as
+ | EngineManifest["supportedFeatures"]
+ | undefined
+ );
+
const applyPreset = () => {
store.dispatch("COMMAND_APPLY_AUDIO_PRESET", {
audioKey: props.activeAudioKey,
@@ -383,7 +417,8 @@ export default defineComponent({
const speedScaleSlider = previewSliderHelper({
modelValue: () => query.value?.speedScale ?? null,
- disable: () => uiLocked.value,
+ disable: () =>
+ uiLocked.value || supportedFeatures.value?.adjustSpeedScale === false,
onChange: setAudioSpeedScale,
max: () => 2,
min: () => 0.5,
@@ -393,7 +428,8 @@ export default defineComponent({
});
const pitchScaleSlider = previewSliderHelper({
modelValue: () => query.value?.pitchScale ?? null,
- disable: () => uiLocked.value,
+ disable: () =>
+ uiLocked.value || supportedFeatures.value?.adjustPitchScale === false,
onChange: setAudioPitchScale,
max: () => 0.15,
min: () => -0.15,
@@ -402,7 +438,9 @@ export default defineComponent({
});
const intonationScaleSlider = previewSliderHelper({
modelValue: () => query.value?.intonationScale ?? null,
- disable: () => uiLocked.value,
+ disable: () =>
+ uiLocked.value ||
+ supportedFeatures.value?.adjustIntonationScale === false,
onChange: setAudioIntonationScale,
max: () => 2,
min: () => 0,
@@ -412,7 +450,8 @@ export default defineComponent({
});
const volumeScaleSlider = previewSliderHelper({
modelValue: () => query.value?.volumeScale ?? null,
- disable: () => uiLocked.value,
+ disable: () =>
+ uiLocked.value || supportedFeatures.value?.adjustVolumeScale === false,
onChange: setAudioVolumeScale,
max: () => 2,
min: () => 0,
diff --git a/src/store/setting.ts b/src/store/setting.ts
index 71e2710691..0ab9fb25af 100644
--- a/src/store/setting.ts
+++ b/src/store/setting.ts
@@ -39,6 +39,7 @@ export const settingStoreState: SettingStoreState = {
toolbarSetting: [],
engineIds: [],
engineInfos: {},
+ engineManifests: {},
themeSetting: {
currentTheme: "Default",
availableThemes: [],
diff --git a/src/store/type.ts b/src/store/type.ts
index 5327488d7a..8a21cfbf60 100644
--- a/src/store/type.ts
+++ b/src/store/type.ts
@@ -6,7 +6,12 @@ import {
StoreOptions,
} from "./vuex";
import { Patch } from "immer";
-import { AccentPhrase, AudioQuery, UserDictWord } from "@/openapi";
+import {
+ AccentPhrase,
+ AudioQuery,
+ EngineManifest,
+ UserDictWord,
+} from "@/openapi";
import { createCommandMutationTree, PayloadRecipeTree } from "./command";
import {
CharacterInfo,
@@ -843,6 +848,7 @@ export type SettingStoreState = {
toolbarSetting: ToolbarSetting;
engineIds: string[];
engineInfos: Record;
+ engineManifests: Record;
themeSetting: ThemeSetting;
acceptRetrieveTelemetry: AcceptRetrieveTelemetryStatus;
experimentalSetting: ExperimentalSetting;
@@ -1042,6 +1048,12 @@ type UiStoreTypes = {
SET_ENGINE_INFOS: { mutation: { engineInfos: EngineInfo[] } };
+ SET_ENGINE_MANIFESTS: {
+ mutation: { engineManifests: Record };
+ };
+
+ FETCH_AND_SET_ENGINE_MANIFESTS: { action(): void };
+
SET_INHERIT_AUDIOINFO: {
mutation: { inheritAudioInfo: boolean };
action(payload: { inheritAudioInfo: boolean }): void;
diff --git a/src/store/ui.ts b/src/store/ui.ts
index b0a4b107e4..da23755fc3 100644
--- a/src/store/ui.ts
+++ b/src/store/ui.ts
@@ -10,6 +10,7 @@ import {
VoiceVoxStoreOptions,
} from "./type";
import { ActivePointScrollMode, EngineInfo } from "@/type/preload";
+import { EngineManifest } from "@/openapi";
export function createUILockAction(
action: (
@@ -147,6 +148,12 @@ export const uiStore: VoiceVoxStoreOptions =
engineInfos.map((engineInfo) => [engineInfo.uuid, "STARTING"])
);
},
+ SET_ENGINE_MANIFESTS(
+ state,
+ { engineManifests }: { engineManifests: Record }
+ ) {
+ state.engineManifests = engineManifests;
+ },
SET_INHERIT_AUDIOINFO(
state,
{ inheritAudioInfo }: { inheritAudioInfo: boolean }
@@ -387,6 +394,25 @@ export const uiStore: VoiceVoxStoreOptions =
engineInfos: await window.electron.engineInfos(),
});
},
+ async FETCH_AND_SET_ENGINE_MANIFESTS({ state, commit }) {
+ commit("SET_ENGINE_MANIFESTS", {
+ engineManifests: Object.fromEntries(
+ await Promise.all(
+ state.engineIds.map(
+ async (engineId) =>
+ await this.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
+ engineId,
+ }).then(async (instance) => [
+ engineId,
+ await instance.invoke("engineManifestEngineManifestGet")(
+ {}
+ ),
+ ])
+ )
+ )
+ ),
+ });
+ },
async SET_INHERIT_AUDIOINFO(
{ commit },
{ inheritAudioInfo }: { inheritAudioInfo: boolean }
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 4b09148f41..d2886d387f 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -469,6 +469,7 @@ export default defineComponent({
const isCompletedInitialStartup = ref(false);
onMounted(async () => {
await store.dispatch("GET_ENGINE_INFOS");
+ await store.dispatch("FETCH_AND_SET_ENGINE_MANIFESTS");
await store.dispatch("START_WAITING_ENGINE_ALL");
await store.dispatch("LOAD_CHARACTER_ALL");
diff --git a/tests/unit/store/Vuex.spec.ts b/tests/unit/store/Vuex.spec.ts
index d37e5fc47b..6b5417274f 100644
--- a/tests/unit/store/Vuex.spec.ts
+++ b/tests/unit/store/Vuex.spec.ts
@@ -81,6 +81,28 @@ describe("store/vuex.js test", () => {
host: "http://127.0.0.1",
},
},
+ engineManifests: {
+ "88022f86-c823-436e-85a3-500c629749c4": {
+ manifestVersion: "0.13.0",
+ name: "DUMMY VOICEVOX ENGINE",
+ uuid: "c7b58856-bd56-4aa1-afb7-b8415f824b06",
+ url: "https://github.com/VOICEVOX/voicevox_engine",
+ icon: "engine_manifest_assets/icon.png",
+ defaultSamplingRate: 24000,
+ termsOfService: "engine_manifest_assets/terms_of_service.md",
+ updateInfos: [],
+ dependencyLicenses: [],
+ supportedFeatures: {
+ adjustMoraPitch: true,
+ adjustPhonemeLength: true,
+ adjustSpeedScale: true,
+ adjustPitchScale: true,
+ adjustIntonationScale: true,
+ adjustVolumeScale: true,
+ interrogativeUpspeak: true,
+ },
+ },
+ },
experimentalSetting: {
enablePreset: false,
enableInterrogativeUpspeak: false,