You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When the text has a paragraph break (\n\n), the first sentence after it is cut between two words, and the cut is heard as a pause where none belongs. With ‖ marking the cut, the second paragraph below is spoken as: "The second paragraph begins with an ordinary ‖ sentence of modest length, and it carries on a little further so that the whole text passes fifty tokens."
Expected: a character the model has no token for costs a token or two and is otherwise harmless, and a paragraph break behaves like a space.
Steps to reproduce
Garbling:
fluidaudiocli tts --backend pocket --voice alba --seed 42 -o parens.wav --text "The control plane on the Pi (which also serves the dashboard) polls the shard every fifteen minutes."
Mid-phrase cut. Save as para.txt, keeping the blank line, then run the command:
First paragraph ends here.
The second paragraph begins with an ordinary sentence of modest length, and it carries on a little further so that the whole text passes fifty tokens.
Parentheses, slashes and paragraph breaks are routine in text written by an LLM, so in a voice assistant most replies are affected. The only workaround is to strip such characters and flatten newlines before synthesis.
Related
#592 (closed) reported garbling with cloned voices only; this reproduces with the shipped alba voice. #584 (closed) concerned smart apostrophes inflating token counts in the same chunker. #933 and #934 concern how sentences are cut once token counts are right; both assume this fix.
Notes — by Claude Code
Written by Claude Code, an AI assistant working with the reporter. Checked by controlled tests on b68f4847.
SentencePieceTokenizer encodes the whole input one token per character once it contains a single scalar with no vocabulary piece. The meeting (which ran late) ended at noon. gives 42 tokens. Google's sentencepiece Python package, loading the same tokenizer.model, gives 16. Without the parentheses both give 11.
Absent from the English vocabulary: ( ) [ ] { } … / # @ + = < > ~ | \ ^ ° € £ • → × ™, emoji, tab, newline, NBSP. The vocabulary does contain all 256 byte-fallback pieces (<0x00>to<0xFF>), which is how Google's sentencepiece` encodes these characters.
The mid-phrase cut is the same defect seen from the chunker. A newline is one of the characters with no vocabulary piece. chunkTextWithMetadata counts tokens before whitespace is collapsed, and splitSentences trims spaces but not newlines, so the sentence after a paragraph break is tokenized with \n\n still attached and comes out at one token per character. It then appears to overflow maxTokensPerChunk and is split at a word boundary. In a longer passage one such sentence measured 204 tokens where Google's sentencepiece gives 60.
A fix is in fix(tts/pocket): tokenize around unknown characters and size chunks on normalized text #932, with model-free unit tests: byte fallback for unknown scalars in viterbiDecode, and whitespace collapsed before counting. With the fix, token ids are identical to those from Google's sentencepiece on ten test lines that include parentheses, a slash, an emoji, an arrow and a bullet, and both symptoms are gone when listening.
Summary
Two audible defects, with no error or log output.
\n\n), the first sentence after it is cut between two words, and the cut is heard as a pause where none belongs. With‖marking the cut, the second paragraph below is spoken as: "The second paragraph begins with an ordinary ‖ sentence of modest length, and it carries on a little further so that the whole text passes fifty tokens."Expected: a character the model has no token for costs a token or two and is otherwise harmless, and a paragraph break behaves like a space.
Steps to reproduce
para.txt, keeping the blank line, then run the command:Environment
FluidAudio
main@b68f4847,FluidInference/pocket-tts-coremlv2.1 English. macOS 26.6.2, M4 Max.Why this matters
Parentheses, slashes and paragraph breaks are routine in text written by an LLM, so in a voice assistant most replies are affected. The only workaround is to strip such characters and flatten newlines before synthesis.
Related
#592 (closed) reported garbling with cloned voices only; this reproduces with the shipped
albavoice. #584 (closed) concerned smart apostrophes inflating token counts in the same chunker.#933 and #934 concern how sentences are cut once token counts are right; both assume this fix.
Notes — by Claude Code
Written by Claude Code, an AI assistant working with the reporter. Checked by controlled tests on
b68f4847.SentencePieceTokenizerencodes the whole input one token per character once it contains a single scalar with no vocabulary piece.The meeting (which ran late) ended at noon.gives 42 tokens. Google'ssentencepiecePython package, loading the sametokenizer.model, gives 16. Without the parentheses both give 11.( ) [ ] { } … / # @ + = < >~ | \ ^ ° € £ • → × ™, emoji, tab, newline, NBSP. The vocabulary does contain all 256 byte-fallback pieces (<0x00>to<0xFF>), which is how Google'ssentencepiece` encodes these characters.chunkTextWithMetadatacounts tokens before whitespace is collapsed, andsplitSentencestrims spaces but not newlines, so the sentence after a paragraph break is tokenized with\n\nstill attached and comes out at one token per character. It then appears to overflowmaxTokensPerChunkand is split at a word boundary. In a longer passage one such sentence measured 204 tokens where Google'ssentencepiecegives 60.viterbiDecode, and whitespace collapsed before counting. With the fix, token ids are identical to those from Google'ssentencepieceon ten test lines that include parentheses, a slash, an emoji, an arrow and a bullet, and both symptoms are gone when listening.