# voice-packs.env — tunables for the **frank-tts voice packs**: the three # numbers per voice (source file, crop offset, crop length) plus the pinned # engine facts that turn a crop into a pack. Read by # `scripts/build-voice-pack.sh`, which is the only thing that consumes this # file — the running service reads none of it, because the packs themselves are # the interface (see scripts/frank-tts.env for what the shim resolves). # # WHAT IS TRACKED HERE AND WHAT IS NOT. Paths and numbers, never credentials, # which is the same rule the other `scripts/*.env` files follow. The pack # DIRECTORIES are host state under $VOICE_PACK_DIR and stay out of the repo; the # crops that produce them are exactly the kind of decision worth a diff, so the # offsets live here. If a crop changes, change it here and rebuild with # --force — the manifest inside the pack records what was actually cut. # # THE OFFSETS ARE NOT ARBITRARY. Two rules pick them: # 1. The source-material invariant: seconds 0-10 of any sample file are # forbidden voice material. The builder refuses a crop that starts inside # that region and asserts the same thing again from the manifest it just # wrote, so the values below are checked, not trusted. # 2. Speech density: the trial cut these from a silencedetect scan of the # source (noise -35 dB, 0.6 s) and took the longest run of continuous # speech at or past the scan window — a reference that starts mid-word or # contains a page-turn of silence clones the artefacts, not the voice. # serverAppConfigs-docs/Dankclaw/Voice Packs.md carries the recipe. # # WHY THE SHA256 LINES EXIST. A crop offset is a statement about one exact # file: 98.739 s into a re-encode of the same audiobook is a different sentence # and therefore a different voice. The builder hashes the source, compares it # with the pinned digest when one is set, and writes the digest into the pack's # manifest — so a pack can always be traced back to the bytes it was cut from. # The three values below are the trial's digests (2026-10-18), verified against # the files on disk. # # THE ENGINE BLOCK IS A PIN, NOT A DEFAULT. The speaker artifacts come from # `qwen-codec` in the qwentts.cpp tree at $VOICE_PACK_ENGINE_COMMIT: it takes a # 24 kHz WAV, writes the RVQ code stream next to it with the extension swapped, # and with --talker also runs that talker's speaker encoder to write the # x-vector .spk. The talker has to be a **Base** build — the customvoice and # voicedesign GGUFs carry no speaker encoder and the extractor says so. The # sizes the builder then verifies (8,192 B of .spk, 5,456-6,864 B of .rvq) are # derived from the codec config in the GGUF, so an engine bump that changes them # fails the build on purpose; see [[Voice Packs]] for the bump procedure. # # HOST PATHS, NOT CONTAINER PATHS. This runs on the host that holds the engine # build (Dankclaw), outside any container, with no GPU and no fleet endpoint. # The model paths below are where scripts/download-frank-tts-engine-qwentts.sh # puts the GGUFs; until that fetch has run, the same two files sit in the trial # tree at /home/dank/voice-audition/qwentts/models/ and the tool is only in its # build directory. The SOURCE paths are the third thing to place: the three # sample files live on the workstation today and have to be copied to # $VOICE_PACK_SOURCE_DIR on Dankclaw before a build can read them. # The packs root on the host: the directory frank-tts mounts read-only at # /voices, one sub-directory per voice. VOICE_PACK_DIR=/home/dank/voices # The pack list. `--all` walks it in order; every name has to carry the four # per-voice values below. VOICE_PACKS="narrator1 narrator2 narrator3" # Where the source recordings live on the engine host. VOICE_PACK_SOURCE_DIR=/home/dank/voice-sources # narrator1 — Randall Munroe "How To", read by Wil Wheaton. Longest speech run # at or past the scan window: 98.739-118.636 s, 19.897 s, which packs to the # 5,456 B floor of the .rvq window. VOICE_PACK_NARRATOR1_SOURCE=${VOICE_PACK_SOURCE_DIR}/audiosample.mp3 VOICE_PACK_NARRATOR1_OFFSET=98.739 VOICE_PACK_NARRATOR1_DURATION=19.897 VOICE_PACK_NARRATOR1_SHA256=2299510467eb9143455d8c1e6a21601c3b451dd59c1dc99b99d4aacb54d1d850 # narrator2 — "Abyssal Archive" (Lokey). 25 s is the ceiling of the reference # window and packs to exactly 6,864 B of .rvq. VOICE_PACK_NARRATOR2_SOURCE=${VOICE_PACK_SOURCE_DIR}/audiosample2.m4b VOICE_PACK_NARRATOR2_OFFSET=191.13 VOICE_PACK_NARRATOR2_DURATION=25 VOICE_PACK_NARRATOR2_SHA256=8e1e33317baf67b2cca67e4ce1b095ae477a18285c7373ec18ad013f2cc320e0 # narrator3 — "Children of Time" (Adrian Tchaikovsky, author-read). VOICE_PACK_NARRATOR3_SOURCE=${VOICE_PACK_SOURCE_DIR}/audiosample3.m4b VOICE_PACK_NARRATOR3_OFFSET=489.232 VOICE_PACK_NARRATOR3_DURATION=25 VOICE_PACK_NARRATOR3_SHA256=6875d2c1db421ab96ccbb68855fa9efd4e054297e94dd1b4345eb6a9869da945 # The pinned extractor and the two GGUFs it needs. $VOICE_PACK_TOOL is a build # output, not a download: rebuild it at $VOICE_PACK_ENGINE_COMMIT after a bump # rather than pointing this line at a newer tree. VOICE_PACK_TOOL=/home/dank/voice-audition/qwentts/qwentts.cpp/build/qwen-codec VOICE_PACK_CODEC_MODEL=/home/dank/models/Qwen3-TTS-GGUF/qwen-tokenizer-12hz-Q8_0.gguf VOICE_PACK_TALKER_MODEL=/home/dank/models/Qwen3-TTS-GGUF/qwen-talker-1.7b-base-Q4_K_M.gguf VOICE_PACK_ENGINE_REPO=https://github.com/ServeurpersoCom/qwentts.cpp VOICE_PACK_ENGINE_COMMIT=5916bf7c79dbb4e66fc059cdc507a79a23a28bb4 # CPU only, and never in the way of the text endpoints on this box: ggml thread # count and `nice -n`. The extractor picks the best backend it is given, so the # device is stated rather than guessed. VOICE_PACK_DEVICE=CPU VOICE_PACK_THREADS=8 VOICE_PACK_NICE=19 # The reference window and the shape the packs are cut to. 24 kHz mono PCM is # the codec's own input rate, so no resampling happens inside the engine. VOICE_PACK_SAMPLE_RATE=24000 VOICE_PACK_MIN_SECONDS=18 VOICE_PACK_MAX_SECONDS=25 # Artifact arithmetic, all four values read out of the pinned tree: the codec # runs at 12.5 Hz (1,920 samples per frame at 24 kHz), packs 16 codebooks at 11 # bits = 22 bytes per frame, and the Base talker's speaker encoder emits # enc_dim 2048 f32 = 8,192 bytes. The .rvq window is 22 x 248 = 5,456 B up to # 22 x 312 = 6,864 B, i.e. crops of 19.84-25 s: an 18 s crop is a legal # reference.wav but too little code for the engine, and the builder says so # before it runs anything. VOICE_PACK_FRAME_RATE=12.5 VOICE_PACK_RVQ_BYTES_PER_FRAME=22 VOICE_PACK_SPK_BYTES=8192 VOICE_PACK_RVQ_MIN_BYTES=5456 VOICE_PACK_RVQ_MAX_BYTES=6864 # Where a --force rebuild puts the pack it replaced. Nothing is ever deleted; # the retired directory stays readable beside its replacement until a human # clears it. VOICE_PACK_TRASH_DIRNAME=.AGENTTRASH