File size: 7,222 Bytes
ca21bed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73b0c0d
 
ca21bed
 
 
 
 
 
 
 
 
 
73b0c0d
 
 
ca21bed
 
 
73b0c0d
 
ca21bed
 
 
 
73b0c0d
ca21bed
 
 
 
 
 
 
73b0c0d
 
 
 
 
 
 
 
 
 
 
 
 
 
ca21bed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73b0c0d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
---
license: cc-by-4.0
language:
- en
library_name: coreml
pipeline_tag: automatic-speech-recognition
base_model:
- ConwayResearch/Underdog-Bark-3.0
- ConwayResearch/Underdog-Bark-3.0-Clean
tags:
- coreml
- apple-neural-engine
- speech-to-text
- asr
- parakeet
- underdog
- fluidaudio
- on-device
---

# Underdog Bark 3.0 · Core ML

Full Core ML pipelines (preprocessor, encoder, decoder, joint) for
[Underdog Bark 3.0](https://huggingface.co/ConwayResearch/Underdog-Bark-3.0) and
[Underdog Bark 3.0 Clean](https://huggingface.co/ConwayResearch/Underdog-Bark-3.0-Clean) by Conway Research, laid
out in the [FluidAudio](https://github.com/FluidInference/FluidAudio) Parakeet TDT v3 component layout so they run
end to end in Swift with the encoder on the Apple Neural Engine.

Underdog already ships a Core ML encoder for Bark 3.0 (with the preprocessor and decoder in Python), and Bark 3.0 Pro
as a full Core ML pipeline. This repo adds the rest of the pipeline for the small models, plus Bark 3.0 Clean, which
had no Core ML build.

| Folder | Model | Output |
|---|---|---|
| [`bark-3.0/`](bark-3.0) | Underdog Bark 3.0 | Verbatim: keeps "um", "uh", repeats |
| [`bark-3.0-clean/`](bark-3.0-clean) | Underdog Bark 3.0 Clean | Cleaned: drops fillers, false starts and repeated words |

English only. Tokenizer and output conventions (punctuation, casing, numerals) are Parakeet TDT v3's.

## Files (each folder)

| File | What it is |
|---|---|
| `Encoder.mlmodelc` | FastConformer encoder, 15 s window, **Neural Engine default** (sparse mask + 6-bit palette per 8 rows; 399 MB Bark 3.0, 410 MB Clean) |
| `Encoder_lut3.mlmodelc` | Same weights as a dense 3-bit palette per row (253 MB): **use this one on the GPU** (`.cpuAndGPU`) |
| `Decoder.mlmodelc` | 2-layer LSTM prediction network |
| `JointDecisionv3.mlmodelc` | TDT joint (token + duration), v3 interface |
| `Preprocessor.mlmodelc` | Parakeet TDT v3 mel front-end (Bark uses the original's) |
| `parakeet_vocab.json` | Parakeet TDT v3 vocabulary (8192 tokens) |
| `NOTICE` | Upstream attribution |

### How the encoder is encoded

Bark stores each encoder linear as per-row levels: 260 of the 264 matrices take five values per output row
`{0, ±a, ±b}`, and layer 23's four feed-forward matrices take seven. Core ML has no entropy-coded weight format, so
the weights are re-encoded as Core ML palettes (`constexpr_lut_to_sparse` + `constexpr_sparse_to_dense`, iOS 18):
the palette of every group of 8 output rows holds those rows' values, and each weight stores its index. Nothing is
re-quantized: every palettized weight reconstructs **bit-exactly** to the value in the upstream `model.bark` (240
matrices; the 24 positional projections act on the fixed positional table and are folded into constants at export).

Grouping 8 rows per palette matters on the Neural Engine, whose palette cost scales with the number of palettes:
per-row palettes run about 3× slower there. The GPU does not care, which is why `Encoder_lut3.mlmodelc` (per-row,
smallest dense file) is the GPU build; the sparse `Encoder.mlmodelc` is rebuilt on every GPU load (minutes), so do not
use it with `.cpuAndGPU`.

## Benchmarks

Apple M5 Pro, macOS 27, FluidAudio `asr-benchmark`, encoder on the Neural Engine, **full** LibriSpeech test sets,
corpus WER (total edits / total reference words, lowercase, punctuation stripped), RTFx = audio / processing time.

| Model | test-clean WER | test-other WER | RTFx clean / other |
|---|--:|--:|--:|
| Bark 3.0 (this repo) | — | 4.96 % | — / 140× |
| Bark 3.0 Clean (this repo) | 2.37 %¹ | 4.66 % | 155×¹ / 143× |
| Bark 3.0 Clean, upstream MLX (`bark_mlx.py`, fp16), same files and scorer | 2.39 % | — | 55× / — |
| Parakeet TDT v3 (FluidInference, reference) | 2.27 % | 4.12 % | 150× / 140× |

Long-form: a one-hour Earnings-22 recording (four concatenated calls) transcribes in **6.5 s (558×)** with Bark 3.0 and
7.0 s (514×) with Clean, models warm; Clean writes 7,787 words to Bark 3.0's 8,223 (fillers and repeats removed).

¹ Measured with a dense 6-bit-palette build of the same exact encoder weights; the shipped sparse `Encoder.mlmodelc`
matched it on test-other (4.66 % both, 143× vs 142×).

Notes:
- LibriSpeech references are verbatim and Clean removes words on purpose, so read speech is not where Clean shines;
  see the upstream cards for AMI, DisfluencySpeech and the other Open ASR sets.
- Bark 3.0 is evaluated upstream with 0.25 s of leading silence. Through FluidAudio's sliding-window pipeline it makes
  no difference (test-other 5.02 % with it vs 4.96 % without), so none is added.
- Bark models emit `<unk>` (token 0) where their training text had a quotation mark. Upstream decoders skip special
  tokens; FluidAudio drops it from the transcript as of [#1054](https://github.com/FluidInference/FluidAudio/pull/1054).
  On older releases strip it from `result.text` (`replacingOccurrences(of: "<unk>", with: "")`); the WER numbers above
  are scored without it.

## Usage (Swift, FluidAudio)

With [FluidAudio](https://github.com/FluidInference/FluidAudio) ([#1054](https://github.com/FluidInference/FluidAudio/pull/1054)
adds `AsrModelVersion.bark3` / `.bark3Clean`, which download the right folder):

```swift
import FluidAudio

let models = try await AsrModels.downloadAndLoad(version: .bark3)   // or .bark3Clean
let asr = AsrManager(config: .default)
try await asr.loadModels(models)
var state = TdtDecoderState.make(decoderLayers: await asr.decoderLayerCount)
let result = try await asr.transcribe(URL(fileURLWithPath: "meeting.wav"), decoderState: &state)
print(result.text)
```

```bash
swift run -c release fluidaudiocli transcribe meeting.wav --model-version bark3   # or bark3-clean
```

On a FluidAudio release without #1054, download one folder and load it with the v3 layout:

```bash
hf download FluidInference/bark-3.0-coreml --include "bark-3.0/*" --local-dir bark
```

```swift
let models = try AsrModels.loadLocal(from: URL(fileURLWithPath: "bark/bark-3.0"), version: .ultra)
```

The first load compiles the encoder for the Neural Engine (up to a minute); later loads are cached by Core ML.
Requires iOS 18 / macOS 15 or later. Not yet tested on iPhone.

## Credits and license

- **Underdog Bark 3.0 and Bark 3.0 Clean** by Conway Research ([Underdog](https://underdog.ai)), CC-BY-4.0.
- Derived from **nvidia/parakeet-tdt-0.6b-v3** (© NVIDIA Corporation, CC-BY-4.0) through
  **moondream/parakeet-ultra** (© Moondream, CC-BY-4.0).
- This repo: Core ML conversion and re-encoding by Fluid Inference. The encoder's linear layers are bit-exact to the
  upstream weights; the remaining tensors (subsampling, depthwise convs, norms, decoder, joint) are the upstream values
  converted to Core ML at fp16. `NOTICE` in each folder carries the upstream attribution. License: [CC-BY-4.0](LICENSE).

## Changelog

| Date | Files | Change | Issue | mobius PR | FluidAudio PR | HF commit |
|---|---|---|---|---|---|---|
| 2026-10-21 | `bark-3.0/*`, `bark-3.0-clean/*` | Initial release | — | — | [#1054](https://github.com/FluidInference/FluidAudio/pull/1054) | [`ca21bed2`](https://huggingface.co/FluidInference/bark-3.0-coreml/commit/ca21bed2ccd9c3fbcd104a6014b9a5638a764bc8) |