Skip to content

Commit c3c5667

Browse files
committed
Add: キャッシュを削除して再生成するボタンを追加し、読み・アクセントをリセットするボタンを AudioCell のコンテキストメニューから再生ボタン上に移動
これでかなりわかりやすくなったはず…!デザイン的にもかなりしっくりきてる
1 parent 4bb906e commit c3c5667

File tree

5 files changed

+137
-27
lines changed

5 files changed

+137
-27
lines changed

src/components/Talk/AudioCell.vue

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,6 @@ const contextMenudata = ref<
552552
MenuItemSeparator,
553553
MenuItemButton,
554554
MenuItemButton,
555-
MenuItemButton,
556555
]
557556
>([
558557
// NOTE: audioTextBuffer.value の変更が nativeEl.value に反映されるのはnextTick。
@@ -622,17 +621,6 @@ const contextMenudata = ref<
622621
},
623622
disableWhenUiLocked: true,
624623
},
625-
{
626-
type: "button",
627-
label: "読み/アクセントをリセット",
628-
onClick: async () => {
629-
contextMenu.value?.hide();
630-
void store.actions.COMMAND_RESET_READING_AND_ACCENT({
631-
audioKey: props.audioKey,
632-
});
633-
},
634-
disableWhenUiLocked: true,
635-
},
636624
// { type: "separator" },
637625
// {
638626
// type: "button",

