CoolFace
Modelpublic

litert-community/sopro-v2-turbo

sourceHugging Faceapache-2.0updated 1d agoView on Hugging Face
0likes204downloads
Model Card

Sopro v2 turbo on LiteRT (CompiledModel)

[image]

Sample from the converted model (plain fp16 weights + int8 AR, streaming vocoder) cloning a CC0 volunteer voice from kyutai/tts-voices (voice-donations/0a67): assets/hero_sample.wav — “The morning light fell across the garden while a small bird sang beside the open window.” Source and license of the reference voice: assets/ATTRIBUTION.md.

What it is

These classic .tflite graphs convert Samuel Vitorino's Sopro v2 turbo, a reference-conditioned text-to-speech model for English, European Portuguese, French and German, to the LiteRT CompiledModel API. The package provides fp32 reference graphs, plain weight-only fp16 graphs and a native-int8 merged AR alternative. It produces 24 kHz audio and includes a streaming vocoder, while resampling, spectral DSP, token sampling and the acoustic solver loop stay on the host. The upstream model and source are Apache-2.0; the included LICENSE is retained verbatim. Conversion used sopro==2.2.0 and upstream model revision f747f9edfb7b0233a3b7105af3a75603a7213d26.

Files

The file sizes below are exact bytes. There are fourteen fp32 graph files, fourteen plain wfp16 graph files and one native-int8 merged AR file. The Android variants add eight fp32, seven wfp16 and one int8 graph file (rows marked "Android variant"), plus the app source under android/. Separate AR prefill/step files and merged alternatives are included so callers can choose one form without loading both. This table covers the executable artifacts, host assets and conversion sources.

FileBytesSHA256DtypePurpose
LICENSE11357c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4metadata / sourceVerbatim upstream Apache-2.0 license
contract.json98047e4ea40c95fd2d3bbc937377e5f3734b89771b3c55fe4f778821a4d77a8442a6fmetadata / sourceStatic shapes, signatures and host limits
conversion/acoustic_graphs.py3856bda1b4426b23c2770d76e0af84b9f81f8411a1a97c94217c6287ba2c4ad3c86ametadata / sourcePortable conversion / host / verification code
conversion/acoustic_host.py4050c7248ea9567b01a08990770dcb978cc7ddf9ca012c8ccf79d3d194f63843c5b2metadata / sourcePortable conversion / host / verification code
conversion/alias_probe.py205641a9f31e4e8fcee8995823daa8118c2bd68462c1ca767e3d1d79804dc030ded8metadata / sourcePortable conversion / host / verification code
conversion/ar_graphs.py4345ab23a34559b82af3ee4f65ec4263e0cedb951d074b3603f7ef490e506b767152metadata / sourcePortable conversion / host / verification code
conversion/ar_host.py43742af8283e5234070e509f48e833b2cda83d5f4f4145f799afc0d17d6f89a3a77cmetadata / sourcePortable conversion / host / verification code
conversion/common.py38030eb12811cf8873c1571ad7385fab1bf83987841ce3f6735293b94b3927e99009metadata / sourcePortable conversion / host / verification code
conversion/convert.py10903c2328d3a3e497c739809c15de1ebb7dcba5e0b1785b6d003b470a84f512f54f0metadata / sourcePortable conversion / host / verification code
conversion/env.sh93763eaadbce97e6e550ee8c0a22854f354462f6a63daceb8ee22cd9156c4c1950bmetadata / sourcePortable conversion / host / verification code
conversion/graphs.py2685573e37bbd1cd2c4360b943fdcdbdaca02158453f394d43fa1bfbf0c2d025e4aametadata / sourcePortable conversion / host / verification code
conversion/host_dsp.py4193725b72c350d1ce13996697075d29f1e36bb6d325bf751b4d55b5121f800e5dd7metadata / sourcePortable conversion / host / verification code
conversion/host_postprocess.py33335fe4424616862a47db9345e58ccc01d7bebe16b830798823d8f60e1fcb265b5dmetadata / sourcePortable conversion / host / verification code
conversion/host_sampler.py46538cb47d226d0dbdf4e2157b1f23f986d7b6051da80d88c50c153ad5a7bf1c44c3metadata / sourcePortable conversion / host / verification code
conversion/host_stream_istft.py43166f60c5df3175ffad796e5ab10805ef55efb04b378c8466dff2f927665548d7e3metadata / sourcePortable conversion / host / verification code
conversion/inference.py12525b331379226d7c104cbd1c73cf7b477a4764043be57f4732b7f0543f7393b7a28metadata / sourcePortable conversion / host / verification code
conversion/litert_utils.py8335285738f0315020700dfd6d226858f42b58481b138fa2912192a47ae921bf8733metadata / sourcePortable conversion / host / verification code
conversion/oracle.py105168dc41a8563611113717b6c7cd2b883eb19f4ea585b34e6dc95c7990e58e47b96metadata / sourcePortable conversion / host / verification code
conversion/parity.py715856fe4adbfccd79bf16a2061fd507a43c91bb9b60f441c232d752fc6cb42089c1metadata / sourcePortable conversion / host / verification code
conversion/perceptual_proxies.py223556a170f791e45bc88c7265b8177db945bfc4e0d2ef2820734f57c3ccef1755f5metadata / sourcePortable conversion / host / verification code
conversion/quality.py50047f9058dc16bf0e96be879573f43ad3cbb8f909a1ccbc34a2b488306f314ea370metadata / sourcePortable conversion / host / verification code
conversion/requirements.txt1364d6c81211dbb482532f32c3e941df3c7306f0b8a04b8e0fa3d8558372a2ef5ffcmetadata / sourcePortable conversion / host / verification code
conversion/semantic_graph.py3012c585e0f05ed1b1468b06387a9e85f1d07114a5e3e0a7c8e976d43e73d21d462cmetadata / sourcePortable conversion / host / verification code
conversion/source.py18011caad8a5d4c729521166c08e70fd1245bf89906f523dc074249ce64c6c71adc4metadata / sourcePortable conversion / host / verification code
conversion/vocoder_stream_graphs.py18158a266c2a6472f206816fb4d6af6bf2433f147c25bd136ccd7c6c0b89f889d46dmetadata / sourcePortable conversion / host / verification code
fp32/sopro_acoustic_condition_fp32.tflite101598868f60849c565fc056a76badcc640d2f8c564b44c23ca09fad73ccd5b80fd146e6bfp32 weights / float32 or int32 I/Oacoustic condition
fp32/sopro_acoustic_condition_t4096_fp32.tflite101598868414975cffdb3c9d9a3ac2596d4e6132087710a87ce6b750c0fe021d3e06d7e07fp32 weights / float32 or int32 I/Oacoustic condition t4096
fp32/sopro_acoustic_velocity_fp32.tflite127775972c9d7821502613595828fc7699d92e43a47c542d9f885dbdbd4592bbf67f57a5afp32 weights / float32 or int32 I/Oacoustic velocity
fp32/sopro_acoustic_velocity_t4096_fp32.tflite1288245487901c9f4af3537275e345a0fa6bd33be34cfac622ba8f7b827485edc30490749fp32 weights / float32 or int32 I/Oacoustic velocity t4096
fp32/sopro_ar_merged_fp32.tflite2234576484cf522669f55f0bc93087e473c6c03df116f0086c922903b8f5688db52cb35aefp32 weights / float32 or int32 I/Oar merged
fp32/sopro_ar_prefill_fp32.tflite210678772a5fbff1f345cfa17795fd33ddf488773d3cfc117d059e4fbe4b8d15ea342a84afp32 weights / float32 or int32 I/Oar prefill
fp32/sopro_ar_step_fp32.tflite210563980b508e7512a55cb0d2105e7fc6927cddee500affd9121eba4dc9db6c59e8ea36cfp32 weights / float32 or int32 I/Oar step
fp32/sopro_semantic_encoder_fp32.tflite80479220c1d67fc72b953a6805690910f59d5fe68f2e47a15013ec27b6f979c1869f1ef9fp32 weights / float32 or int32 I/Osemantic encoder
fp32/sopro_speaker_encoder_fp32.tflite12613536f7e5f8302821fa9917c50656f64e81007201fa9ebc0a973f29f65eb7890e6d57fp32 weights / float32 or int32 I/Ospeaker encoder
fp32/sopro_style_prefix_fp32.tflite319122478e5c8aed42c5ec23da2224fd2ed3bb3368b6a49b8b1b450118759829c075ec5fp32 weights / float32 or int32 I/Ostyle prefix
fp32/sopro_vocoder_fp32.tflite5414531252fd19478ba13a6ffaae324d67c33ad7c3bbfd5eb28fd20a51d2fd93fba6dfc7fp32 weights / float32 or int32 I/Ovocoder
fp32/sopro_vocoder_stream_flush_fp32.tflite5417790810271f9c9f2a83872b9fd88760faa59f68dffe13634596d39fef2de9532e3c02fp32 weights / float32 or int32 I/Ovocoder stream flush
fp32/sopro_vocoder_stream_start_fp32.tflite541815843a64a0e437caac9ee9637d1d73115ccfe8cff5a24f1665b4ab181a4483737da1fp32 weights / float32 or int32 I/Ovocoder stream start
fp32/sopro_vocoder_stream_step_fp32.tflite5417899651701381131b45fe9afff4cbbff8ef881c2e0254b7333cbcb34d216cc1931282fp32 weights / float32 or int32 I/Ovocoder stream step
host_assets/ar_tables_fp16.bin12870788feda5bf963e8034c89edcd7d16721087072941eae5d41e591828faa88d93574dlittle-endian fp16 + int32 metadataPacked embedding tables; un-gated fp16 storage option
host_assets/config.json4482b8593ff96b2976215ff64e58e784f4d667f4f0f205ae8686b57e78c10f42d6d7metadata / sourceUpstream configuration
host_assets/dsp_constants_fp32.bin446224d52b5b6dacba6ba21a6e0d44578575211f13d0711bbebeb5c06e0c6656ede574little-endian fp32 + int32 metadataPacked host frontend / FFT constants
host_assets/host_assets.json666568e37b921b8a91d578ef157092427a4bd691354e543f82c40dee91efae3ac16emetadata / sourceLittle-endian tensor offsets and shapes
host_assets/sopro_ar_tables_fp32.npz25742598a8e4c7e415c6978ad06b12919311b301544910f11da696cbc2b18ef4ca2559a9original array dtypesOriginal fp32 tables used by measured pipeline
host_assets/sopro_dsp_constants_fp32.npz448952d2c8aeafd550b84e9ea9d2fa59d9683f9c28c4f5ac7ca393dd707af3a1d9ed45original array dtypesOriginal host DSP arrays
host_assets/tokenizer.model3708212d76e7a4e8dbd0a4d2137c13200ac9710ad3fd93fad20a61f71102ab7bae754dmetadata / sourceUpstream SentencePiece tokenizer
int8/sopro_ar_merged_i8native.tflite552170004c9a54699f2e1b0ca6e777970b440bf5666e8399ea1342dce7d2dea6c5ad86fbint8 weights / float32 or int32 I/Oar merged
wfp16/sopro_acoustic_condition_t4096_wfp16.tflite5082222423c8c7ce01a1488911b86ffce5133f55029a53b47a2a55c6f6bd8c11a6be947bfp16 weights / float32 or int32 I/Oacoustic condition t4096
wfp16/sopro_acoustic_condition_wfp16.tflite508222249fa5e75f006bb3c8f291f0bf2393e20bff22baea6a70b7443eb4ee1a61d92bddfp16 weights / float32 or int32 I/Oacoustic condition
wfp16/sopro_acoustic_velocity_t4096_wfp16.tflite6567214435db79991879a313d3fbffcf8b5e05394c8ad3122e564fe8937f39a45928e07efp16 weights / float32 or int32 I/Oacoustic velocity t4096
wfp16/sopro_acoustic_velocity_wfp16.tflite646235681dfc3d21128e1cae50ccab4147a3126157bfaf5d753d4934063b35e13f76ae79fp16 weights / float32 or int32 I/Oacoustic velocity
wfp16/sopro_ar_merged_wfp16.tflite112038416c5401ec8a55c6c51c4fb07979d469320d8886ab7a1bbbc14a1ed410597b85137fp16 weights / float32 or int32 I/Oar merged
wfp16/sopro_ar_prefill_wfp16.tflite105544496560814c267ab096b04c8982031a55057c5128207edb524794fcd175cce520114fp16 weights / float32 or int32 I/Oar prefill
wfp16/sopro_ar_step_wfp16.tflite105426896934e951d2237ff2ff4621108724d712a8199c127c0ae93d87886784c87426fb1fp16 weights / float32 or int32 I/Oar step
wfp16/sopro_semantic_encoder_wfp16.tflite40895552a68d0d8fc5ee867716e7d556f3e0561ae618c0810f186d1c5bb2d55a7742af9bfp16 weights / float32 or int32 I/Osemantic encoder
wfp16/sopro_speaker_encoder_wfp16.tflite651614439f46f2b66bc6c3f73b48f69faf7e480efc72d3a20a5a0cca8b4ee325ecabd0efp16 weights / float32 or int32 I/Ospeaker encoder
wfp16/sopro_style_prefix_wfp16.tflite1618800ab56621a5c9bf880656ad517c4219b3db4d1da3f75f9a9ae929d7dbe9a1ae8c9fp16 weights / float32 or int32 I/Ostyle prefix
wfp16/sopro_vocoder_stream_flush_wfp16.tflite27246640adb45a266fc98e69d3731cd2ebf8db47ff692feaf1fda35b4cc53a18e4e9e4d3fp16 weights / float32 or int32 I/Ovocoder stream flush
wfp16/sopro_vocoder_stream_start_wfp16.tflite272505125da076e8414f62db2bf99357495be848c87fe5921608b32ff38e39e59811d65ffp16 weights / float32 or int32 I/Ovocoder stream start
wfp16/sopro_vocoder_stream_step_wfp16.tflite272474888657d179710ffba7d0a5dec3227226c93c8b7868bc51cd34ae19d8eb9ecf634afp16 weights / float32 or int32 I/Ovocoder stream step
wfp16/sopro_vocoder_wfp16.tflite27158496ef807881576945d7f2415990ffbf0f6a22626a8b2ecf3cd826e4707d0bbf2ee4fp16 weights / float32 or int32 I/Ovocoder
fp32/sopro_acoustic_velocity_r6_fp32.tflite127810560bbd1ca24238167d7bbfb75b9c5c01113ee408d3135ef36962a57982263d84740fp32 weights / float32 or int32 I/Oacoustic velocity r6 fp32 (Android variant)
wfp16/sopro_acoustic_velocity_r6_wfp16.tflite646556168b78862126487e49fffefbfa1aed5b28c4f9eb45384f224023706514352f6a7bfp16 weights / float32 or int32 I/Oacoustic velocity r6 wfp16 (Android variant)
fp32/sopro_acoustic_velocity_t4096_r6_fp32.tflite128867328f47c3fbf103e2d3f2d250362f71b46ccbbe9baccb5a4ad5e773155942793088dfp32 weights / float32 or int32 I/Oacoustic velocity t4096 r6 fp32 (Android variant)
wfp16/sopro_acoustic_velocity_t4096_r6_wfp16.tflite6571238451aa464fbca8240b906621ba334d8891835d12c51ccc95a76b4ec6a2775e54c6fp16 weights / float32 or int32 I/Oacoustic velocity t4096 r6 wfp16 (Android variant)
fp32/sopro_ar_merged_r6_fp32.tflite2234892001cf7373f5c7a05659c00738a5883aabea1cfb4e9c4c67e1b3c83f0eff1a13f39fp32 weights / float32 or int32 I/Oar merged r6 fp32 (Android variant)
wfp16/sopro_ar_merged_r6_wfp16.tflite1120616807d66f29a5b64111ba5914abcfbdbcaff9c910f4ccbac1e8835e8da58ca4a7c75fp16 weights / float32 or int32 I/Oar merged r6 wfp16 (Android variant)
int8/sopro_ar_merged_r6_int8.tflite552491923d80c19472006c6e6602977629f6419c33903fd12a12924c65407b03cf941f2eint8 weights / float32 or int32 I/Oar merged r6 int8 (Android variant)
fp32/sopro_acoustic_condition_r6_fp32.tflite10159659260568d9fcd9f0dba6722a442a75c6e24dce4aeb1cdd3b6cd30784c38e52838fafp32 weights / float32 or int32 I/Oacoustic condition r6 fp32 (Android variant)
wfp16/sopro_acoustic_condition_r6_wfp16.tflite50820160ad5ef423f523dc903e5909b2310bd82311f4a972b82c4a0c145d255999eb7d0efp16 weights / float32 or int32 I/Oacoustic condition r6 wfp16 (Android variant)
fp32/sopro_acoustic_condition_t4096_r6_fp32.tflite1015965929dbb9ec867fe6eeb1855ab7687fb97ea351cf3e9b7aef753a6a1fbcabd6d262ffp32 weights / float32 or int32 I/Oacoustic condition t4096 r6 fp32 (Android variant)
wfp16/sopro_acoustic_condition_t4096_r6_wfp16.tflite5082016023e6866a3f1c0f016999e21f288481e31574ee0da1ce3cf2336ffb0c87550addfp16 weights / float32 or int32 I/Oacoustic condition t4096 r6 wfp16 (Android variant)
fp32/sopro_semantic_encoder_r6_fp32.tflite8142479216230b386eb1b10c3a749997a4aad492e1472c0e6a101aa98e3a2bf63243c957fp32 weights / float32 or int32 I/Osemantic encoder r6 fp32 (Android variant)
wfp16/sopro_semantic_encoder_r6_wfp16.tflite413718400b1af43d356f225b3707746364b23187b18fcdb59318c0369a5596482d431484fp16 weights / float32 or int32 I/Osemantic encoder r6 wfp16 (Android variant)
fp32/sopro_style_prefix_r6_fp32.tflite319356063ca99524d37134460af37f769a9bfb9896446841069cb013c9219a6897deb35fp32 weights / float32 or int32 I/Ostyle prefix r6 fp32 (Android variant)
wfp16/sopro_style_prefix_r6_wfp16.tflite162102495791de76282b699122aa575f78d155878b1be333430223377d36404d4e45285fp16 weights / float32 or int32 I/Ostyle prefix r6 wfp16 (Android variant)
fp32/sopro_style_prefix_r9_fp32.tflite31931165dbe43a551015ab2a03451a015918dfeaa550e9456f0d277a7646db92769eaf5fp32 weights / float32 or int32 I/Ostyle prefix r9 fp32 (Android variant)
host_assets/ar_tables_fp32.bin257413129c6c89d2af9efffbf3c0a486acbd1f708c610b5cd0d67ab338abce0841f3b88alittle-endian fp32Raw fp32 embedding tables for the Android app (little-endian)
host_assets/ar_tables_fp32.json8019b42a5f8923891627a6c36b151ebc0c9153221939f01a5c46bf1a57282f8349fmetadata / sourceOffsets and shapes of artablesfp32.bin
contract_r6.json939616fbc6bb3e96ba84d8a0e0ebb11668e15062cf9c76d56dde13f5ac12665bd4820metadata / sourceAndroid variant signatures (round-6 rewrites)
contract_r9.json2383ac8d892dac48d546ebe36220fee693e176bef78437bb32f91b33ed486321abbemetadata / sourceAndroid variant signatures (fp32 style)
android/see android/README.mdKotlin sourceAndroid sample app (Compose, LiteRT CompiledModel), 37 source files

The wfp16/ files use plain FLOAT_CASTING: the final vocoder head is also stored as fp16, with float32 public I/O and CPU computation. The native-int8 AR uses per-channel int8 weights with dynamic activation quantization inside its CPU kernels. No speaker, acoustic or vocoder int8 candidate is included.

Raw host bins are little-endian; host_assets/host_assets.json gives each tensor's shape, dtype, byte offset and byte size. The measured pipeline used the original fp32 NPZ embedding tables. The requested fp16 raw embedding tables are a storage option, not an independently quality-gated host change. The original NPZs remain available for the validated path.

Graph contract

Batch size is one. Float inputs and outputs are float32 for every weight variant; token IDs, gather indices and last_index are int32. Bind actual signature names and buffer order from contract.json, not inferred tensor-index order. The ordinary signature is serving_default; merged AR exposes prefill and step with the same corresponding I/O contracts.

GraphInputs, in exported argument orderOutputs
Speaker encoderspeaker mel [1,80,1001]id [1,192], style [1,128], control [1,8], condition [1,512]
Semantic encoderWhisper mel [1,80,1002]reference tokens [1,235] int32
Style prefixsemantic embeddings [1,160,512]prefix [1,8,512]
AR prefillembeddings [1,256,512], additive bias [1,1,256,256], last index [1]logits [1,4377], K and V [1,96,256,64] each
AR stepembedding [1,1,512], cos and sin [1,1,1,64] each, bias [1,1,1,1024], K and V [1,96,1024,64] eachlogits [1,4377], new K and V [1,96,1,64] each
Acoustic conditiontokens [1,N], token mask [1,1,N], frame-to-token [T]mu [1,100,T]
Acoustic velocityx [1,100,T], time [1], mu [1,100,T], condition [1,512], condition mel [1,100,T], condition mask [1,1,T], key bias [1,1,1,T]velocity [1,100,T]
Offline vocoderdenormalized mel [1,100,1024], frame mask [1,1,1024]iSTFT features [1,1024,1026]
Vocoder stream startmel [1,100,64]features [1,37,1026], embed state [1,100,6], convolution state [8,1,512,6], pending state [8,1,512,3]
Vocoder stream stepmel [1,100,64] and the three states in that orderfeatures [1,64,1026] and updated states
Vocoder stream flushthe three statesfeatures [1,27,1026]; discard neural state

Acoustic (T,N) is (2048,512) or (4096,1024). The _t4096 pair is the second bucket. The semantic encoder follows the installed package's 500-position crop before its fixed interpolation to 235 tokens. The fixed prefix uses eight style vectors, 120 prompt tokens and BOS, leaving at most 127 text tokens in the 256-position bucket; reject or segment a longer prefix before inference. Source activation functions are retained: semantic/vocoder erf-GELU and the acoustic DiT's native tanh-GELU.

The Android variants (_r6 / _r9 files) keep these computations and change only inputs the GPU delegate could not lower: AR prefill takes last_onehot [1,256] instead of last_index; acoustic condition takes token_onehot [1,N,4377] and frame_onehot [T,N] instead of semantic_tokens and frame_to_token; the semantic encoder returns digit_logits [1,235,27] and the host takes the argmax; the style prefix is shipped as fp32. Their signatures are listed in contract.json under android_contract_deltas, and their Mac CPU outputs equal the originals (maximum difference 0.0).

Host-side steps

  • Load mono audio, resample and apply the upstream reference normalization. Compute separate speaker, Whisper and acoustic mel frontends, including the speaker frame LayerNorm and Whisper's zero append, logarithmic floor and dynamic-range clamp. Normalize the acoustic reference mel with the supplied mean and standard deviation.
  • Tokenize with the language tag, gather text and semantic embedding tables, build the style/text/prompt/BOS prefix, and create RoPE tables and runtime additive masks. Keep packed K/V on the host; each step returns only its new slice. Attention includes the current key/value and masks invalid cache slots with −10,000.
  • Sample at temperature 0.8, top-k 25 and top-p 0.9, using the probability-below-kth rule, shifted top-p removal and 1e-8 normalization clamps. BOS is always masked; EOS is enabled from prediction ten. Use one uniform draw and inverse CDF. The evaluated free-running seed rule was NumPy default_rng(2000+i).
  • Construct frame-to-token indices from the actual token/frame lengths. Run the two Euler steps on the sway grid [0,0.29289323,1], using the same initial noise when measuring parity, and re-mask the reference prompt after each step with sigma_min 1e-6.
  • Denormalize the solved mel, retain 32 reference-context frames before generated frames, run the vocoder, split its head output into log-magnitude and phase, apply exp/clamp and complex phase, and perform host iSTFT with FFT 1024, hop 256 and periodic Hann. Overlap-add, window-envelope division and center cropping remain host operations.
  • Remove reference-context samples and apply output_gain, leading/trailing trim, single-segment join, soft_limit, then an 80 ms fade-out without fade-in. Streaming iSTFT state is ola, env, tail_start, processed_frames, and emitted_samples.

fp32 FFT contract: the acoustic mel mirror explicitly selects NumPy's fp32 FFT path with an exact power-of-two normalization and scale restoration. A mathematically equivalent implementation that dispatches to fp64 changes fp32 rounding. Preserve the implementation in conversion/host_dsp.py when reproducing these measurements; another FFT library needs its own parity check.

Reference contract

The static host input is exactly 10 seconds at 24 kHz, or 240,000 samples, resampled to 160,000 samples at 16 kHz for the encoders. Longer references are cropped; deterministic zero-padding of shorter inputs is the host's fixed-shape option. Short-reference padding was not evaluated: both measured references were at least ten seconds, and normalization preceded cropping. This differs from upstream crop_on_pause, which can append random room tone and is bypassed here. Upstream describes voice cloning from 5–20 seconds of reference audio; that guidance is not a measurement of this fixed bucket.

Buckets and limits

ComponentStatic limitMeaning
Acoustic primaryT=2048, N=512Up to about 11.8 seconds of generated speech after the ten-second reference
Acoustic longT=4096, N=1024Up to about 33.7 seconds by shape capacity; the sampler remains capped at 30 seconds
AR256 prefix positions; cache 1024At most 704 generated tokens; worst contract occupancy 960 positions
Offline vocoder1024 mel framesAbout 10.9 seconds total mel coverage; reference context consumes part of that bucket
Streaming vocoder64-frame callsNo fixed upper utterance-length bucket; current tail-replay helper needs at least 128 real mel frames including context

Streaming start emits 37 feature frames, ordinary steps emit 64, and flush emits 27. For a partial final chunk, the host replays the last 128 real mel frames through a separate start/step, keeps only the remainder from that step, then flushes that replay state. This is the measured exact tail strategy; it does not cover arbitrarily short inputs. The acoustic solver itself is still offline.

