53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
import { useSyncExternalStore } from "react";
|
|
import { speechVoiceIdSchema, type SpeechVoice } from "@/shared/tts/voices";
|
|
|
|
export const SPEECH_VOICE_STORAGE_KEY = "tjwater.agent.speech-voice";
|
|
const VOICE_CHANGE_EVENT = "tjwater:speech-voice-change";
|
|
let memoryVoice = "";
|
|
let useMemoryPreference = false;
|
|
|
|
export function getSelectedSpeechVoice(): string {
|
|
let value: string;
|
|
try { value = useMemoryPreference ? memoryVoice : localStorage.getItem(SPEECH_VOICE_STORAGE_KEY) ?? ""; }
|
|
catch { value = memoryVoice; }
|
|
return speechVoiceIdSchema.safeParse(value).success ? value : "";
|
|
}
|
|
|
|
export function setSelectedSpeechVoice(value: string) {
|
|
const voice = speechVoiceIdSchema.safeParse(value).success ? value : "";
|
|
memoryVoice = voice;
|
|
try {
|
|
if (voice) localStorage.setItem(SPEECH_VOICE_STORAGE_KEY, voice);
|
|
else localStorage.removeItem(SPEECH_VOICE_STORAGE_KEY);
|
|
useMemoryPreference = false;
|
|
} catch { useMemoryPreference = true; }
|
|
window.dispatchEvent(new Event(VOICE_CHANGE_EVENT));
|
|
}
|
|
|
|
function subscribe(onChange: () => void) {
|
|
window.addEventListener(VOICE_CHANGE_EVENT, onChange);
|
|
window.addEventListener("storage", onChange);
|
|
return () => {
|
|
window.removeEventListener(VOICE_CHANGE_EVENT, onChange);
|
|
window.removeEventListener("storage", onChange);
|
|
};
|
|
}
|
|
|
|
export function useSelectedSpeechVoice() {
|
|
return useSyncExternalStore(subscribe, getSelectedSpeechVoice, () => "");
|
|
}
|
|
|
|
const chineseNames: Record<string, string> = {
|
|
"zh-CN-XiaoxiaoNeural": "晓晓", "zh-CN-XiaoyiNeural": "晓伊",
|
|
"zh-CN-YunxiNeural": "云希", "zh-CN-YunyangNeural": "云扬",
|
|
"zh-CN-YunjianNeural": "云健", "zh-CN-YunxiaNeural": "云夏"
|
|
};
|
|
|
|
export function getSpeechVoiceName(voice: SpeechVoice) {
|
|
return chineseNames[voice.id] ?? voice.id.replace(`${voice.locale}-`, "").replace(/Neural$/, "");
|
|
}
|
|
|
|
export function getSpeechVoiceDescription(voice: SpeechVoice) {
|
|
return `普通话 · ${voice.gender === "Female" ? "女声" : "男声"}`;
|
|
}
|