tts: change voice language at speak time when the book language is not accurate (#296)

This commit is contained in:
Huang Xin
2025-02-04 23:44:59 +01:00
committed by GitHub
parent 6ca96be917
commit 1c6cc2bca0
2 changed files with 22 additions and 4 deletions
@@ -8,6 +8,7 @@ export class EdgeTTSClient implements TTSClient {
#rate = 1.0;
#pitch = 1.0;
#voice: TTSVoice | null = null;
#currentVoiceLang = '';
#voices: TTSVoice[] = [];
#edgeTTS: EdgeSpeechTTS;
@@ -60,12 +61,17 @@ export class EdgeTTSClient implements TTSClient {
preload = false,
): AsyncGenerator<TTSMessageEvent> {
const { marks } = parseSSMLMarks(ssml);
const lang = parseSSMLLang(ssml) || 'en';
const ssmlLang = parseSSMLLang(ssml) || 'en';
let lang = ssmlLang;
if (lang === 'en' && /[\p{Script=Han}]/u.test(ssml)) {
lang = 'zh';
}
let voiceId = 'en-US-AriaNeural';
if (!this.#voice) {
if (!this.#voice || ssmlLang !== lang) {
const voices = await this.getVoices(lang);
this.#voice = voices[0] ? voices[0] : this.#voices.find((v) => v.id === voiceId) || null;
this.#currentVoiceLang = lang;
}
if (this.#voice) {
voiceId = this.#voice.id;
@@ -229,6 +235,9 @@ export class EdgeTTSClient implements TTSClient {
}
async getVoices(lang: string): Promise<TTSVoice[]> {
if (this.#currentVoiceLang) {
lang = this.#currentVoiceLang;
}
const locale = lang === 'en' ? getUserLocale(lang) || lang : lang;
const voices = await this.getAllVoices();
return voices.filter((v) => v.lang.startsWith(locale));
@@ -175,6 +175,7 @@ export class WebSpeechClient implements TTSClient {
#rate = 1.0;
#pitch = 1.0;
#voice: SpeechSynthesisVoice | null = null;
#currentVoiceLang = '';
#voices: SpeechSynthesisVoice[] = [];
#synth = window.speechSynthesis;
available = true;
@@ -213,11 +214,16 @@ export class WebSpeechClient implements TTSClient {
// no need to preload for web speech
if (preload) return;
const lang = parseSSMLLang(ssml) || 'en';
if (!this.#voice) {
const ssmlLang = parseSSMLLang(ssml) || 'en';
let lang = ssmlLang;
if (lang === 'en' && /[\p{Script=Han}]/u.test(ssml)) {
lang = 'zh';
}
if (!this.#voice || ssmlLang !== lang) {
const voices = await this.getVoices(lang);
const voiceId = voices[0]?.id ?? '';
this.#voice = this.#voices.find((v) => v.voiceURI === voiceId) || null;
this.#currentVoiceLang = lang;
}
for await (const ev of speakWithMarks(
ssml,
@@ -286,6 +292,9 @@ export class WebSpeechClient implements TTSClient {
}
async getVoices(lang: string) {
if (this.#currentVoiceLang) {
lang = this.#currentVoiceLang;
}
const locale = lang === 'en' ? getUserLocale(lang) || lang : lang;
const isValidVoice = (id: string) => {
return !id.includes('com.apple') || id.includes('com.apple.voice.compact');