Skip to content
Draft

Guide #2849

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openapi.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/components/Dialog/AllDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<ImportSongProjectDialog v-model="isImportSongProjectDialogOpenComputed" />
<PresetManageDialog v-model:dialogOpened="isPresetManageDialogOpenComputed" />
<HelpDialog v-model:dialogOpened="isHelpDialogOpenComputed" />
<GuideDialog v-model:dialogOpened="isGuideDialogOpenComputed" />
</template>

<script setup lang="ts">
Expand All @@ -55,6 +56,7 @@ import ImportSongProjectDialog from "@/components/Dialog/ImportSongProjectDialog
import ExportSongAudioDialog from "@/components/Dialog/ExportSongAudioDialog/Container.vue";
import PresetManageDialog from "@/components/Dialog/PresetManageDialog.vue";
import HelpDialog from "@/components/Dialog/HelpDialog/HelpDialog.vue";
import GuideDialog from "@/components/Dialog/GuideDialog.vue";
import { useStore } from "@/store";
import { filterCharacterInfosByStyleType } from "@/store/utility";

Expand Down Expand Up @@ -213,4 +215,12 @@ const isHelpDialogOpenComputed = computed({
isHelpDialogOpen: val,
}),
});

const isGuideDialogOpenComputed = computed({
get: () => store.state.isGuideDialogOpen,
set: (val) =>
store.actions.SET_DIALOG_OPEN({
isGuideDialogOpen: val,
}),
});
</script>
1 change: 1 addition & 0 deletions src/components/Dialog/EngineManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ const getFeatureName = (name: keyof SupportedFeatures) => {
interrogativeUpspeak: "疑問文の自動調整",
synthesisMorphing: "2種類のスタイルでモーフィングした音声を合成",
sing: "歌唱音声合成",
guide: "音声認識による自動調整ガイド機能",
manageLibrary: "音声ライブラリのインストール・アンインストール",
returnResourceUrl: "キャラクター情報のリソースをURLで返送",
applyKatakanaEnglish: "未知の英単語をカタカナ読みに変換",
Expand Down
189 changes: 189 additions & 0 deletions src/components/Dialog/GuideDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<template>
<QDialog v-model="dialogOpened">
<QCard class="q-pa-md">
<div class="guide-title">Guide</div>
<div class="audio-selection-container">
<QBtn
color="primary"
flat
dense
icon="play_arrow"
:disabled="audioFileBlob == null"
@click="playSelectedAudio"
/>
<QInput
v-model="audioDisplayName"
label="Select Audio"
dense
readonly
/>
<QBtn flat icon="folder_open" dense @click="pickAudioFile" />
</div>
<div class="q-my-xs" />
<QExpansionItem
v-model="advancedSettingsExpanded"
dense
label="Advanced Settings"
>
<QList dense>
<QItem>
<QToggle
v-model="advancedSettings.normalize"
label="Normalize Pitch"
dense
/>
</QItem>
<QItem>
<QToggle
v-model="advancedSettings.assignLength"
label="Apply Duration"
dense
/>
</QItem>
<QItem>
<QToggle
v-model="advancedSettings.assignPitch"
label="Apply Pitch"
dense
/>
</QItem>
<QItem>
<QToggle
v-model="advancedSettings.trimAudio"
label="Trim Audio"
dense
/>
</QItem>
</QList>
</QExpansionItem>
<div style="display: flex">
<QSpace />
<QBtn flat dense label="Confirm" @click="guide" />
</div>
</QCard>
</QDialog>
</template>

<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { useStore } from "@/store";

const store = useStore();

const dialogOpened = defineModel<boolean>("dialogOpened");

const audioFilePath = ref<string | null>(null);
const audioFileBlob = ref<Blob | null>(null);

const advancedSettingsExpanded = ref(false);

const advancedSettings = ref({
trim: false,
normalize: true,
assignLength: true,
assignPitch: true,
trimAudio: true,
});

const audioDisplayName = computed(() => {
if (audioFilePath.value == null) {
return "";
} else {
return `Selected: ${audioFilePath.value.split("/").pop()} `;
}
});

const pickAudioFile = async () => {
const audioPath = await window.backend.showOpenFileDialog({
title: "Select Audio File",
name: "",
mimeType: "audio/*",
extensions: ["*"],
});
if (audioPath == undefined) {
return;
}
try {
// Result<Uint8Array<ArrayBufferLike>>
const audioArray = await window.backend.readFile({ filePath: audioPath });
if (audioArray.ok) {
// dirty cast to satisfy TS
audioFileBlob.value = new Blob([audioArray.value as unknown as BlobPart]);
} else {
console.error("Failed to read audio file:", audioArray.error);
}
} catch (error) {
console.error("Error reading audio file:", error);
}
audioFilePath.value = audioPath;
};

const guide = () => {
const audioKey = store.getters.ACTIVE_AUDIO_KEY;
if (audioKey == undefined || audioFileBlob.value == null) {
return;
}
const engineId = store.state.audioItems[audioKey].voice.engineId;
store.actions
.GUIDE_AUDIO_QUERY({
engineId: engineId,
audioKey: audioKey,
refAudio: audioFileBlob.value,
...advancedSettings.value,
})
.catch((error) => {
console.error("Error guiding audio query:", error);
});
dialogOpened.value = false;
};

const audioPlaying = ref(false);

const playSelectedAudio = () => {
if (audioFileBlob.value == null) {
return;
}
audioPlaying.value = true;
store.actions
.PLAY_AUDIO_BLOB({
audioBlob: audioFileBlob.value,
})
.catch((error) => {
console.error("Error playing audio blob:", error);
})
.then(() => {
audioPlaying.value = false;
})
.catch(() => {
audioPlaying.value = false;
});
};

// stop audio when dialog is closed
watch(
[dialogOpened, audioFilePath],
async ([dialogOpenedValue, newAudioFilePath]) => {
if (
(!dialogOpenedValue && audioPlaying.value) ||
newAudioFilePath != null
) {
await store.actions.STOP_AUDIO();
audioPlaying.value = false;
}
},
);
</script>

<style scoped lang="scss">
.guide-title {
font-size: 1.5rem;
font-weight: bold;
}
.audio-selection-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 8px;
}
</style>
1 change: 1 addition & 0 deletions src/components/Dialog/ToolBarCustomDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ const usableButtonsDesc: Record<ToolbarButtonTagType, string> = {
UNDO: "操作を一つ戻します。",
REDO: "元に戻した操作をやり直します。",
IMPORT_TEXT: "テキストファイル(.txt)を読み込みます。",
GUIDE: "音声ガイド機能を使用します。",
EMPTY:
"これはボタンではありません。レイアウトの調整に使います。また、実際には表示されません。",
};
Expand Down
3 changes: 1 addition & 2 deletions src/components/Talk/AccentPhrase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@
import { computed, ref } from "vue";
import AudioAccent from "./AudioAccent.vue";
import AudioParameter from "./AudioParameter.vue";
import type { DetailTypes } from "./AudioDetail.vue";
import { MenuItemButton } from "@/components/Menu/type";
import ContextMenu from "@/components/Menu/ContextMenu/Container.vue";
import { useStore } from "@/store";
Expand Down Expand Up @@ -237,8 +238,6 @@ defineExpose({
container,
});

