The upstream Chatterbox README documents paralinguistic tags ([laugh],
[chuckle], [sigh], ...) as native to the Turbo family, and the vendored
Turbo pack passes them straight to the tokenizer -- it never parses them.
Our engine deleted them before the model saw them, so the feature looked
implemented and was inaudible.
* the text engine keeps a Turbo tag verbatim and records its name; on any
other pack the nine Turbo names stay literal text instead of being
swallowed as a voice name, and the run says so
* the v2 angle tokens are re-spelled for Turbo (<giggle> -> [giggle]) and
left untouched everywhere else
* generate_audio now builds one plan for every family, synthesises one
chunk per model call and joins the chunks; the classic path goes through
the engine too, which is what makes "[Alice]" open a character segment
* per-chunk seeds (seed + index) keep a chunked run reproducible without
giving every chunk the same prosody
* four appended widgets on ChatterboxTTS: enable_chunking,
max_chars_per_chunk, chunk_combination_method, silence_between_chunks_ms
* chunk boundaries prefer a sentence end and never land inside a tag
* Turbo's exaggeration is passed as 0.0: the family ignores it for
sampling and upstream warns about every non-zero value, once per chunk
Every entry point that conditions on a reference clip now takes the
waveform in memory: the vendored loaders gained load_audio_at (path,
ndarray or tensor -> float32 mono at the target rate, soundfile +
torchaudio, no librosa), and the resamples ride the new dsp module. The
node hands over preprocess_comfy_audio output at the right rate per
site (S3Gen 24k for conditioning, the tokenizer's 16k for the VC
source), so temp_wav_path and its temp-file machinery are gone -- which
also removes the 16-bit PCM quantization the file round-trip imposed.
Resampling is the one deliberate numerical difference: torchaudio's
sinc kernel instead of soxr, touching only clips supplied at call time.
The built-in-voice shared-assets E2E stays byte-identical
(md5 370acf3f9469d55759c3d6096b2628ff); the reference-clip E2E changes
measurably in the last digits, as expected and reported.
The training-baked featurization (three Slaney mel filterbanks, the
voice encoder's STFT, the periodic Hann behind the vocoder's ISTFT
convergence) is reimplemented operation-for-operation in a new vendored
module, src/chatterbox/models/dsp.py (ISC attribution retained), so
librosa and scipy leave the vendored tree's import list. Bit-exactness
against librosa 1.0.0 on these exact parameters was proven once by the
probe; tests/test_dsp.py pins what must not drift afterwards.
- ve/s3gen/s3gen_v3 safetensors + grapheme tokenizer are byte-identical
across every repo that ships them (sha256-verified): fetched at most
once into a reserved _shared store, hardlinked into each pack
- downloads carry pack-unique files only (allow_patterns); legacy
full-repo folders satisfy the store before anything is fetched
- vendored MTL loads ve/s3gen from either serialization, safetensors
first; upstream's strict load refused every SLP pack (P-036)
- .pt twins (s3gen.pt, s3gen_v3.pt, t3_cfg.pt, ve.pt) are never fetched
Upstream 0.1.7 renamed S3Token2Wav.inference's kwarg to
n_cfm_timesteps; the B2 patch kept the 0.1.2 spelling, so
every real VC run raised TypeError. Found by the E2E smoke
on real weights (6/7 passed; TTS path green).
Vendored pack classes are not nn.Modules; the dtype cast
crashed on named_modules. Walk their Module attributes, and
delegate into each child's own tree (_modules is invisible
to __dict__ iteration).
Vendored tree mocked at collection, so the suite runs
green without perth, diffusers, s3tokenizer or conformer.
Frozen widget snapshot pins node ids and widget order.
Vendored 0.1.2 was stale: no mtl_tts, no turbo. 30 local
patches recorded in src/PATCHES.md; diffusers dependency
replaced by matcha/_diffusers_compat.py.