Files
readest/apps/readest-app/src/utils/ssml.ts
T
Huang Xin 00003a9415 Preloading audio for Edge TTS and other fixes (#138)
* Blacklist some low-quality web tts voices

* Dropdown layout tweaks

* Handle no audio data received in Edge TTS

* Preloading audio for Edge TTS
2025-01-10 14:11:09 +01:00

63 lines
1.5 KiB
TypeScript

type TTSMark = {
offset: number;
name: string;
text: string;
};
export const parseSSMLMarks = (ssml: string) => {
ssml = ssml.replace(/<speak[^>]*>/i, '');
ssml = ssml.replace(/<\/speak>/i, '');
const markRegex = /<mark\s+name="([^"]+)"\s*\/>/g;
let plainText = '';
const marks: TTSMark[] = [];
let match;
while ((match = markRegex.exec(ssml)) !== null) {
const markTagEndIndex = markRegex.lastIndex;
const nextMarkIndex = ssml.indexOf('<mark', markTagEndIndex);
const nextChunk = ssml.slice(
markTagEndIndex,
nextMarkIndex !== -1 ? nextMarkIndex : ssml.length,
);
const cleanedChunk = nextChunk
.replace(/<[^>]+>/g, '')
.replace(/\r\n/g, ' ')
.replace(/\r/g, ' ')
.replace(/\n/g, ' ')
.trimStart();
plainText += cleanedChunk;
const offset = plainText.length - cleanedChunk.length;
const markName = match[1]!;
marks.push({ offset, name: markName, text: cleanedChunk });
}
return { plainText, marks };
};
export const findSSMLMark = (charIndex: number, marks: TTSMark[]) => {
let left = 0;
let right = marks.length - 1;
let result: TTSMark | null = null;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const mark = marks[mid]!;
if (mark.offset <= charIndex) {
result = mark;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
};
export const parseSSMLLang = (ssml: string): string | null => {
const match = ssml.match(/xml:lang\s*=\s*"([^"]+)"/);
return match ? match[1]! : null;
};