58 lines
1.4 KiB
TypeScript
58 lines
1.4 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, '').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;
|
|
};
|