The long fixtures generated 16.416–17.824 seconds of raw audio, using 385–418 tokens and at most 635 cache positions. All four used the long bucket and streaming vocoder. These are measured long-utterance tests, not thirty-second saturation tests; the thirty-second bound is established separately by the static capacity arithmetic.

Android

The android/ directory holds a Kotlin sample app (Compose, LiteRT CompiledModel) that runs the whole pipeline on the phone: the three mel front-ends with an fp32 FFT, the 24→16 kHz resampler, the SentencePiece tokenizer with the language tag, the packed-KV AR loop with the sampler, the two-step acoustic solver, the streaming vocoder with a host iSTFT, and the post-processing chain, then plays the audio through AudioTrack while it is being decoded. Every host step was gated on the JVM against the Python dumps before the device (worst tensor difference 2e-5 on the mels, 2.4e-7 on the iSTFT, sampler picks 2,920/2,920, tokenizer 1,214/1,214) and again on the phone, where the device chains reproduce the Mac results (fp32 teacher-forced raw waveform corr ≥ 0.9999993, 24/24).

Five graphs were re-exported without any numerical change so that the GPU delegate accepts them; the new files carry the _r6/_r9 suffix in their directories and supersede the originals on Android (Mac CPU parity against the originals: maximum difference 0.0). sopro_ar_merged: the prefill's last-row gather became a one-hot float input last_onehot [1,256]. sopro_acoustic_condition: the token-embedding lookup and the frame gather became one-hot float selections (token_onehot [1,N,4377], frame_onehot [T,N]). sopro_acoustic_velocity: the BROADCAST_TO lowering was removed. sopro_semantic_encoder: the graph returns the FSQ digit logits [1,235,27] and the host takes the argmax and forms the token (bases 1, 7, 35, 175, 875). sopro_style_prefix: shipped as fp32 (3.2 MB). Signatures and buffer order are in contract.json.

Measured placement on a Galaxy S26 (SM-S942Q, Adreno), LiteRT 2.2.0, release build, screen on, the phone held by the test alone, battery 39–43 °C. Warm latency is the synchronized run + read time of one call, median over the streaming suite.

GraphStorageAcceleratorGateWarm ms
speaker encoderwfp16GPU, precision FP32id max diff 2e-7; default precision returns non-finite output16.3
semantic encoderwfp16GPU, precision FP32tokens 235/235 ×2; default precision flips 93–100 of 23550.6
style prefixfp32CPUthe delegate rejects its batched matmul (non-constant tensor)1.0
AR merged prefill / stepint8CPUGPU FP32 is exact (2,920/2,920) but a step costs 22 ms against 9.5 ms on CPU: the 50 MB packed KV crosses the bus every step47.5 / 9.5
acoustic conditionwfp16GPU, default precisionsolved-mel corr ≥ 0.999732.5
acoustic velocitywfp16GPU, default precisionsolved-mel corr ≥ 0.9996, chain log-mel corr ≥ 0.9975, HNR change ≤ 0.2 dB177.2
vocoder stream start / step / flushwfp16CPUGPU output is not speech in either precision (raw waveform corr ≈ 0, HNR change 5.7 dB); a rank-4 rewrite did not change it7.5 / 7.4 / 4.1

Streaming on that placement, 36 utterances (the two private references and the CC0 demo voice × the 12 test sentences), release build:

PlacementTime to first audio, median [min, max]Real-time factor, median [min, max]Ready after a cold launchFirst tap after Ready
shipped (GPU acoustic + encoders, CPU AR + vocoder)2.07 s [1.13, 3.77]0.41 [0.25, 0.79]3.5 s (five GPU compilations)1.8–2.9 s
all CPU3.34 s [1.77, 4.06]0.64 [0.36, 0.78]0.6 s3.1 s

