Summary
ASR-only consumers unconditionally ship the LuxTTS G2P resources, and compile the TTS
and Diarizer subsystems, with no code path able to reach any of it. Could TTS and
Diarizer get the same opt-out trait treatment NemoTextProcessing received in #880 /
#888?
(Refiling #989, which I closed — it covered TTS only.)
Measurements
FluidAudio 0.17.5, release build of an ASR-only macOS app: Parakeet / Phonon-2 on the
ANE via AsrManager + AsrModels only. No TtsManager, no Kokoro, no LuxTTS, no
DiarizerManager — grep across the app's sources returns zero references to any of them.
FluidAudio_FluidAudio.bundle is 1.0 MB, and every byte is TTS:
| File |
Bytes |
luxtts_en_us_lexicon.tsv.zz |
982,385 |
luxtts_en_us_g2p_aux.json |
37,000 |
Info.plist |
1,134 |
That comes from the unconditional
resources: [.process("TTS/LuxTts/G2p/Resources")]
on the FluidAudio target.
Source footprint of the subsystems, for scale against the one actually in use:
| Subsystem |
Size |
Sources/FluidAudio/TTS |
2.4 MB |
Sources/FluidAudio/ASR |
1.3 MB |
Sources/FluidAudio/Diarizer |
728 KB |
Sources/FluidAudio/VAD |
60 KB |
TTS is the largest subsystem in the library — nearly double ASR. In a prior unstripped
build of the same app, TTS symbols were roughly 9% of the binary's symbol table.
So an ASR-only consumer pays ~1.0 MB of provably unreachable resources, plus the compile
time and binary weight of 3.1 MB of TTS and Diarizer source.
Why traits rather than separate products
Separate FluidAudioASR / FluidAudioTTS products would break every existing
import FluidAudio. The trait approach already taken for NemoTextProcessing avoids
that: default-on preserves current behaviour exactly, and consumers who want a smaller
build opt out. Mirroring the existing pattern in Package@swift-6.2.swift:
traits: [
.trait(name: "TTS", ...),
.trait(name: "Diarizer", ...),
.default(enabledTraits: ["NemoTextProcessing", "TTS", "Diarizer"]),
]
with those sources, and the .process("TTS/LuxTts/G2p/Resources") resource rule,
conditioned on the matching trait.
I appreciate the resource rule is the harder half. Conditioning source files and
resources on a trait within a single target is more awkward than conditioning a
dependency on a binary target, which is what #880 needed. If the practical route is
splitting TTS and Diarizer into their own trait-conditioned targets, that is equally good
from a consumer's point of view.
Happy to test a branch against an ASR-only consumer and report the resulting bundle and
binary sizes.
Summary
ASR-only consumers unconditionally ship the LuxTTS G2P resources, and compile the TTS
and Diarizer subsystems, with no code path able to reach any of it. Could
TTSandDiarizerget the same opt-out trait treatmentNemoTextProcessingreceived in #880 /#888?
(Refiling #989, which I closed — it covered TTS only.)
Measurements
FluidAudio 0.17.5, release build of an ASR-only macOS app: Parakeet / Phonon-2 on the
ANE via
AsrManager+AsrModelsonly. NoTtsManager, no Kokoro, no LuxTTS, noDiarizerManager— grep across the app's sources returns zero references to any of them.FluidAudio_FluidAudio.bundleis 1.0 MB, and every byte is TTS:luxtts_en_us_lexicon.tsv.zzluxtts_en_us_g2p_aux.jsonInfo.plistThat comes from the unconditional
on the
FluidAudiotarget.Source footprint of the subsystems, for scale against the one actually in use:
Sources/FluidAudio/TTSSources/FluidAudio/ASRSources/FluidAudio/DiarizerSources/FluidAudio/VADTTS is the largest subsystem in the library — nearly double ASR. In a prior unstripped
build of the same app, TTS symbols were roughly 9% of the binary's symbol table.
So an ASR-only consumer pays ~1.0 MB of provably unreachable resources, plus the compile
time and binary weight of 3.1 MB of TTS and Diarizer source.
Why traits rather than separate products
Separate
FluidAudioASR/FluidAudioTTSproducts would break every existingimport FluidAudio. The trait approach already taken forNemoTextProcessingavoidsthat: default-on preserves current behaviour exactly, and consumers who want a smaller
build opt out. Mirroring the existing pattern in
Package@swift-6.2.swift:with those sources, and the
.process("TTS/LuxTts/G2p/Resources")resource rule,conditioned on the matching trait.
I appreciate the resource rule is the harder half. Conditioning source files and
resources on a trait within a single target is more awkward than conditioning a
dependency on a binary target, which is what #880 needed. If the practical route is
splitting TTS and Diarizer into their own trait-conditioned targets, that is equally good
from a consumer's point of view.
Happy to test a branch against an ASR-only consumer and report the resulting bundle and
binary sizes.