type DetailTypes = "accent" | "pitch" | "length";

const store = useStore();

const uiLocked = computed(() => store.getters.UI_LOCKED);
Expand Down
11 changes: 10 additions & 1 deletion src/components/Talk/AudioDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ registerHotkeyWithCleanup({
});

// detail selector
type DetailTypes = "accent" | "pitch" | "length";
export type DetailTypes = "accent" | "pitch" | "length";
const selectedDetail = ref<DetailTypes>("accent");

// accent phrase
Expand Down Expand Up @@ -407,4 +407,13 @@ const isAltKeyDown = useAltKey();
overflow-x: scroll;
}
}

.action-buttons-wrapper {
flex-grow: 1;
border: 1px solid red;
padding: 0.5rem;
display: flex;
justify-content: end;
align-items: end;
}
</style>
7 changes: 7 additions & 0 deletions src/components/Talk/ToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ const saveProject = async () => {
const importTextFile = () => {
void store.actions.COMMAND_IMPORT_FROM_FILE({ type: "dialog" });
};
const openGuideDoalog = () => {
void store.actions.SET_DIALOG_OPEN({ isGuideDialogOpen: true });
};

const usableButtons: Record<
ToolbarButtonTagType,
Expand Down Expand Up @@ -157,6 +160,10 @@ const usableButtons: Record<
click: importTextFile,
disable: uiLocked,
},
GUIDE: {
click: openGuideDoalog,
disable: uiLocked,
},
EMPTY: null,
};

Expand Down
Loading
Loading