tts: punctuate unbounded chunks, strip URLs and paths, log chunk text
This commit is contained in:
parent
29c400bdb4
commit
075db9154a
1 changed files with 31 additions and 8 deletions
|
|
@ -73,28 +73,49 @@ const ttsCache = new Map();
|
|||
const TTS_CHUNK_MAX = 2500; // upper bound per chunk (server-side comfortable)
|
||||
const TTS_FIRST_CHUNK_MAX = 500; // smaller first chunk for low time-to-first-audio
|
||||
|
||||
// Strip patterns F5 would pronounce character-by-character (URLs, paths, emails).
|
||||
// F5 has no spell-vs-speak heuristic — feeding it https://foo.bar produces audible
|
||||
// garbage that users hear as "random chars trying to be pronounced".
|
||||
function stripUnspeakable(text) {
|
||||
return text
|
||||
.replace(/https?:\/\/\S+/gi, "")
|
||||
.replace(/\b[\w-]+@[\w.-]+\.\w+\b/g, "")
|
||||
.replace(/\/[\w./_-]{4,}/g, "")
|
||||
.replace(/[A-Z]:\\[\w\\._-]+/g, "")
|
||||
.replace(/[ \t]+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
// Split one chunk off the head of `remaining`. Always ends the chunk on
|
||||
// sentence punctuation when possible — F5-TTS hallucinates noise at the tail
|
||||
// of unpunctuated fragments since it doesn't know where to stop. If no
|
||||
// boundary exists in `limit` chars, split on a space and append a period so
|
||||
// F5 has a clean stop signal.
|
||||
function splitOne(remaining, limit) {
|
||||
if (remaining.length <= limit) return [remaining, ''];
|
||||
const slice = remaining.substring(0, limit);
|
||||
let splitAt = -1;
|
||||
for (let i = slice.length - 1; i > limit * 0.3; i--) {
|
||||
for (let i = slice.length - 1; i > 0; i--) {
|
||||
const ch = slice[i];
|
||||
if ((ch === '.' || ch === '!' || ch === '?' || ch === '\n') &&
|
||||
(i === slice.length - 1 || slice[i + 1] === ' ' || slice[i + 1] === '\n')) {
|
||||
splitAt = i + 1;
|
||||
break;
|
||||
const splitAt = i + 1;
|
||||
return [remaining.substring(0, splitAt).trim(), remaining.substring(splitAt).trim()];
|
||||
}
|
||||
}
|
||||
if (splitAt === -1) splitAt = slice.lastIndexOf(' ');
|
||||
let splitAt = slice.lastIndexOf(' ');
|
||||
if (splitAt <= 0) splitAt = limit;
|
||||
return [remaining.substring(0, splitAt).trim(), remaining.substring(splitAt).trim()];
|
||||
const head = remaining.substring(0, splitAt).trim();
|
||||
const tail = remaining.substring(splitAt).trim();
|
||||
const punctuatedHead = /[.!?]$/.test(head) ? head : head + ".";
|
||||
return [punctuatedHead, tail];
|
||||
}
|
||||
|
||||
function splitTextIntoChunks(text) {
|
||||
if (text.length <= TTS_FIRST_CHUNK_MAX) return [text];
|
||||
const cleaned = stripUnspeakable(text);
|
||||
if (cleaned.length <= TTS_FIRST_CHUNK_MAX) return [cleaned];
|
||||
|
||||
const chunks = [];
|
||||
let remaining = text;
|
||||
let remaining = cleaned;
|
||||
let isFirst = true;
|
||||
while (remaining.length > 0) {
|
||||
const limit = isFirst ? TTS_FIRST_CHUNK_MAX : TTS_CHUNK_MAX;
|
||||
|
|
@ -105,6 +126,8 @@ function splitTextIntoChunks(text) {
|
|||
}
|
||||
|
||||
console.log(`TTS: split ${text.length} chars into ${chunks.length} chunks:`, chunks.map((c, i) => `chunk${i + 1}=${c.length}chars`));
|
||||
// Diagnostic: log full text of each chunk so we can spot garbage being sent to F5.
|
||||
chunks.forEach((c, i) => console.log(`TTS chunk ${i + 1} text:`, JSON.stringify(c)));
|
||||
return chunks;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue