Instructions to use gyoom-sa/UVR-MDX-LiteRT with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use gyoom-sa/UVR-MDX-LiteRT with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
UVR MDX-Net β LiteRT (on-device vocal separation)
Two UVR MDX-Net vocal separators, converted to LiteRT (.tflite) as fp16 and made to
delegate whole to a mobile GPU β the models that power on-device vocal/instrumental separation in
the MusicStemSeparation Android app. This repo ships the models plus everything needed to run and
reproduce them:
separate.pyβ a self-contained, runnable Python reference (numpy STFT + LiteRT). WAV in βvocals+instrumentalWAV out.export/export_mdx_litert.pyβ the single, deterministic ONNXβLiteRT-fp16 export script (it reproduces the shipped.tflitebyte-for-byte).- A documented Android (Kotlin) integration recipe below β one
CompiledModel.create()call that picks NPU β GPU β CPU by itself.
Not int8.
base_model_relation: quantizedis HF's closest label; these are fp16 precision + format (ONNXβLiteRT) conversions of the original UVR MDX-Net weights, not integer-quantized models.
Models
| Model | Params | dim_f |
n_fft |
hop |
Size | Notes |
|---|---|---|---|---|---|---|
UVR_MDXNET_9482.fp16acc.tflite |
7.4 M | 2048 | 4096 | 1024 | ~28 MB | general, fast (default) |
UVR-MDX-NET-Voc_FT.fp16acc.tflite |
16.7 M | 3072 | 6144 | 1024 | ~64 MB | vocal fine-tune, higher quality |
Both run a native 256-frame segment (dim_t = 256, β 5.9 s @ 44.1 kHz). The model predicts the
vocal spectrogram; the instrumental is the free residual mix β vocals.
Quickstart
pip install -r requirements.txt # numpy + ai-edge-litert (WAV IO is stdlib)
# input must be WAV β convert first if needed:
ffmpeg -i example.mp3 example.wav
python separate.py example.wav # 9482 (default) β example_vocals.wav + example_instrumental.wav
python separate.py example.wav --model voc_ft # higher-quality model
python separate.py example.wav --model voc_ft --denoise --out-dir out/
separate.py runs the model on the CPU (via ai-edge-litert/XNNPACK) β a correct, portable
reference. The fp16 speedup and the GPU/NPU path are Android-only (see below).
Model I/O contract (the one thing to get right)
input = OUTPUT = float32 tensor [1, 4, dim_f, 256] (NCHW)
The 4 channels are complex-as-channels, plane order [L_re, L_im, R_re, R_im]
(real plane = 2Β·c, imag plane = 2Β·c + 1), flat row-major index ((planeΒ·dim_f) + bin)Β·256 + frame.
- The STFT keeps bins
[0, dim_f)of then_fft/2 + 1one-sided bins β i.e. the Nyquist bin is dropped (dim_f = n_fft/2); on the inverse it is zero-padded back. - Voc FT carries no embedded STFT metadata β derive
n_fft = dim_f Β· 2. - A wrong packing (swapped re/im planes, a kept Nyquist bin, a stray
1/βn) produces plausible-but-wrong audio, not an error. If a stem sounds off, look here first.
How it works β STFT outside the graph
The .tflite is the learned core only β a convolutional U-Net that maps a spectrogram to the vocal
spectrogram. The STFT, chunking, overlap-add and inverse STFT are ordinary CPU code (numpy in
separate.py, Kotlin in the app). Per chunk:
STFT (host CPU) β inference (LiteRT: CPU / GPU / NPU) β iSTFT (host CPU) β weighted overlap-add
This is a faithful reimplementation of UVR's mdx.py demix: periodic Hann window, center=True reflect
padding, unnormalized (torch.stft(normalized=False)), a fixed 10% crossfade between chunks
(set to 0 for the classic hard-tiled concat), and the free instrumental residual. Because the accelerator
only ever sees the learned core, the model delegates cleanly and every stage is independently timed.
Android on-device inference
One call. LiteRT tries NPU, then GPU, then CPU, and keeps whatever works.
Gradle
dependencies {
implementation("com.google.ai.edge.litert:litert:2.1.6") // GPU accelerator .so ships inside this AAR
}
android {
androidResources { noCompress += "tflite" } // no inflate when copying the asset out
// ABIs: arm64-v8a (+ x86_64 for the emulator)
}
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.BuiltinNpuAcceleratorProvider
import com.google.ai.edge.litert.CompiledModel
import com.google.ai.edge.litert.Environment
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val model = CompiledModel.create(
tflitePath, // copy the asset to filesDir once, pass that path β see gotchas
CompiledModel.Options(Accelerator.NPU, Accelerator.GPU, Accelerator.CPU).apply {
gpuOptions = CompiledModel.GpuOptions(precision = CompiledModel.GpuOptions.Precision.FP16)
cpuOptions = CompiledModel.CpuOptions(numThreads = bigCoreCount) // defaults to 1 β always set it
},
env,
)
val inputs = model.createInputBuffers()
val outputs = model.createOutputBuffers()
// per chunk:
inputs[0].writeFloat(chunkFloats) // 1*4*dim_f*256, [L_re, L_im, R_re, R_im]
model.run(inputs, outputs)
val vocals = outputs[0].readFloat()
Run one warm-up inference on a zeroed input, and retry with Options.CPU if it throws β once the model
is compiled LiteRT won't fall back for you.
Gotchas
numThreadsdefaults to 1. The CPU rung is XNNPACK, fp16 flag and all, so it's a fine floor β but only if you set the thread count.- Pass a file path, not
context.assets: the assets overload copies the whole model onto the heap (~128 MB peak for Voc FT). - Nothing tells you which accelerator you actually got. Grep logcat for
Replacing N out of M node(s) with delegate.
NPU is work, not a free rung: only SM8550/8650/8750/8850, listed MediaTek SoCs and Tensor G3βG6
qualify, and you ship the Qualcomm/MediaTek libraries yourself (20β110 MB per device, install-time Play
Feature Delivery). Untested here; leaving Accelerator.NPU in the set is harmless on everything else.
Why LiteRT β the acceleration story
MDX-Net delegates whole. It is a clean single-domain conv U-Net β no attention, no LSTM, no whole-tensor norm β which is exactly the op set XNNPACK, the GPU (ML Drift / OpenCL) and (on capable SoCs) the NPU map end-to-end. With the STFT pulled out of the graph, the accelerator sees only convs, transposed-convs and the TDF fully-connected blocks, so the whole graph delegates in one partition.
fp16 is the ~2Γ lever and it's safe here. fp16 (not int8) is the quality-safe speedup, but only for a model with no whole-tensor reduction to overflow the fp16 ceiling (65504). MDX has only per-channel BatchNorm, which folds into the conv β nothing overflows:
| Model | peak activation | fp16 ceiling | headroom |
|---|---|---|---|
| 9482 | 559 | 65504 | 117Γ |
| Voc FT | 1384 | 65504 | 47Γ |
fp16 is requested via the .tflite metadata flag reduced_precision_support = "fp16accfp16" (the fast
accumulate value β measured quality-safe for MDX), which lets XNNPACK run the delegated ops in fp16 on
ARMv8.2-FP16 cores.
The GPU fix β un-fuse TRANSPOSE_CONV's ReLU
The converter fuses each decoder ConvTranspose + BN + ReLU into one TRANSPOSE_CONV op with a fused
ReLU β and a fused activation is precisely what tags the op v4. LiteRT 2.1.6's OpenCL delegate caps
TRANSPOSE_CONV at v3, whose semantics have no fused activation. That lone v4 op therefore fails
GPU-only compilation outright β and with a CPU rung in the set, silently strands all five decoder
upsamplers on the CPU.
The export un-fuses it: each TRANSPOSE_CONV(+fused ReLU) becomes a legitimately-v3 bias-only
TRANSPOSE_CONV followed by a standalone RELU op (which the GPU supports). The whole graph then
delegates in one partition and computes the right function. (Simply force-lowering the v4 tag to v3
instead β the earlier attempt β made the GPU accept the graph but silently drop the ReLU on all five
decoder upsamplers, producing an audible broadband buzz.) CPU numerics are unchanged.
Reproducing the export
The two .tflite were produced by export/export_mdx_litert.py, and the export is deterministic β
re-running it from the source ONNX yields the shipped files byte-for-byte (verified via SHA-256).
# heavy converter env (Python 3.11):
pip install torch==2.9.1 --index-url https://download.pytorch.org/whl/cpu
pip install -r export/requirements.txt
python export/export_mdx_litert.py UVR_MDXNET_9482.onnx UVR_MDXNET_9482.fp16acc.tflite
python export/export_mdx_litert.py UVR-MDX-NET-Voc_FT.onnx UVR-MDX-NET-Voc_FT.fp16acc.tflite
Six gated steps: onnx2torch β SNR-gate (>100 dB vs ONNX Runtime) β litert_torch export (NCHW, static dim_t=256) β +fp16 metadata β un-fuse TRANSPOSE_CONV ReLU β verify (CPU SNR, output shape, no op above the GPU v3 cap). Source ONNX: the UVR MDX-Net releases from
TRvlvr/model_repo, also on the Hub at
Politrees/UVR_resources.
onnx2tfdoes not work here β its per-op NCHWβNHWC guessing breaks the TDF MatMuls. The PyTorch β StableHLO β TFLite route (litert_torch) does no layout guessing and keeps NCHW I/O, so the app's complex-as-channels packing feeds the model directly.
Fidelity & performance
- Fidelity: the fp16
.tfliteis gated at > 100 dB CPU SNR vs the ONNX reference; measured β 109 dB (9482) and β 112 dB (Voc FT) after the un-fuse step. Validated by ear on-device. - Performance (one measured device β Xiaomi Redmi Note 10 Pro, Snapdragon 732G / Adreno 618, Android
13): XNNPACK-fp16 runs the graph at RTF β 1.0 (roughly real-time) as the universal CPU floor;
the GPU path (whole-graph
LITERT_CLpartition) runs clean and faster than XNNPACK after the un-fuse fix. Numbers vary by SoC; treat these as one data point, not a benchmark. - NPU: the rung costs one word in the accelerator set (
Accelerator.NPU), but it is hardware-gated (SM8550/8650/8750/8850, listed MediaTek SoCs, Tensor G3βG6) and needs ~20β110 MB of vendor libraries shipped in the APK. Deferred/untested β see above.
Attribution & license
Released under the MIT License, matching the upstream UVR MDX-Net models.
- UVR MDX-Net and the 9482 model β the Ultimate Vocal Remover project by Anjok07 and contributors.
- UVR-MDX-NET Voc FT β vocal fine-tune by Kimberley Jensen.
- Source ONNX via TRvlvr/model_repo (Hub mirror: Politrees/UVR_resources).
This repo's contribution: the ONNX β LiteRT fp16 conversion, the whole-graph GPU fix (un-fusing
TRANSPOSE_CONV's ReLU), the runnable separate.py reference, and the Android integration recipe. The
weights are the original UVR models, format-converted.
- Downloads last month
- 161
Model tree for gyoom-sa/UVR-MDX-LiteRT
Base model
Politrees/UVR_resources