6 CoreML models (.mlpackage) split for step-by-step autoregressive generation:
Model
FP16
INT8
Purpose
Input
Output
audio_vae_encoder
82 MB
41 MB
Encode prompt audio to latents
[1, 1, 220500] (5s @ 44.1kHz)
[1, 64, T]
audio_vae_decoder
82 MB
41 MB
Decode latents to 44.1kHz audio
[1, 64, T] (flexible)
[1, 1, T*1764]
feat_encoder
228 MB
114 MB
Encode latent patches to LM embeddings
[1, 1, 4, 64]
[1, 1, 1024]
base_lm_step
696 MB
349 MB
Single AR step (24-layer LM + FSQ + stop)
embed + pos + 48 KV caches
lm_hidden + fsq + stop + 48 caches
residual_lm_step
236 MB
119 MB
Single AR step (8-layer residual LM)
embed + pos + 16 KV caches
res_hidden + 16 caches
locdit_step
237 MB
119 MB
Single Euler diffusion step
x, mu, t, cond, dt (batch=2 for CFG)
velocity [2, 64, 4]
Architecture
VoxCPM 1.5 is a tokenizer-free diffusion autoregressive TTS built on MiniCPM-4 (0.5B LM backbone). It generates 44.1kHz audio at 6.25 Hz token rate using flow matching diffusion.
Per-component correlation (CoreML vs PyTorch FP32)
Model
FP16
INT8
Notes
audio_vae_encoder
0.999989
0.999989
Fixed 5s input, Snake activations patched
audio_vae_decoder
0.999999
0.999999
Flexible latent length via RangeDim
feat_encoder
1.000000
1.000000
8-layer non-causal transformer
base_lm_step
0.999998
0.999998
24 layers, GQA patched, scatter-based KV cache
residual_lm_step
1.000000
1.000000
8 layers, same GQA/cache pattern
locdit_step
0.999999
0.999999
Flow matching estimator, cond_len=4
End-to-end verification (both variants produce identical results)
Language
Input
ASR output
Match
English
"Hello, this is a test of the voice cloning system."
"Hello, this is a test of the voice cloning system."
Exact
Chinese
"你好,这是一个语音克隆系统的测试。"
"你好这是一个语音克隆系统的测试"
Exact (minus punctuation)
Generation Pipeline
1. Encode prompt audio: latent = audio_vae_encoder(pad_to_5s(prompt))
2. Reshape into patches: [1, 64, T] → [1, n_patches, 4, 64]
3. Encode patches: feat_emb = feat_encoder(each patch)
4. Project: feat_lm = enc_to_lm_proj(feat_emb)
5. Embed text: text_emb = embed_tokens[token_ids] * scale_emb
6. Combine: [text_emb, audio_start_token, feat_lm] → [1, seq_len, 1024]
7. Prefill: step through all tokens via base_lm_step + residual_lm_step
8. Loop (autoregressive):
a. dit_hidden = lm_to_dit_proj(lm_hidden_fsq) + res_to_dit_proj(res_hidden)
b. noise = randn(1, 64, 4)
c. For t in 10 Euler steps (1.0 → 0.001):
vel = locdit_step(noise, dit_hidden, prefix_cond, t) # batch=2 for CFG
noise = noise - vel * dt
d. pred_feat = noise (after all steps)
e. If stop_head predicts stop and step > min_len: break
f. prefix_cond = pred_feat
g. next_emb = enc_to_lm_proj(feat_encoder(pred_feat))
h. lm_hidden, fsq, stop = base_lm_step(next_emb, pos, caches)
i. res_hidden = residual_lm_step(fsq + next_emb, pos, caches)
9. Decode: audio = audio_vae_decoder(concat(all pred_feats))
Performance
Measured on Apple Silicon (macOS, CPU_AND_GPU compute units, INT8):
Metric
Value
Prefill throughput
~27 tok/s
Generation throughput
~4.5 steps/s
Peak RAM
~3.8 GB
Output sample rate
44,100 Hz
Token rate
6.25 Hz (160ms per token)
Usage
Requirements
macOS 14+ on Apple Silicon
Python 3.10+ with coremltools and numpy
Quick start
python
1import coremltools as ct
2import numpy as np
34# Choose precision: "fp16" or "int8"5precision ="int8"67# Load models8vae_enc = ct.models.MLModel(f"{precision}/audio_vae_encoder.mlpackage")9vae_dec = ct.models.MLModel(f"{precision}/audio_vae_decoder.mlpackage")10feat_enc = ct.models.MLModel(f"{precision}/feat_encoder.mlpackage")11base_lm = ct.models.MLModel(f"{precision}/base_lm_step.mlpackage")12res_lm = ct.models.MLModel(f"{precision}/residual_lm_step.mlpackage")13locdit = ct.models.MLModel(f"{precision}/locdit_step.mlpackage")1415# Load shared constants16embed_tokens = np.load("constants/embed_tokens.npy")17# ... see generate_coreml.py for full pipeline
Full generation script
See generate_coreml.py in the conversion repo for a complete zero-PyTorch generation pipeline:
bash
1python generate_coreml.py \2 --text "Hello, this is a test."\3 --prompt prompt.wav \4 --prompt-text "This is the prompt transcript."\5 --output output.wav
Conversion Details
Converted using coremltools with Float16 compute precision (compute_units=CPU_AND_GPU). INT8 variant post-quantized via ct.optimize.coreml.linear_quantize_weights() (linear symmetric).