src/components/Talk/AudioDetail.vue

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,34 @@
2222
</QTabs>
2323
</div>
2424
<div class="play-button-wrapper">
25+
<div class="sub-button-row">
26+
<QBtn
27+
round
28+
flat
29+
size="sm"
30+
color="primary"
31+
icon="sym_r_casino"
32+
:disable="nowGenerating || uiLocked"
33+
@click="playWithClearCache"
34+
>
35+
<QTooltip anchor="top middle" :offset="[0, 30]" :delay="150">
36+
音声合成結果を再生成して再生します(結果がわずかに変わることがあります)
37+
</QTooltip>
38+
</QBtn>
39+
<QBtn
40+
round
41+
flat
42+
size="sm"
43+
color="primary"
44+
icon="sym_r_restart_alt"
45+
:disable="nowGenerating || uiLocked"
46+
@click="resetReadingAndAccent"
47+
>
48+
<QTooltip anchor="top middle" :offset="[0, 30]" :delay="150">
49+
単語の読みとアクセントをデフォルトに戻します
50+
</QTooltip>
51+
</QBtn>
52+
</div>
2553
<QBtn
2654
v-if="!nowPlaying && !nowGenerating"
2755
fab
@@ -244,7 +272,7 @@ watch(accentPhrases, async () => {
244272
scrollToActivePoint();
245273
});
246274
247-
// audio play
275+
// 音声の再生
248276
const play = async () => {
249277
try {
250278
await store.actions.PLAY_AUDIO({
@@ -269,6 +297,46 @@ const play = async () => {
269297
}
270298
};
271299
300+
// 音声の再生成と再生
301+
const playWithClearCache = async () => {
302+
try {
303+
await store.actions.PLAY_AUDIO_WITH_CLEAR_CACHE({
304+
audioKey: props.activeAudioKey,
305+
});
306+
} catch (e) {
307+
const msg = handlePossiblyNotMorphableError(e);
308+
// AivisSpeech Engine から音声合成エラーが返された
309+
if (e instanceof Error && e.message === "Response returned an error code") {
310+
void store.actions.SHOW_ALERT_DIALOG({
311+
title: "音声合成に失敗しました",
312+
message:
313+
msg ??
314+
"現在のテキストや読み方では音声合成できない可能性があります。\nテキストや読み方を変更して再度お試しください。",
315+
});
316+
} else {
317+
void store.actions.SHOW_ALERT_DIALOG({
318+
title: "再生に失敗しました",
319+
message: msg ?? "音声合成エンジンの再起動をお試しください。",
320+
});
321+
}
322+
}
323+
};
324+
325+
// 読み上げとアクセントをリセット
326+
const resetReadingAndAccent = async () => {
327+
try {
328+
await store.actions.COMMAND_RESET_READING_AND_ACCENT({
329+
audioKey: props.activeAudioKey,
330+
});
331+
} catch (e) {
332+
window.backend.logError(e);
333+
void store.actions.SHOW_ALERT_DIALOG({
334+
title: "読み・アクセントのリセットに失敗しました",
335+
message: "想定外のエラーが発生しました。",
336+
});
337+
}
338+
};
339+
272340
const stop = () => {
273341
void store.actions.STOP_AUDIO();
274342
};
@@ -411,12 +479,18 @@ const isAltKeyDown = useAltKey();
411479
.play-button-wrapper {
412480
align-self: center;
413481
display: flex;
414-
align-items: flex-end;
482+
align-items: center;
415483
flex-wrap: nowrap;
416-
flex-direction: row-reverse;
417-
justify-content: space-between;
418-
margin: 10px;
419-
gap: 0 5px;
484+
flex-direction: column;
485+
justify-content: center;
486+
padding-bottom: 8px;
487+
gap: 2px;
488+
489+
.sub-button-row {
490+
display: flex;
491+
flex-direction: row;
492+
justify-content: center;
493+
}
420494
}
421495
}
422496

src/store/audio.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { determineNextPresetKey } from "./preset";
3030
import {
3131
fetchAudioFromAudioItem,
3232
generateLabFromAudioQuery,
33+
generateUniqueIdAndQuery,
34+
clearAudioBlobCache,
3335
handlePossiblyNotMorphableError,
3436
isMorphable,
3537
} from "./audioGenerate";
@@ -1788,7 +1790,7 @@ export const audioStore = createPartialStore<AudioStoreTypes>({
17881790
async ({ mutations, actions }, { audioKey }: { audioKey: AudioKey }) => {
17891791
await actions.STOP_AUDIO();
17901792

1791-
// 音声用意
1793+
// 音声の用意
17921794
let fetchAudioResult: FetchAudioResult;
17931795
mutations.SET_AUDIO_NOW_GENERATING({
17941796
audioKey,
@@ -1815,6 +1817,28 @@ export const audioStore = createPartialStore<AudioStoreTypes>({
18151817
),
18161818
},
18171819

1820+
PLAY_AUDIO_WITH_CLEAR_CACHE: {
1821+
action: createUILockAction(
1822+
async ({ actions, state }, { audioKey }: { audioKey: AudioKey }) => {
1823+
1824+
// audioKey に対応する AudioItem を取得
1825+
const audioItem = state.audioItems[audioKey];
1826+
if (!audioItem) {
1827+
throw new Error(`AudioItem not found for key: ${audioKey}`);
1828+
}
1829+
1830+
// AudioItem に対応するキャッシュ ID を取得し、キャッシュをクリア
1831+
// キャッシュが存在しない場合は何も行われない
1832+
const [cacheId] = await generateUniqueIdAndQuery(state, audioItem);
1833+
clearAudioBlobCache(cacheId);
1834+
1835+
// 音声の用意 (キャッシュがクリアされているので必ず再生成される)
1836+
// 以降の処理は PLAY_AUDIO と同じ
1837+
return actions.PLAY_AUDIO({ audioKey });
1838+
},
1839+
),
1840+
},
1841+
18181842
PLAY_AUDIO_BLOB: {
18191843
action: createUILockAction(
18201844
async (

src/store/audioGenerate.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,30 +285,50 @@ export async function generateLabFromAudioQuery(
285285
return labString;
286286
}
287287

288-
async function generateUniqueIdAndQuery(
288+
/**
289+
* AudioItem からキャッシュ ID と AudioQuery を生成する。
290+
* @param state 設定ストアの状態
291+
* @param audioItem 対象の AudioItem
292+
* @returns [キャッシュID, AudioQuery | undefined]
293+
*/
294+
export async function generateUniqueIdAndQuery(
289295
state: SettingStoreState,
290296
audioItem: AudioItem,
291297
): Promise<[string, EditorAudioQuery | undefined]> {
292-
audioItem = JSON.parse(JSON.stringify(audioItem)) as AudioItem;
293-
const audioQuery = audioItem.query;
298+
// 影響を与えないようにディープコピー
299+
const copiedAudioItem = JSON.parse(JSON.stringify(audioItem)) as AudioItem;
300+
const audioQuery = copiedAudioItem.query;
294301
if (audioQuery != undefined) {
295302
audioQuery.outputSamplingRate =
296-
state.engineSettings[audioItem.voice.engineId].outputSamplingRate;
303+
state.engineSettings[copiedAudioItem.voice.engineId].outputSamplingRate;
297304
audioQuery.outputStereo = state.savingSetting.outputStereo;
298305
// AivisSpeech では音声合成時に AudioQuery.kana に読み上げテキストを指定する必要がある
299-
audioQuery.kana = audioItem.text;
306+
audioQuery.kana = copiedAudioItem.text;
300307
}
301308

302309
const id = await generateTempUniqueId([
303-
audioItem.text,
310+
copiedAudioItem.text,
304311
audioQuery,
305-
audioItem.voice,
306-
audioItem.morphingInfo,
312+
copiedAudioItem.voice,
313+
copiedAudioItem.morphingInfo,
307314
state.experimentalSetting.enableInterrogativeUpspeak, // このフラグが違うと、同じAudioQueryで違う音声が生成されるので追加
308315
]);
309316
return [id, audioQuery];
310317
}
311318

319+
/**
320+
* 指定されたキャッシュ ID に対応するキャッシュを削除する。
321+
* @param id 削除するキャッシュのID
322+
*/
323+
export function clearAudioBlobCache(id: string): void {
324+
if (Object.prototype.hasOwnProperty.call(audioBlobCache, id)) {
325+
delete audioBlobCache[id];
326+
log.info(`Cache cleared for id: ${id}`);
327+
} else {
328+
log.info(`Cache not found for id: ${id}, no need to clear.`);
329+
}
330+
}
331+
312332
export function isMorphable(
313333
state: AudioStoreState,
314334
{

src/store/type.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,10 @@ export type AudioStoreTypes = {
469469
action(payload: { audioKey: AudioKey }): boolean;
470470
};
471471

472+
PLAY_AUDIO_WITH_CLEAR_CACHE: {
473+
action(payload: { audioKey: AudioKey }): boolean;
474+
};
475+
472476
PLAY_AUDIO_BLOB: {
473477
action(payload: { audioBlob: Blob; audioKey?: AudioKey }): boolean;
474478
};

0 commit comments

Comments
 (0)