Time to first audio is the tap-to-first-PCM-chunk time; the AR loop and the two velocity calls precede the first vocoder chunk. Quality on the shipped placement: Whisper-turbo WER 0.56 % / 3.70 % / 0 % and speaker cosine 0.939 / 0.916 / 0.777 for reference one / reference two / the demo voice (the demo voice's own PyTorch baseline is 0.781; its clip is a plain volunteer recording). Playback applies the output gain and soft limit per chunk; the saved WAV goes through the full offline post-processing.

Pixel 8a (Mali): not measured yet.

What is not included

Mac CPU numbers and Galaxy S26 Android numbers are included; the Pixel 8a (Mali) column is not measured yet. No streaming acoustic graphs, NPU verification or private reference voices are included. A macOS ai-edge-litert 2.2.0 GPU-only CompiledModel ends the process with SIGSEGV on a trivial Linear-plus-ReLU graph at creation; the 2.1.6 wheel runs the same file on Metal, so no Mac GPU numbers are given here. Private LibriSpeech reference readers and their generated audio are excluded from this package.

Gates and results

All execution measurements in this section used Apple M4 Max CPU, four threads, ai-edge-litert 2.2.0 CompiledModel, with float32 public tensors and the weight dtype named by the table. Source checks used PyTorch 2.11.0 / torchaudio 2.11.0 / Sopro 2.2.0 in float32. The machine was shared; timings are contended. The short suite contains two private reference voices and twelve sentences, giving 24 utterances in English, Portuguese, French and German. Four additional long utterances cover English and German. This is a small conversion-gate suite, not broad model-quality coverage.

For fp32 per-graph floats, the measured rule is finite values, correlation at least 0.9999, norm ratio within 1±0.001, and max error at most max(0.001,0.0001×reference_absmax) on valid positions. Integer output agreement is checked separately. Reduced-precision acoustic graphs are judged in solved-mel and output log-mel space; vocoders are judged in waveform space. Absolute phase-channel tensor errors do not determine audible parity.

fp32 reference set

Gate / measured domainCoverageResult
Speaker embeddingsBoth referencesid max error 2.98023e-7; other outputs at most 1.75089e-6; minimum correlation 0.999999999998
Semantic tokens / diagnostic digit logitsBoth references235/235 tokens exact each; digit-logit max error 1.75476e-4
AR prefill and step24 prefixes; 2,896 replay stepsStep greedy agreement 100%; worst step-logit error 3.29018e-5
Acoustic mu / velocity / solved mel24 utterances; two velocity calls eachMaximum errors 6.86646e-5 / 5.50747e-5 / 1.46389e-4
Offline vocoder waveform24 melsMinimum correlation 0.999999998368; maximum error 1.38946e-4
Full teacher-forced raw / final waveform24 utterances, oracle tokens and noiseMinimum correlations 0.999999470531 / 0.999999426261; max errors 0.00471610 / 0.00564674; final trim lengths exact
Streaming vocoder waveform against source offline24 melsMinimum correlation 0.999999994171; maximum error 3.94199e-4
Long full chain, raw waveformFour utterancesMinimum correlation 0.999997804111; maximum error 0.01242098

The source's own fp32 streaming/offline difference reached 2.15493e-4; its fp64 waveform check reached 4.96575e-13. This source summation-order difference was accepted without changing the graphs.

Plain wfp16 set

Gate / measured domainCoverageResult
Semantic token agreement with fp32 graphBoth references234/235 and 235/235 exact
AR greedy replay with fp32 graph2,920 predictions including prefillsPrefill-only swap: 2,919/2,920; sole disagreement gap 9.72748e-5; style, step and merged swaps: 2,920/2,920
Acoustic condition single swap, solved mel / output log-mel24 utterancesMinimum correlations 0.999999984985 / 0.999931426392; solved-mel max error 0.00727928
Acoustic velocity single swap, solved mel / output log-mel24 utterancesMinimum correlations 0.999999922480 / 0.999944061523; solved-mel max error 0.01574764
Plain offline vocoder single swap, waveform24 utterancesMinimum correlation 0.999938064242; max error 0.05010722
Plain stream start / step / flush single swaps, waveform24 utterances eachMinimum correlations 0.999999530346 / 0.999968632447 / 0.999999902165
All plain-wfp16 vocoder streaming, identical input mel24 utterancesMinimum waveform correlation 0.999967892482; max error 0.02738303
Full reduced teacher-forced chain, output log-mel24 utterancesMinimum correlation 0.999905133195; PASS by acoustic mel-domain rule
Full reduced teacher-forced perceptual proxies, same tokens/noise as fp32 chain24 utterancesMaximum HNR change 0.011127 dB; maximum high-band ratio change 0.021581 dB; limits 1.0 / 1.5 dB
Long reduced full chain, output log-melFour utterancesMinimum correlation 0.999970696744

The full reduced chain uses the wfp16 neural stages and native-int8 merged AR, with original fp32 host tables. Teacher forcing fixes the reference/generated token sequence and initial noise, so AR precision does not select a different downstream sequence in that test. The all-wfp16 AR alternative was also executed on the same teacher path. Its minimum raw-waveform correlation was 0.977468237158 on one Portuguese utterance; that value is informational under the acoustic mel-domain rule, not omitted or treated as waveform equivalence. HNR uses 25 ms autocorrelation frames with 10 ms hop and the fp32 output's HNR>0 dB voiced mask; energy ratio compares 4–12 kHz with 0–4 kHz.

Native-int8 merged AR

Gate / measured domainCoverageResult
Oracle-path greedy replay, fp32 AR comparison2,920 predictions2,834 agreements, 97.0548%; informational, no bit-exact claim
Single-swap teacher-forced downstream waveform24 utterances, fixed tokens/noiseMinimum correlation 0.999999470531; max error 0.00471610
Free-running with other graphs fp3224 utterancesMean WER 1.306168%; speaker cosine mean 0.925210, minimum 0.868675
Exact packaged pipeline configuration24 utterancesQuality shown below; wfp16 graphs plus native-int8 merged AR and fp32 host tables

WER and speaker similarity

ASR: openai-whisper turbo / large-v3-turbo, package 20250625, float32 CPU, four threads; language forced per utterance, 16 kHz float32 array input, no ffmpeg. WER uses jiwer 4.0.0, Whisper EnglishTextNormalizer for English and BasicTextNormalizer for Portuguese/French/German. Values are the unweighted mean of per-utterance WER, in percent. Speaker cosine uses the source float32 speaker encoder on each output and its reference embedding. Hardware/runtime and contended conditions are those stated above; these are quality comparisons, not speed comparisons.

Output configurationMean WER %Mean speaker cosineMinimum speaker cosine
PyTorch source oracle, seeds 1000+i1.1135910.9259230.856220
All-fp32 LiteRT free-running, seeds 2000+i2.0584830.9222570.869553
Packaged reduced configuration, seeds 2000+i1.2598720.9259390.853079
LanguageUtterancesSource-oracle mean WER %Reduced-pipeline mean WER %Reduced-pipeline mean speaker cosine
en120.5952381.0110290.937780
pt43.3333331.6666670.931149
fr41.5625001.3888890.919935
de40.0000001.4705880.891210

All 24 reduced-pipeline final waveforms were finite, with RMS above 0.001 and peak at most 1.0; measured durations were 4.4693–6.0053 seconds. The quality gate required mean speaker cosine no more than 0.03 below the oracle, minimum cosine at least 0.80, and mean WER no more than three percentage points above the oracle.

Contended CPU timing

Apple M4 Max CPU; ai-edge-litert 2.2.0 CompiledModel; four threads; contended. The graph table includes input-buffer writes, synchronous execution and output-buffer reads, with two warmups followed by eight timed calls on recorded real inputs. Each row names its weight format; public I/O is float32 or the declared int32 indices. Model load is excluded. Other workloads shared the machine, so these are observed latencies, not latency guarantees or phone estimates.

Graph / signatureWeight formatMedian msp90 ms
speakerencoder / servingdefaultwfp1610.54111.901
semanticencoder / servingdefaultwfp1627.51529.755
styleprefix / servingdefaultwfp160.8200.908
arprefill / servingdefaultwfp1636.13138.471
arstep / servingdefaultwfp1611.80713.194
acousticcondition / servingdefaultwfp1633.96835.781
acousticvelocity / servingdefaultwfp16208.691219.439
vocoder / serving_defaultwfp1621.44024.355
vocoderstreamstart / serving_defaultwfp165.1005.410
vocoderstreamstep / serving_defaultwfp166.8287.412
vocoderstreamflush / serving_defaultwfp165.8557.450
ar_merged / prefillwfp1640.04244.715
ar_merged / stepwfp1613.67221.533
ar_merged / prefilli8native37.69343.195
ar_merged / stepi8native12.95614.991

The reduced free-running pipeline, including reference processing, AR, acoustic solving, streaming vocoder and host DSP/post-processing, had RTF 0.442386 median and 0.600927 maximum over 24 utterances under the same contended four-thread CPU conditions. Model loading and audio-file serialization were excluded. RTF is pipeline wall seconds divided by final audio seconds. The timed configuration uses native-int8 merged AR, wfp16 remaining graphs and the original fp32 host tables.

Minimal usage

These examples call only the speaker graph. The Python block uses zero-valued host mel as a runnable tensor-I/O smoke input; replace it with the normalized speaker log-mel [1,80,1001] from your own reference frontend for real use. Run from the repository root after installing the pinned environment in REPRODUCE.md.

python
from pathlib import Path
import numpy as np
from ai_edge_litert.compiled_model import (
    CompiledModel, Options, CpuOptions, HardwareAccelerator,
)

path = Path("fp32/sopro_speaker_encoder_fp32.tflite")
model = CompiledModel.from_file(str(path), options=Options(
    hardware_accelerators=HardwareAccelerator.CPU,
    cpu_options=CpuOptions(num_threads=4),
))
inputs = model.create_input_buffers(0)
outputs = model.create_output_buffers(0)
speaker_mel = np.zeros((1, 80, 1001), dtype=np.float32)
try:
    inputs[0].write(speaker_mel.ravel())  # args_0
    model.run_by_index(0, inputs, outputs)
    id_emb = np.array(outputs[0].read(192, np.float32)).reshape(1, 192)
    print(id_emb.shape, float(np.linalg.norm(id_emb)))  # output_0
finally:
    for buffer in inputs + outputs:
        buffer.destroy()
    model.close()

The Kotlin example follows the LiteRT Kotlin CompiledModel/TensorBuffer API shape. It is a design example, not an Android execution result; modelPath points to the downloaded fp32 speaker file, and hostMel is channel-major float32 speaker log-mel.

kotlin
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel

fun speakerEmbedding(modelPath: String, hostMel: FloatArray): FloatArray {
    require(hostMel.size == 80 * 1001)
    val model = CompiledModel.create(
        modelPath, CompiledModel.Options(Accelerator.CPU), null
    )
    val inputs = model.createInputBuffers()
    val outputs = model.createOutputBuffers()
    try {
        inputs[0].writeFloat(hostMel) // args_0: [1,80,1001]
        model.run(inputs, outputs)
        return outputs[0].readFloat().also { // output_0: id_emb
            require(it.size == 192)
        }
    } finally {
        inputs.forEach { it.close() }
        outputs.forEach { it.close() }
        model.close()
    }
}

Text examples and use

These plain-word sentences were used in the private test suite and contain no personal names or trademarks:

  • English: “The morning light fell across the garden while a small bird sang beside the open window.”
  • Portuguese: “A luz da manhã entrou pela janela e iluminou as flores que estavam sobre a mesa da cozinha.”
  • French: “Après la pluie, nous avons suivi le petit chemin qui descend vers le lac au milieu des arbres.”
  • German: “Nach dem Regen gingen wir den schmalen Weg entlang und hörten den Wind in den hohen Bäumen.”

Upstream disclaimer: “Please use the model for good: do not impersonate people.”

Conversion and verification code is included under conversion/. Supply your own authorized reference audio to reproduce speaker or full-pipeline checks. No private fixtures, reference audio, transcripts, oracle dumps or generated samples are included.