chua commited on
Commit
b337fe7
·
verified ·
1 Parent(s): d2d8268

Prepare Audio8-TTS-Realistic-0.6B private release

Browse files

Complete inference package, model card, multilingual evaluation, and verified SGLang Omni compatibility.

.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
NOTICE ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ audio8_tts
2
+
3
+ The model architecture is inspired by the DualAR design used in Fish Audio
4
+ S2 Pro.
README.md CHANGED
@@ -1,3 +1,306 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ language:
4
+ - zh
5
+ - en
6
+ - ja
7
+ - ko
8
+ - de
9
+ - fr
10
+ - es
11
+ - it
12
+ - ru
13
+ - ar
14
+ - fi
15
+ - hi
16
+ - id
17
+ - ms
18
+ - nl
19
+ - "no"
20
+ - pl
21
+ - pt
22
+ - sv
23
+ - th
24
+ - tr
25
+ - vi
26
+ - yue
27
+ library_name: transformers
28
+ pipeline_tag: text-to-speech
29
+ base_model: Edge0/Audio8-TTS-Preview-0.6b
30
+ tags:
31
+ - audio
32
+ - text-to-speech
33
+ - tts
34
+ - voice-cloning
35
+ - zero-shot
36
+ - multilingual
37
  ---
38
+
39
+ <div align="center">
40
+
41
+ <img src="./audio8-banner.jpeg" alt="Audio8" width="760">
42
+
43
+ # Audio8-TTS-Realistic-0.6B
44
+
45
+ **More realistic speech. More languages. Still 0.6B.**
46
+
47
+ [![GitHub](https://img.shields.io/badge/GitHub-Audio8__TTS-black?style=for-the-badge&logo=github)](https://github.com/Edge0-AI/Audio8_TTS)
48
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue?style=for-the-badge)](./LICENSE)
49
+ ![Demo — coming soon](https://img.shields.io/badge/Demo-Coming%20soon-lightgrey?style=for-the-badge&logo=githubpages)
50
+
51
+ </div>
52
+
53
+ **Audio8-TTS-Realistic-0.6B** builds on [Audio8-TTS-Preview-0.6b](https://huggingface.co/Edge0/Audio8-TTS-Preview-0.6b), delivering **substantially improved speech realism** while retaining its compact **0.6B** architecture and zero-shot voice cloning capabilities.
54
+
55
+ This release supports **22 languages plus Cantonese**, adding **12 languages** to Preview's coverage. It uses the same Transformers inference interface as Preview. This repository includes the complete model checkpoint, its 44.1 kHz neural audio codec, tokenizer, processor, and custom Transformers code.
56
+
57
+ ## Highlights
58
+
59
+ - **Improved realism:** more natural prosody and expressive delivery, building on Preview.
60
+ - **Broader multilingual coverage:** 22 languages plus Cantonese, with 12 newly supported languages.
61
+ - **Compact model:** 601 million main-model parameters, with the audio codec bundled separately.
62
+ - **Flexible generation:** zero-shot voice cloning from a reference recording and transcript, or speech generation without a reference.
63
+ - **GPU serving:** an SGLang Omni adapter with paged attention, dynamic batching, and an OpenAI-compatible speech API.
64
+
65
+ ## Supported Languages
66
+
67
+ | Coverage | Languages |
68
+ |---|---|
69
+ | Retained from Preview | Chinese, Dutch, English, French, German, Italian, Japanese, Korean, Polish, Spanish |
70
+ | Newly supported | Arabic, Finnish, Hindi, Indonesian, Malay, Norwegian, Portuguese, Russian, Swedish, Thai, Turkish, Vietnamese |
71
+ | Also retained | Cantonese |
72
+
73
+ Language quality varies; see the per-language evaluation below. Benchmark coverage includes some additional languages and is distinct from the supported-language list.
74
+
75
+ ## Model Details
76
+
77
+ Audio8 TTS uses a DualAR architecture inspired by [Fish Audio S2 Pro](https://github.com/fishaudio/fish-speech). The slow AR transformer predicts one semantic token for each audio frame. The fast AR transformer predicts the frame's codec codebooks, conditioned on the slow hidden state and preceding codebooks.
78
+
79
+ | Component | Configuration |
80
+ |---|---|
81
+ | Main model | 601,159,424 parameters, excluding the codec |
82
+ | Slow AR | 24 layers, width 896, 14 attention heads, 2 KV heads |
83
+ | Fast AR | 4 layers, width 896, 14 attention heads, 2 KV heads |
84
+ | Acoustic tokens | 10 codebooks, 4,096 entries per codebook |
85
+ | Codec | 44.1 kHz, 2,048 samples per model frame (~21.5 frames/s) |
86
+ | Context | Up to 2,048 packed text/audio positions |
87
+
88
+ The bundled codec handles both reference-audio encoding and waveform decoding, so no additional codec checkpoint is required. The **0.6B** size refers to the main DualAR model; the codec is excluded from that parameter count.
89
+
90
+ ## Installation
91
+
92
+ Python 3.10 or newer and a CUDA-capable GPU are recommended.
93
+
94
+ ```bash
95
+ pip install "torch>=2.5.0" "torchaudio>=2.5.0" \
96
+ "transformers>=4.57.0,<5" "soundfile>=0.12" "safetensors>=0.4"
97
+ ```
98
+
99
+ The Transformers release package was smoke-tested with PyTorch 2.8.0 and Transformers 4.57.3 on an NVIDIA H20 using BF16. Checks covered model loading, English and Chinese generation with and without reference audio, and generation in Arabic, Cantonese, Portuguese, and Russian. These are runtime checks, separate from the benchmark results below.
100
+
101
+ ## Usage
102
+
103
+ The model uses custom Transformers code. Review the files in this repository, then load it with `trust_remote_code=True`.
104
+
105
+ ### Zero-shot voice cloning
106
+
107
+ Use a clean reference recording. Its transcript must match the spoken content exactly.
108
+
109
+ ```python
110
+ import soundfile as sf
111
+ import torch
112
+ from transformers import AutoModel, AutoProcessor
113
+
114
+ model_id = "Edge0/Audio8-TTS-Realistic-0.6b"
115
+ device = "cuda" if torch.cuda.is_available() else "cpu"
116
+ dtype = torch.bfloat16 if device == "cuda" else torch.float32
117
+
118
+ processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
119
+ model = AutoModel.from_pretrained(
120
+ model_id,
121
+ trust_remote_code=True,
122
+ dtype=dtype,
123
+ ).eval().to(device)
124
+
125
+ inputs = processor(
126
+ text=["Welcome to Audio8 TTS. It is wonderful to hear from you."],
127
+ reference_audio=["reference.wav"],
128
+ reference_text=["The exact transcript of the reference recording."],
129
+ return_tensors="pt",
130
+ )
131
+ inputs = {name: value.to(device) for name, value in inputs.items()}
132
+
133
+ with torch.inference_mode():
134
+ output = model.generate(
135
+ **inputs,
136
+ max_new_tokens=1024,
137
+ temperature=0.8,
138
+ top_p=0.95,
139
+ top_k=50,
140
+ do_sample=True,
141
+ return_dict_in_generate=True,
142
+ )
143
+ waveforms, waveform_lengths = model.decode_audio(output.codes)
144
+
145
+ audio = waveforms[0, : int(waveform_lengths[0])].float().cpu().numpy()
146
+ sf.write("output.wav", audio, model.config.codec_sample_rate)
147
+ ```
148
+
149
+ ### Generation without a reference
150
+
151
+ Replace the processor call above with:
152
+
153
+ ```python
154
+ inputs = processor(
155
+ text=["This utterance does not use a reference voice."],
156
+ return_tensors="pt",
157
+ )
158
+ ```
159
+
160
+ Then move the inputs to the model device and run the same generation and decoding steps. For command-line inference, batching, and supervised fine-tuning, see the [Audio8 TTS repository](https://github.com/Edge0-AI/Audio8_TTS), using `Edge0/Audio8-TTS-Realistic-0.6b` as the model path.
161
+
162
+ ## High-throughput serving: SGLang Omni
163
+
164
+ The [SGLang Omni adapter](https://github.com/Edge0-AI/Audio8_TTS/tree/master/sglang_omni) provides GPU serving for Audio8 TTS. It is installed as an independent model plugin and does not overwrite SGLang Omni core files.
165
+
166
+ | Capability | Support |
167
+ |---|---|
168
+ | Attention and batching | SGLang paged attention and dynamic batching |
169
+ | DualAR execution | Slow AR serving with a fixed KV cache for the Fast AR codebook decoder |
170
+ | Voice cloning | Reference-audio encoding and waveform decoding |
171
+ | API | OpenAI-compatible `/v1/audio/speech` service |
172
+
173
+ The adapter targets SGLang Omni revision `68a572348837f7b004857b4b07993c20ade4c017`, with PyTorch 2.9.1, SGLang 0.5.8, and Transformers 4.57.1. Follow the [deployment instructions](https://github.com/Edge0-AI/Audio8_TTS#sglang-omni-serving) for installation, configuration, and API examples, and set the model path to `Edge0/Audio8-TTS-Realistic-0.6b` or its downloaded snapshot. The adapter supports generation without a reference and zero-shot voice cloning.
174
+
175
+ Use the **0.6B** configuration, `sglang_omni/configs/audio8_tts_0_6b.yaml`. This Realistic checkpoint passed non-streaming `/v1/audio/speech` smoke tests both with and without a reference on an NVIDIA H20, using the versions above and the adapter from Audio8_TTS commit `07e40f5d0b03fc473635ef378654bfb581027ac3`. These checks used BF16 with Torch compilation and CUDA graphs disabled; they validate compatibility, not throughput or streaming performance.
176
+
177
+ ## Evaluation
178
+
179
+ Error rates are percentages (**lower is better**). Speaker similarity (SIM) is on a **0–1 scale** (**higher is better**). These metrics measure intelligibility and speaker similarity; they do not directly measure perceived speech realism.
180
+
181
+ ### Benchmark summary
182
+
183
+ Results for **Audio8-TTS-Realistic (0.6B)**:
184
+
185
+ | Benchmark | Coverage | Mean error % ↓ | Mean SIM ↑ |
186
+ |---|---|---:|---:|
187
+ | Seed-TTS | English / Chinese | **1.25** | **0.704** |
188
+ | CV3 | 9 core languages | **4.86** | **0.661** |
189
+ | MiniMax | 24 evaluated languages | **5.89** | **0.754** |
190
+
191
+ All three summary error rates are unweighted language macro averages. Seed-TTS averages English WER and Chinese CER, excluding hard Chinese. CV3 averages Chinese, English, Japanese, Korean, German, French, Spanish, Italian, and Russian, excluding hard subsets and the 13 additional languages. MiniMax averages all 24 evaluated languages, including languages outside the supported-language list.
192
+
193
+ ### Reference comparison
194
+
195
+ The following error rates come from the sources used in our technical report. Different evaluation pipelines and text normalization make this a reference comparison rather than a strictly matched ranking. A dash means that a result is unavailable in the comparison, not that the model lacks support.
196
+
197
+ | Model | Parameters (B) | Seed-TTS EN/ZH error ↓ | CV3 (9) error ↓ | MiniMax (24) error ↓ | Result source |
198
+ |---|---:|---:|---:|---:|---|
199
+ | **Audio8-TTS-Realistic** | **0.6** | **1.25** | **4.86** | **5.89** | Our evaluation |
200
+ | Qwen3-TTS-12Hz-0.6B | 0.6 | 1.12 | — | — | Qwen report |
201
+ | Qwen3-TTS-1.7B | 1.7 | 1.01 | 7.73 | 27.41 | Qwen report / Boson evaluation |
202
+ | Higgs Audio v3 | 4 | 1.11 | 4.41 | 2.74 | Boson evaluation |
203
+ | Higgs Audio v2 | 4.7 | 1.17 | 4.49 | — | Our reevaluation |
204
+ | Fish S2 Pro | 4.6 | 1.32 | 4.49 | 4.40 | Our reevaluation / Fish report |
205
+ | MOSS-TTS-v1.5 | 8.5 | 1.52 | 6.11 | 3.78 | VoxCPM compilation / Boson evaluation |
206
+ | CosyVoice3-1.5B | 1.5 | 1.67 | 6.89 | — | VoxCPM compilation |
207
+ | VoxCPM2 | 2.3 | 1.41 | 5.35 | — | VoxCPM compilation |
208
+
209
+ Sources: [Qwen3-TTS report](https://arxiv.org/abs/2601.15621), [Boson AI Higgs Audio v3 benchmarks](https://huggingface.co/bosonai/higgs-audio-v3-tts-4b), [Fish Audio S2 report](https://arxiv.org/abs/2603.08823), and the [VoxCPM repository](https://github.com/OpenBMB/VoxCPM). Fish S2 Pro was reevaluated on Seed-TTS and CV3 because its official evaluation uses a different normalizer; Higgs Audio v2 was evaluated locally. Cross-project SIM values are omitted here because speaker encoders and evaluation conventions differ.
210
+
211
+ Parameter conventions also differ: Audio8's 0.6B excludes its separate codec; Qwen and CosyVoice use release labels; Higgs Audio v3 uses its nominal **4B** release label. The remaining sizes are rounded checkpoint counts used in our report (VoxCPM2 excludes its separate AudioVAE). They should not be interpreted as identically counted end-to-end system sizes.
212
+
213
+ ### Seed-TTS detail
214
+
215
+ | Split | Metric | Error % ↓ | SIM ↑ |
216
+ |---|---|---:|---:|
217
+ | English | WER | 1.464 | 0.663 |
218
+ | Chinese | CER | 1.029 | 0.745 |
219
+ | Chinese, hard | CER | 12.797 | 0.702 |
220
+
221
+ ### Multilingual detail
222
+
223
+ <details>
224
+ <summary>CV3: 22 languages and hard subsets</summary>
225
+
226
+ | Language | Metric | Error % ↓ | SIM ↑ |
227
+ |---|---|---:|---:|
228
+ | Chinese | CER | 1.814 | 0.6898 |
229
+ | English | WER | 2.907 | 0.6295 |
230
+ | Japanese | CER | 7.511 | 0.6659 |
231
+ | Korean | CER | 4.801 | 0.6962 |
232
+ | German | WER | 3.200 | 0.6669 |
233
+ | French | WER | 7.938 | 0.6355 |
234
+ | Spanish | WER | 3.389 | 0.6680 |
235
+ | Italian | WER | 4.933 | 0.6498 |
236
+ | Russian | WER | 7.258 | 0.6451 |
237
+ | Arabic | WER | 21.098 | 0.6122 |
238
+ | Finnish | WER | 32.279 | 0.7028 |
239
+ | Hindi | WER | 21.714 | 0.7245 |
240
+ | Indonesian | WER | 13.743 | 0.6682 |
241
+ | Malay | WER | 17.798 | 0.6710 |
242
+ | Dutch | WER | 10.873 | 0.6918 |
243
+ | Norwegian | WER | 17.961 | 0.6905 |
244
+ | Polish | WER | 19.233 | 0.6738 |
245
+ | Portuguese | WER | 6.871 | 0.6772 |
246
+ | Swedish | WER | 19.252 | 0.7104 |
247
+ | Thai | WER | 48.574 | 0.6691 |
248
+ | Turkish | WER | 12.567 | 0.6496 |
249
+ | Vietnamese | WER | 28.111 | 0.6790 |
250
+
251
+ Hard Chinese CER is **10.665%** and hard English WER is **4.736%**. These hard subsets are excluded from the 9-language summary. Each of the 22 regular language rows contains 500 evaluation items. Chinese, Japanese, and Korean use CER; the other regular language rows use WER.
252
+
253
+ </details>
254
+
255
+ <details>
256
+ <summary>MiniMax: all 24 evaluated languages</summary>
257
+
258
+ | Language | Metric | Error % ↓ | SIM ↑ |
259
+ |---|---|---:|---:|
260
+ | Arabic | CER | 2.403 | 0.7040 |
261
+ | Cantonese | CER | 31.349 | 0.7710 |
262
+ | Chinese | CER | 1.112 | 0.7950 |
263
+ | Czech † | WER | 8.628 | 0.7330 |
264
+ | Dutch | WER | 1.715 | 0.7050 |
265
+ | English | WER | 2.161 | 0.7740 |
266
+ | Finnish | WER | 5.072 | 0.7910 |
267
+ | French | WER | 3.198 | 0.6660 |
268
+ | German | WER | 0.898 | 0.7510 |
269
+ | Greek † | CER | 4.181 | 0.7910 |
270
+ | Hindi | CER | 9.209 | 0.8170 |
271
+ | Indonesian | WER | 2.128 | 0.7270 |
272
+ | Italian | WER | 1.958 | 0.7370 |
273
+ | Japanese | CER | 5.142 | 0.7890 |
274
+ | Korean | CER | 3.731 | 0.7850 |
275
+ | Polish | WER | 1.957 | 0.7890 |
276
+ | Portuguese | WER | 2.234 | 0.7860 |
277
+ | Romanian † | WER | 17.425 | 0.7330 |
278
+ | Russian | WER | 3.071 | 0.7490 |
279
+ | Spanish | WER | 1.350 | 0.7450 |
280
+ | Thai | CER | 6.674 | 0.7760 |
281
+ | Turkish | WER | 1.560 | 0.7650 |
282
+ | Ukrainian † | WER | 16.343 | 0.6940 |
283
+ | Vietnamese | CER | 7.966 | 0.7120 |
284
+
285
+ Each language has 100 evaluation items. Rows marked † are evaluated for completeness and are outside this release's supported-language list. The metric column preserves the evaluation suite's language-specific WER/CER choice. Cantonese is included in the 24-language average.
286
+
287
+ </details>
288
+
289
+ ### Compared with Preview
290
+
291
+ In the same archived MiniMax evaluation pipeline, the 24-language mean error rate decreased from **29.637%** for Preview to **5.894%** for Realistic, while mean SIM increased from **0.6932** to **0.7535**. This is an average over the full benchmark, including languages beyond the supported list; improvements are not uniform across every language and split. For example, ordinary Seed-TTS Chinese CER was **0.943% → 1.029%** in that same evaluation series. Preview values in its original public model card were measured in an earlier evaluation and should not be mixed with this comparison.
292
+
293
+ The main release goal is improved speech realism alongside expanded language coverage. WER/CER and SIM alone do not establish a human preference score.
294
+
295
+ ## Limitations and Responsible Use
296
+
297
+ - Quality varies by language and evaluation set. The detailed results include difficult cases, including Cantonese and some newly supported languages; the support list is not a claim of equal quality across languages.
298
+ - Noisy reference audio or mismatched transcripts can reduce stability and speaker similarity. The reference audio, transcript, and target text share a finite context budget; split long passages into manageable utterances.
299
+ - Speech may contain pronunciation errors, repetitions, omissions, or early stopping. Listen to generated audio before using it in a finished product.
300
+ - Obtain permission to clone a voice and disclose synthetic audio where appropriate.
301
+
302
+ ## License and Acknowledgements
303
+
304
+ The code and model weights are released under the [Apache License 2.0](./LICENSE). See [NOTICE](./NOTICE) for attribution details.
305
+
306
+ We thank the Fish Audio team for publishing the DualAR architecture used in Fish Audio S2 Pro.
audio8-banner.jpeg ADDED
codec.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c310505aa11fe2f6cc63b8d3130dc7e77e73227774f5c62575769b1f47a8d048
3
+ size 1349857559
config.json ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "ArkttsModel"
4
+ ],
5
+ "attention_o_bias": false,
6
+ "attention_qk_norm": false,
7
+ "attention_qkv_bias": true,
8
+ "auto_map": {
9
+ "AutoConfig": "configuration_arktts.ArkttsConfig",
10
+ "AutoModel": "modeling_arktts.ArkttsModel",
11
+ "AutoModelForCausalLM": "modeling_arktts.ArkttsModel"
12
+ },
13
+ "codebook_size": 4096,
14
+ "codec_filename": "codec.pth",
15
+ "codec_frame_size": 2048,
16
+ "codec_post_intermediate_size": 1216,
17
+ "codec_post_n_head": 16,
18
+ "codec_post_n_layer": 8,
19
+ "codec_post_n_local_heads": 8,
20
+ "codec_sample_rate": 44100,
21
+ "dim": 896,
22
+ "dropout": 0.0,
23
+ "dtype": "bfloat16",
24
+ "eos_token_id": 151645,
25
+ "fast_attention_o_bias": false,
26
+ "fast_attention_qk_norm": false,
27
+ "fast_attention_qkv_bias": false,
28
+ "fast_dim": 896,
29
+ "fast_head_dim": 64,
30
+ "fast_intermediate_size": 4864,
31
+ "fast_n_head": 14,
32
+ "fast_n_local_heads": 2,
33
+ "head_dim": 64,
34
+ "hidden_size": 896,
35
+ "initializer_range": 0.02,
36
+ "intermediate_size": 4864,
37
+ "max_position_embeddings": 2048,
38
+ "max_seq_len": 2048,
39
+ "model_type": "arktts",
40
+ "n_fast_layer": 4,
41
+ "n_head": 14,
42
+ "n_layer": 24,
43
+ "n_local_heads": 2,
44
+ "norm_eps": 1e-06,
45
+ "norm_fastlayer_input": true,
46
+ "num_attention_heads": 14,
47
+ "num_codebooks": 10,
48
+ "num_hidden_layers": 24,
49
+ "num_key_value_heads": 2,
50
+ "pad_token_id": 151643,
51
+ "ras_temperature": 1.0,
52
+ "ras_top_p": 0.9,
53
+ "ras_window_size": 10,
54
+ "rms_norm_eps": 1e-06,
55
+ "rope_base": 1000000.0,
56
+ "rope_theta": 1000000.0,
57
+ "scale_codebook_embeddings": false,
58
+ "semantic_begin_id": 151678,
59
+ "semantic_end_id": 155773,
60
+ "transformers_version": "4.57.3",
61
+ "use_gradient_checkpointing": false,
62
+ "vocab_size": 155776,
63
+ "tie_word_embeddings": true
64
+ }
configuration_arktts.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ class ArkttsConfig(PretrainedConfig):
7
+ model_type = "arktts"
8
+
9
+ def __init__(
10
+ self,
11
+ vocab_size: int = 155776,
12
+ dim: int = 896,
13
+ n_layer: int = 24,
14
+ n_head: int = 14,
15
+ n_local_heads: int = 2,
16
+ head_dim: int = 64,
17
+ intermediate_size: int = 4864,
18
+ max_seq_len: int = 2048,
19
+ rope_base: float = 1_000_000,
20
+ norm_eps: float = 1e-6,
21
+ dropout: float = 0.0,
22
+ attention_qkv_bias: bool = True,
23
+ attention_qk_norm: bool = False,
24
+ attention_o_bias: bool = False,
25
+ tie_word_embeddings: bool = True,
26
+ codebook_size: int = 4096,
27
+ num_codebooks: int = 10,
28
+ semantic_begin_id: int = 151678,
29
+ semantic_end_id: int = 155773,
30
+ n_fast_layer: int = 4,
31
+ fast_dim: int = 896,
32
+ fast_n_head: int = 14,
33
+ fast_n_local_heads: int = 2,
34
+ fast_head_dim: int = 64,
35
+ fast_intermediate_size: int = 4864,
36
+ fast_attention_qkv_bias: bool = False,
37
+ fast_attention_qk_norm: bool = False,
38
+ fast_attention_o_bias: bool = False,
39
+ norm_fastlayer_input: bool = True,
40
+ initializer_range: float = 0.02,
41
+ use_gradient_checkpointing: bool = False,
42
+ codec_filename: str = "codec.pth",
43
+ codec_sample_rate: int = 44100,
44
+ codec_frame_size: int = 2048,
45
+ codec_post_n_layer: int = 8,
46
+ codec_post_n_head: int = 16,
47
+ codec_post_n_local_heads: int = 8,
48
+ codec_post_intermediate_size: int = 1216,
49
+ ras_window_size: int = 10,
50
+ ras_temperature: float = 1.0,
51
+ ras_top_p: float = 0.9,
52
+ eos_token_id: int = 151645,
53
+ pad_token_id: int = 151643,
54
+ **kwargs,
55
+ ):
56
+ kwargs.pop("model_type", None)
57
+ kwargs.pop("audio_embed_dim", None)
58
+ kwargs.pop("is_reward_model", None)
59
+ super().__init__(
60
+ eos_token_id=eos_token_id,
61
+ pad_token_id=pad_token_id,
62
+ tie_word_embeddings=tie_word_embeddings,
63
+ **kwargs,
64
+ )
65
+ self.vocab_size = int(vocab_size)
66
+ self.dim = int(dim)
67
+ self.hidden_size = self.dim
68
+ self.n_layer = int(n_layer)
69
+ self.num_hidden_layers = self.n_layer
70
+ self.n_head = int(n_head)
71
+ self.num_attention_heads = self.n_head
72
+ self.n_local_heads = int(n_local_heads)
73
+ self.num_key_value_heads = self.n_local_heads
74
+ self.head_dim = int(head_dim)
75
+ self.intermediate_size = int(intermediate_size)
76
+ self.max_seq_len = int(max_seq_len)
77
+ self.max_position_embeddings = self.max_seq_len
78
+ self.rope_base = float(rope_base)
79
+ self.rope_theta = self.rope_base
80
+ self.norm_eps = float(norm_eps)
81
+ self.rms_norm_eps = self.norm_eps
82
+ self.dropout = float(dropout)
83
+ self.attention_qkv_bias = bool(attention_qkv_bias)
84
+ self.attention_qk_norm = bool(attention_qk_norm)
85
+ self.attention_o_bias = bool(attention_o_bias)
86
+ self.codebook_size = int(codebook_size)
87
+ self.num_codebooks = int(num_codebooks)
88
+ self.semantic_begin_id = int(semantic_begin_id)
89
+ self.semantic_end_id = int(semantic_end_id)
90
+ self.n_fast_layer = int(n_fast_layer)
91
+ self.fast_dim = int(fast_dim)
92
+ self.fast_n_head = int(fast_n_head)
93
+ self.fast_n_local_heads = int(fast_n_local_heads)
94
+ self.fast_head_dim = int(fast_head_dim)
95
+ self.fast_intermediate_size = int(fast_intermediate_size)
96
+ self.fast_attention_qkv_bias = bool(fast_attention_qkv_bias)
97
+ self.fast_attention_qk_norm = bool(fast_attention_qk_norm)
98
+ self.fast_attention_o_bias = bool(fast_attention_o_bias)
99
+ self.norm_fastlayer_input = bool(norm_fastlayer_input)
100
+ self.initializer_range = float(initializer_range)
101
+ self.use_gradient_checkpointing = bool(use_gradient_checkpointing)
102
+ self.codec_filename = str(codec_filename)
103
+ self.codec_sample_rate = int(codec_sample_rate)
104
+ self.codec_frame_size = int(codec_frame_size)
105
+ self.codec_post_n_layer = int(codec_post_n_layer)
106
+ self.codec_post_n_head = int(codec_post_n_head)
107
+ self.codec_post_n_local_heads = int(codec_post_n_local_heads)
108
+ self.codec_post_intermediate_size = int(codec_post_intermediate_size)
109
+ self.ras_window_size = int(ras_window_size)
110
+ self.ras_temperature = float(ras_temperature)
111
+ self.ras_top_p = float(ras_top_p)
112
+
113
+
114
+ __all__ = ["ArkttsConfig"]
generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "do_sample": true,
3
+ "eos_token_id": 151645,
4
+ "max_new_tokens": 512,
5
+ "pad_token_id": 151643,
6
+ "temperature": 0.7,
7
+ "top_p": 0.9,
8
+ "transformers_version": "4.57.3",
9
+ "top_k": 50
10
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f590053e980a45ebcbb2e787cff4f2dead932b2b61e5cf54d05664d9764e12d8
3
+ size 1202342528
modeling_arktts.py ADDED
@@ -0,0 +1,831 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import shutil
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from torch import Tensor
13
+ from torch.nn.attention import SDPBackend, sdpa_kernel
14
+ from transformers import GenerationConfig, PreTrainedModel
15
+ from transformers.generation import (
16
+ LogitsProcessor,
17
+ LogitsProcessorList,
18
+ StoppingCriteriaList,
19
+ )
20
+ from transformers.modeling_outputs import ModelOutput
21
+ from transformers.utils.hub import cached_file
22
+
23
+ from .configuration_arktts import ArkttsConfig
24
+
25
+
26
+ @dataclass
27
+ class ArkttsModelOutput(ModelOutput):
28
+ logits: Optional[Tensor] = None
29
+ hidden_states: Optional[Tensor] = None
30
+ codebook_logits: Optional[Tensor] = None
31
+
32
+
33
+ @dataclass
34
+ class ArkttsGenerateOutput(ModelOutput):
35
+ codes: Optional[Tensor] = None
36
+ code_lengths: Optional[Tensor] = None
37
+ finished: Optional[Tensor] = None
38
+
39
+
40
+ class ArkttsSemanticLogitsProcessor(LogitsProcessor):
41
+ def __init__(self, semantic_begin_id: int, semantic_end_id: int, eos_token_id: int):
42
+ self.semantic_begin_id = int(semantic_begin_id)
43
+ self.semantic_end_id = int(semantic_end_id)
44
+ self.eos_token_id = int(eos_token_id)
45
+
46
+ def __call__(self, input_ids: Tensor, scores: Tensor) -> Tensor:
47
+ filtered = torch.full_like(scores, float("-inf"))
48
+ filtered[:, self.semantic_begin_id : self.semantic_end_id + 1] = scores[
49
+ :, self.semantic_begin_id : self.semantic_end_id + 1
50
+ ]
51
+ filtered[:, self.eos_token_id] = scores[:, self.eos_token_id]
52
+ return filtered
53
+
54
+
55
+ class ArkttsLegacyTopKTopPLogitsProcessor(LogitsProcessor):
56
+ """Matches the candidate filtering order used by the original inference code."""
57
+
58
+ def __init__(self, top_k: int, top_p: float):
59
+ if top_k <= 0:
60
+ raise ValueError("top_k must be positive")
61
+ if not 0.0 < top_p <= 1.0:
62
+ raise ValueError("top_p must be in (0, 1]")
63
+ self.top_k = int(top_k)
64
+ self.top_p = float(top_p)
65
+
66
+ def __call__(self, input_ids: Tensor, scores: Tensor) -> Tensor:
67
+ sorted_scores, sorted_indices = torch.sort(scores, descending=True, dim=-1)
68
+ cumulative = torch.cumsum(torch.softmax(sorted_scores, dim=-1), dim=-1)
69
+ positions = torch.arange(sorted_scores.shape[-1], device=scores.device)
70
+ threshold = torch.tensor(self.top_p, dtype=cumulative.dtype, device=cumulative.device)
71
+ remove_sorted = (cumulative > threshold) | (positions >= self.top_k)
72
+ remove_sorted[..., 0] = False
73
+ remove = torch.zeros_like(remove_sorted).scatter(1, sorted_indices, remove_sorted)
74
+ return scores.masked_fill(remove, float("-inf"))
75
+
76
+
77
+ class ArkttsKVCache(nn.Module):
78
+ def __init__(
79
+ self,
80
+ batch_size: int,
81
+ max_length: int,
82
+ heads: int,
83
+ head_dim: int,
84
+ dtype,
85
+ return_full: bool = False,
86
+ ):
87
+ super().__init__()
88
+ shape = (batch_size, heads, max_length, head_dim)
89
+ self.register_buffer("keys", torch.zeros(shape, dtype=dtype), persistent=False)
90
+ self.register_buffer("values", torch.zeros(shape, dtype=dtype), persistent=False)
91
+ self.return_full = bool(return_full)
92
+
93
+ def update(self, cache_position: Tensor, keys: Tensor, values: Tensor):
94
+ self.keys[:, :, cache_position] = keys
95
+ self.values[:, :, cache_position] = values
96
+ end = self.keys.shape[2] if self.return_full else int(cache_position[-1].item()) + 1
97
+ return self.keys[:, :, :end], self.values[:, :, :end]
98
+
99
+
100
+ class ArkttsRMSNorm(nn.Module):
101
+ def __init__(self, dim: int, eps: float):
102
+ super().__init__()
103
+ self.eps = float(eps)
104
+ self.weight = nn.Parameter(torch.ones(dim))
105
+
106
+ def forward(self, x: Tensor) -> Tensor:
107
+ normalized = x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + self.eps)
108
+ return normalized.to(x.dtype) * self.weight
109
+
110
+
111
+ def _precompute_rope(length: int, head_dim: int, base: float) -> Tensor:
112
+ frequencies = 1.0 / (
113
+ base ** (torch.arange(0, head_dim, 2).float()[: head_dim // 2] / head_dim)
114
+ )
115
+ phases = torch.outer(torch.arange(length), frequencies)
116
+ values = torch.polar(torch.ones_like(phases), phases)
117
+ return torch.stack((values.real, values.imag), dim=-1).to(torch.bfloat16)
118
+
119
+
120
+ def _apply_rope(x: Tensor, rope: Tensor) -> Tensor:
121
+ shaped = x.float().reshape(*x.shape[:-1], -1, 2)
122
+ if rope.ndim == 3:
123
+ rope = rope[None, :, None]
124
+ elif rope.ndim == 4:
125
+ rope = rope[:, :, None]
126
+ else:
127
+ raise ValueError(f"Unexpected RoPE shape: {tuple(rope.shape)}")
128
+ output = torch.stack(
129
+ (
130
+ shaped[..., 0] * rope[..., 0] - shaped[..., 1] * rope[..., 1],
131
+ shaped[..., 1] * rope[..., 0] + shaped[..., 0] * rope[..., 1],
132
+ ),
133
+ dim=-1,
134
+ )
135
+ return output.flatten(3).to(x.dtype)
136
+
137
+
138
+ class ArkttsAttention(nn.Module):
139
+ def __init__(
140
+ self,
141
+ dim: int,
142
+ n_head: int,
143
+ n_local_heads: int,
144
+ head_dim: int,
145
+ qkv_bias: bool,
146
+ output_bias: bool,
147
+ qk_norm: bool,
148
+ norm_eps: float,
149
+ dropout: float,
150
+ use_sdpa: bool,
151
+ ):
152
+ super().__init__()
153
+ total = (n_head + 2 * n_local_heads) * head_dim
154
+ self.wqkv = nn.Linear(dim, total, bias=qkv_bias)
155
+ self.wo = nn.Linear(n_head * head_dim, dim, bias=output_bias)
156
+ self.n_head = int(n_head)
157
+ self.n_local_heads = int(n_local_heads)
158
+ self.head_dim = int(head_dim)
159
+ self.dropout = float(dropout)
160
+ self.use_sdpa = bool(use_sdpa)
161
+ self.qk_norm = bool(qk_norm)
162
+ if self.qk_norm:
163
+ self.q_norm = ArkttsRMSNorm(head_dim, norm_eps)
164
+ self.k_norm = ArkttsRMSNorm(head_dim, norm_eps)
165
+ self.kv_cache: Optional[ArkttsKVCache] = None
166
+
167
+ def forward(
168
+ self,
169
+ x: Tensor,
170
+ rope: Tensor,
171
+ attention_mask: Optional[Tensor],
172
+ cache_position: Optional[Tensor] = None,
173
+ ) -> Tensor:
174
+ batch, length, _ = x.shape
175
+ query_size = self.n_head * self.head_dim
176
+ kv_size = self.n_local_heads * self.head_dim
177
+ query, key, value = self.wqkv(x).split((query_size, kv_size, kv_size), dim=-1)
178
+ query = query.view(batch, length, self.n_head, self.head_dim)
179
+ key = key.view(batch, length, self.n_local_heads, self.head_dim)
180
+ value = value.view(batch, length, self.n_local_heads, self.head_dim)
181
+ if self.qk_norm:
182
+ query = self.q_norm(query)
183
+ key = self.k_norm(key)
184
+ query = _apply_rope(query, rope).transpose(1, 2)
185
+ key = _apply_rope(key, rope).transpose(1, 2)
186
+ value = value.transpose(1, 2)
187
+ if self.kv_cache is not None:
188
+ if cache_position is None:
189
+ raise ValueError("cache_position is required when KV cache is enabled")
190
+ key, value = self.kv_cache.update(cache_position, key, value)
191
+ repeats = self.n_head // self.n_local_heads
192
+ key = key.repeat_interleave(repeats, dim=1)
193
+ value = value.repeat_interleave(repeats, dim=1)
194
+ if self.use_sdpa:
195
+ output = F.scaled_dot_product_attention(
196
+ query,
197
+ key,
198
+ value,
199
+ attn_mask=attention_mask,
200
+ dropout_p=self.dropout if self.training else 0.0,
201
+ )
202
+ else:
203
+ scores = query @ key.transpose(-2, -1) / math.sqrt(self.head_dim)
204
+ if attention_mask is not None:
205
+ scores = scores.masked_fill(~attention_mask, float("-inf"))
206
+ probabilities = torch.softmax(scores, dim=-1)
207
+ if self.training and self.dropout:
208
+ probabilities = F.dropout(probabilities, p=self.dropout)
209
+ output = probabilities @ value
210
+ output = output.transpose(1, 2).contiguous().view(batch, length, query_size)
211
+ return self.wo(output)
212
+
213
+
214
+ class ArkttsFeedForward(nn.Module):
215
+ def __init__(self, dim: int, intermediate_size: int):
216
+ super().__init__()
217
+ self.w1 = nn.Linear(dim, intermediate_size, bias=False)
218
+ self.w2 = nn.Linear(intermediate_size, dim, bias=False)
219
+ self.w3 = nn.Linear(dim, intermediate_size, bias=False)
220
+
221
+ def forward(self, x: Tensor) -> Tensor:
222
+ return self.w2(F.silu(self.w1(x)) * self.w3(x))
223
+
224
+
225
+ class ArkttsTransformerBlock(nn.Module):
226
+ def __init__(
227
+ self,
228
+ dim: int,
229
+ intermediate_size: int,
230
+ n_head: int,
231
+ n_local_heads: int,
232
+ head_dim: int,
233
+ qkv_bias: bool,
234
+ output_bias: bool,
235
+ qk_norm: bool,
236
+ norm_eps: float,
237
+ dropout: float,
238
+ use_sdpa: bool,
239
+ ):
240
+ super().__init__()
241
+ self.attention = ArkttsAttention(
242
+ dim, n_head, n_local_heads, head_dim, qkv_bias, output_bias,
243
+ qk_norm, norm_eps, dropout, use_sdpa,
244
+ )
245
+ self.feed_forward = ArkttsFeedForward(dim, intermediate_size)
246
+ self.ffn_norm = ArkttsRMSNorm(dim, norm_eps)
247
+ self.attention_norm = ArkttsRMSNorm(dim, norm_eps)
248
+
249
+ def forward(self, x, rope, attention_mask, cache_position=None):
250
+ hidden = x + self.attention(self.attention_norm(x), rope, attention_mask, cache_position)
251
+ return hidden + self.feed_forward(self.ffn_norm(hidden))
252
+
253
+
254
+ class ArkttsModel(PreTrainedModel):
255
+ config_class = ArkttsConfig
256
+ base_model_prefix = ""
257
+ main_input_name = "input_ids"
258
+ _no_split_modules = ["ArkttsTransformerBlock"]
259
+ _supports_sdpa = True
260
+
261
+ def __init__(self, config: ArkttsConfig):
262
+ super().__init__(config)
263
+ self.embeddings = nn.Embedding(config.vocab_size, config.dim)
264
+ self.codebook_embeddings = nn.Embedding(config.codebook_size * config.num_codebooks, config.dim)
265
+ self.layers = nn.ModuleList([
266
+ ArkttsTransformerBlock(
267
+ config.dim, config.intermediate_size, config.n_head, config.n_local_heads,
268
+ config.head_dim, config.attention_qkv_bias, config.attention_o_bias,
269
+ config.attention_qk_norm, config.norm_eps, config.dropout, True,
270
+ )
271
+ for _ in range(config.n_layer)
272
+ ])
273
+ self.norm = ArkttsRMSNorm(config.dim, config.norm_eps)
274
+ self.fast_project_in = (
275
+ nn.Linear(config.dim, config.fast_dim)
276
+ if config.fast_dim != config.dim else nn.Identity()
277
+ )
278
+ self.fast_embeddings = nn.Embedding(config.codebook_size, config.fast_dim)
279
+ self.fast_layers = nn.ModuleList([
280
+ ArkttsTransformerBlock(
281
+ config.fast_dim, config.fast_intermediate_size, config.fast_n_head,
282
+ config.fast_n_local_heads, config.fast_head_dim,
283
+ config.fast_attention_qkv_bias, config.fast_attention_o_bias,
284
+ config.fast_attention_qk_norm, config.norm_eps, config.dropout, False,
285
+ )
286
+ for _ in range(config.n_fast_layer)
287
+ ])
288
+ self.fast_norm = ArkttsRMSNorm(config.fast_dim, config.norm_eps)
289
+ self.fast_output = nn.Linear(config.fast_dim, config.codebook_size, bias=False)
290
+ self.register_buffer(
291
+ "freqs_cis", _precompute_rope(config.max_seq_len, config.head_dim, config.rope_base),
292
+ persistent=False,
293
+ )
294
+ self.register_buffer(
295
+ "fast_freqs_cis",
296
+ _precompute_rope(config.num_codebooks, config.fast_head_dim, config.rope_base),
297
+ persistent=False,
298
+ )
299
+ self.__dict__["_arktts_codec"] = None
300
+ self.post_init()
301
+
302
+ def _init_weights(self, module):
303
+ if isinstance(module, (nn.Linear, nn.Embedding)):
304
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
305
+ if isinstance(module, nn.Linear) and module.bias is not None:
306
+ module.bias.data.zero_()
307
+
308
+ def get_input_embeddings(self):
309
+ return self.embeddings
310
+
311
+ def set_input_embeddings(self, value):
312
+ self.embeddings = value
313
+
314
+ def get_output_embeddings(self):
315
+ return None
316
+
317
+ def _embed(self, input_ids: Tensor) -> Tensor:
318
+ codebook_embeds = []
319
+ for index in range(self.config.num_codebooks):
320
+ codebook_embeds.append(
321
+ self.codebook_embeddings(input_ids[:, index + 1] + index * self.config.codebook_size)
322
+ )
323
+ codebook_sum = torch.stack(codebook_embeds, dim=1).sum(dim=1)
324
+ semantic = (input_ids[:, 0] >= self.config.semantic_begin_id) & (
325
+ input_ids[:, 0] <= self.config.semantic_end_id
326
+ )
327
+ codebook_sum = torch.where(semantic.unsqueeze(-1), codebook_sum, 0.0)
328
+ return self.embeddings(input_ids[:, 0]) + codebook_sum
329
+
330
+ @staticmethod
331
+ def _causal_mask(attention_mask: Tensor, query_positions: Tensor, key_length: int) -> Tensor:
332
+ if attention_mask.shape[1] < key_length:
333
+ attention_mask = F.pad(
334
+ attention_mask,
335
+ (0, key_length - attention_mask.shape[1]),
336
+ value=0,
337
+ )
338
+ key_positions = torch.arange(key_length, device=attention_mask.device)
339
+ causal = key_positions[None, :] <= query_positions[:, None]
340
+ return causal[None, None] & attention_mask[:, None, None, :key_length].bool()
341
+
342
+ def forward(
343
+ self,
344
+ input_ids: Tensor,
345
+ attention_mask: Optional[Tensor] = None,
346
+ labels: Optional[Tensor] = None,
347
+ output_hidden_states: Optional[bool] = None,
348
+ return_dict: Optional[bool] = None,
349
+ **kwargs,
350
+ ):
351
+ del labels, output_hidden_states, kwargs
352
+ if input_ids.ndim != 3 or input_ids.shape[1] != self.config.num_codebooks + 1:
353
+ raise ValueError(
354
+ f"input_ids must have shape [B, {self.config.num_codebooks + 1}, T]"
355
+ )
356
+ batch, _, length = input_ids.shape
357
+ if attention_mask is None:
358
+ attention_mask = torch.ones((batch, length), dtype=torch.long, device=input_ids.device)
359
+ position_ids = attention_mask.long().cumsum(-1).sub(1).clamp_min(0)
360
+ rope = self.freqs_cis[position_ids]
361
+ mask = self._causal_mask(
362
+ attention_mask,
363
+ torch.arange(length, device=input_ids.device),
364
+ length,
365
+ )
366
+ hidden = self._embed(input_ids)
367
+ for layer in self.layers:
368
+ hidden = layer(hidden, rope, mask)
369
+ normalized = self.norm(hidden)
370
+ logits = F.linear(normalized, self.embeddings.weight)
371
+ output = ArkttsModelOutput(logits=logits, hidden_states=hidden)
372
+ if return_dict is False:
373
+ return (logits, hidden)
374
+ return output
375
+
376
+ def _setup_generation_caches(self, batch_size: int, max_length: int, dtype):
377
+ del max_length
378
+ for layer in self.layers:
379
+ layer.attention.kv_cache = ArkttsKVCache(
380
+ batch_size,
381
+ self.config.max_seq_len,
382
+ self.config.n_local_heads,
383
+ self.config.head_dim,
384
+ dtype,
385
+ return_full=True,
386
+ ).to(self.device)
387
+ for layer in self.fast_layers:
388
+ layer.attention.kv_cache = ArkttsKVCache(
389
+ batch_size, self.config.num_codebooks, self.config.fast_n_local_heads,
390
+ self.config.fast_head_dim, dtype, return_full=True,
391
+ ).to(self.device)
392
+
393
+ def _slow_step(
394
+ self,
395
+ input_ids: Tensor,
396
+ cache_position: Tensor,
397
+ position_ids: Tensor,
398
+ attention_mask: Tensor,
399
+ ) -> tuple[Tensor, Tensor]:
400
+ hidden = self._embed(input_ids)
401
+ rope = self.freqs_cis[position_ids]
402
+ mask = self._causal_mask(
403
+ attention_mask,
404
+ cache_position,
405
+ self.config.max_seq_len,
406
+ )
407
+ for layer in self.layers:
408
+ hidden = layer(hidden, rope, mask, cache_position)
409
+ hidden = hidden[:, -1:]
410
+ normalized = self.norm(hidden)
411
+ logits = F.linear(normalized, self.embeddings.weight)[:, -1]
412
+ fast_hidden = normalized if self.config.norm_fastlayer_input else hidden
413
+ return logits, fast_hidden
414
+
415
+ def _fast_step(self, hidden: Tensor, position: int) -> Tensor:
416
+ cache_position = torch.tensor([position], device=hidden.device, dtype=torch.long)
417
+ rope = self.fast_freqs_cis[cache_position]
418
+ key_mask = torch.ones(
419
+ (hidden.shape[0], self.config.num_codebooks),
420
+ device=hidden.device,
421
+ dtype=torch.bool,
422
+ )
423
+ mask = self._causal_mask(key_mask, cache_position, self.config.num_codebooks)
424
+ for layer in self.fast_layers:
425
+ hidden = layer(hidden, rope, mask, cache_position)
426
+ return self.fast_output(self.fast_norm(hidden))[:, -1]
427
+
428
+ @staticmethod
429
+ def _as_processor_list(value) -> LogitsProcessorList:
430
+ if value is None:
431
+ return LogitsProcessorList()
432
+ if isinstance(value, LogitsProcessorList):
433
+ return value
434
+ return LogitsProcessorList(value)
435
+
436
+ @staticmethod
437
+ def _sample(scores: Tensor, generator=None) -> Tensor:
438
+ probabilities = torch.softmax(scores, dim=-1)
439
+ random = torch.rand(
440
+ probabilities.shape,
441
+ dtype=probabilities.dtype,
442
+ device=probabilities.device,
443
+ generator=generator,
444
+ )
445
+ noise = -torch.log(random)
446
+ return torch.argmax(probabilities / noise, dim=-1)
447
+
448
+ def _processed_scores(
449
+ self,
450
+ input_ids: Tensor,
451
+ scores: Tensor,
452
+ processors: LogitsProcessorList,
453
+ top_k: int,
454
+ top_p: float,
455
+ temperature: float,
456
+ ) -> Tensor:
457
+ scores = processors(input_ids, scores)
458
+ scores = ArkttsLegacyTopKTopPLogitsProcessor(top_k, top_p)(input_ids, scores)
459
+ temperature_value = torch.tensor(
460
+ temperature, dtype=scores.dtype, device=scores.device
461
+ ).clamp_min(1e-5)
462
+ return scores / temperature_value
463
+
464
+ def _sample_semantic(
465
+ self,
466
+ history: Tensor,
467
+ logits: Tensor,
468
+ custom_processors: LogitsProcessorList,
469
+ top_k: int,
470
+ top_p: float,
471
+ temperature: float,
472
+ previous: Optional[Tensor],
473
+ do_sample: bool,
474
+ generator=None,
475
+ ) -> Tensor:
476
+ processors = LogitsProcessorList([
477
+ ArkttsSemanticLogitsProcessor(
478
+ self.config.semantic_begin_id,
479
+ self.config.semantic_end_id,
480
+ self.config.eos_token_id,
481
+ ),
482
+ *custom_processors,
483
+ ])
484
+ regular_scores = self._processed_scores(
485
+ history, logits, processors, top_k, top_p, temperature
486
+ )
487
+ if not do_sample:
488
+ return regular_scores.argmax(dim=-1)
489
+ normal = self._sample(regular_scores, generator=generator)
490
+ high_scores = self._processed_scores(
491
+ history,
492
+ logits,
493
+ processors,
494
+ top_k,
495
+ self.config.ras_top_p,
496
+ self.config.ras_temperature,
497
+ )
498
+ high = self._sample(high_scores, generator=generator)
499
+ if previous is None:
500
+ return normal
501
+ repeated = (previous == normal[:, None]).any(dim=1)
502
+ semantic = (normal >= self.config.semantic_begin_id) & (
503
+ normal <= self.config.semantic_end_id
504
+ )
505
+ return torch.where(repeated & semantic, high, normal)
506
+
507
+ def _generate_codebooks(
508
+ self,
509
+ slow_hidden: Tensor,
510
+ semantic: Tensor,
511
+ processors: LogitsProcessorList,
512
+ top_k: int,
513
+ top_p: float,
514
+ temperature: float,
515
+ do_sample: bool,
516
+ generator=None,
517
+ ) -> Tensor:
518
+ hidden = self.fast_project_in(slow_hidden)
519
+ self._fast_step(hidden, 0)
520
+ current = (semantic - self.config.semantic_begin_id).clamp(0, self.config.codebook_size - 1)
521
+ codebooks = [current]
522
+ fast_history = current[:, None]
523
+ hidden = self.fast_embeddings(current)[:, None]
524
+ for position in range(1, self.config.num_codebooks):
525
+ scores = self._fast_step(hidden, position)
526
+ scores = self._processed_scores(
527
+ fast_history, scores, processors, top_k, top_p, temperature
528
+ )
529
+ current = self._sample(scores, generator=generator) if do_sample else scores.argmax(dim=-1)
530
+ codebooks.append(current)
531
+ fast_history = torch.cat((fast_history, current[:, None]), dim=1)
532
+ hidden = self.fast_embeddings(current)[:, None]
533
+ return torch.stack(codebooks, dim=1)
534
+
535
+ def _prepare_prompt(
536
+ self,
537
+ input_ids=None,
538
+ attention_mask=None,
539
+ prefix_input_ids=None,
540
+ prefix_attention_mask=None,
541
+ suffix_input_ids=None,
542
+ suffix_attention_mask=None,
543
+ reference_codes=None,
544
+ reference_code_lengths=None,
545
+ reference_audio_values=None,
546
+ reference_audio_lengths=None,
547
+ ):
548
+ if input_ids is not None:
549
+ if input_ids.ndim != 3 or input_ids.shape[1] != self.config.num_codebooks + 1:
550
+ raise ValueError("Direct input_ids must have shape [B, num_codebooks + 1, T]")
551
+ if attention_mask is None:
552
+ attention_mask = torch.ones(
553
+ input_ids.shape[0], input_ids.shape[-1], dtype=torch.long, device=input_ids.device
554
+ )
555
+ return input_ids.to(self.device), attention_mask.to(self.device)
556
+ if prefix_input_ids is None or suffix_input_ids is None:
557
+ raise ValueError("Processor output or direct input_ids is required")
558
+ prefix_input_ids = prefix_input_ids.to(self.device)
559
+ suffix_input_ids = suffix_input_ids.to(self.device)
560
+ prefix_attention_mask = prefix_attention_mask.to(self.device)
561
+ suffix_attention_mask = suffix_attention_mask.to(self.device)
562
+ if reference_audio_values is not None:
563
+ if reference_codes is not None:
564
+ raise ValueError("Provide reference audio or reference codes, not both")
565
+ reference_codes, reference_code_lengths = self.encode_audio(
566
+ reference_audio_values.to(self.device), reference_audio_lengths.to(self.device)
567
+ )
568
+ if reference_codes is not None:
569
+ reference_codes = reference_codes.to(self.device)
570
+ reference_code_lengths = reference_code_lengths.to(self.device)
571
+ batch_size = prefix_input_ids.shape[0]
572
+ rows = []
573
+ for batch_index in range(batch_size):
574
+ prefix = prefix_input_ids[batch_index, prefix_attention_mask[batch_index].bool()]
575
+ suffix = suffix_input_ids[batch_index, suffix_attention_mask[batch_index].bool()]
576
+ if reference_codes is None:
577
+ semantic_row = torch.cat((prefix, suffix))
578
+ values = torch.zeros(
579
+ (self.config.num_codebooks + 1, semantic_row.numel()),
580
+ dtype=torch.long,
581
+ device=self.device,
582
+ )
583
+ values[0] = semantic_row
584
+ else:
585
+ length = int(reference_code_lengths[batch_index])
586
+ codes = reference_codes[batch_index, :, :length].long()
587
+ semantic_codes = codes[0] + self.config.semantic_begin_id
588
+ semantic_row = torch.cat((prefix, semantic_codes, suffix))
589
+ values = torch.zeros(
590
+ (self.config.num_codebooks + 1, semantic_row.numel()),
591
+ dtype=torch.long,
592
+ device=self.device,
593
+ )
594
+ values[0] = semantic_row
595
+ values[1:, prefix.numel() : prefix.numel() + length] = codes
596
+ rows.append(values)
597
+ max_length = max(row.shape[1] for row in rows)
598
+ prompt = torch.zeros(
599
+ (batch_size, self.config.num_codebooks + 1, max_length),
600
+ dtype=torch.long,
601
+ device=self.device,
602
+ )
603
+ prompt[:, 0] = self.config.pad_token_id
604
+ prompt_mask = torch.zeros((batch_size, max_length), dtype=torch.long, device=self.device)
605
+ for batch_index, row in enumerate(rows):
606
+ start = max_length - row.shape[1]
607
+ prompt[batch_index, :, start:] = row
608
+ prompt_mask[batch_index, start:] = 1
609
+ return prompt, prompt_mask
610
+
611
+ @torch.inference_mode()
612
+ def generate(
613
+ self,
614
+ input_ids: Optional[Tensor] = None,
615
+ attention_mask: Optional[Tensor] = None,
616
+ prefix_input_ids: Optional[Tensor] = None,
617
+ prefix_attention_mask: Optional[Tensor] = None,
618
+ suffix_input_ids: Optional[Tensor] = None,
619
+ suffix_attention_mask: Optional[Tensor] = None,
620
+ reference_codes: Optional[Tensor] = None,
621
+ reference_code_lengths: Optional[Tensor] = None,
622
+ reference_audio_values: Optional[Tensor] = None,
623
+ reference_audio_lengths: Optional[Tensor] = None,
624
+ generation_config: Optional[GenerationConfig] = None,
625
+ logits_processor=None,
626
+ codebook_logits_processor=None,
627
+ stopping_criteria=None,
628
+ max_new_tokens: Optional[int] = None,
629
+ temperature: Optional[float] = None,
630
+ top_p: Optional[float] = None,
631
+ top_k: Optional[int] = None,
632
+ do_sample: Optional[bool] = None,
633
+ return_dict_in_generate: bool = False,
634
+ generator=None,
635
+ **kwargs,
636
+ ):
637
+ if kwargs:
638
+ raise TypeError(f"Unexpected generation arguments: {sorted(kwargs)}")
639
+ config = generation_config or getattr(self, "generation_config", GenerationConfig())
640
+ config_max_new = getattr(config, "max_new_tokens", None)
641
+ config_temperature = getattr(config, "temperature", None)
642
+ config_top_p = getattr(config, "top_p", None)
643
+ config_top_k = getattr(config, "top_k", None)
644
+ max_new_tokens = int(max_new_tokens if max_new_tokens is not None else (config_max_new or 512))
645
+ temperature = float(temperature if temperature is not None else (config_temperature or 0.7))
646
+ top_p = float(top_p if top_p is not None else (config_top_p or 0.9))
647
+ top_k = int(top_k if top_k is not None else (config_top_k or 50))
648
+ do_sample = bool(do_sample if do_sample is not None else getattr(config, "do_sample", True))
649
+ if max_new_tokens <= 0:
650
+ raise ValueError("max_new_tokens must be positive")
651
+ if temperature <= 0:
652
+ raise ValueError("temperature must be positive")
653
+ prompt, prompt_mask = self._prepare_prompt(
654
+ input_ids, attention_mask, prefix_input_ids, prefix_attention_mask,
655
+ suffix_input_ids, suffix_attention_mask, reference_codes,
656
+ reference_code_lengths, reference_audio_values, reference_audio_lengths,
657
+ )
658
+ batch_size, _, prompt_width = prompt.shape
659
+ if prompt_width >= self.config.max_seq_len:
660
+ raise ValueError(
661
+ f"Prompt length {prompt_width} must be smaller than {self.config.max_seq_len}"
662
+ )
663
+ max_new_tokens = min(max_new_tokens, self.config.max_seq_len - prompt_width)
664
+ self._setup_generation_caches(
665
+ batch_size, prompt_width + max_new_tokens, next(self.parameters()).dtype
666
+ )
667
+ semantic_processors = self._as_processor_list(logits_processor)
668
+ codebook_processors = self._as_processor_list(codebook_logits_processor)
669
+ criteria = stopping_criteria or StoppingCriteriaList()
670
+ if not isinstance(criteria, StoppingCriteriaList):
671
+ criteria = StoppingCriteriaList(criteria)
672
+ cache_position = torch.arange(prompt_width, device=self.device, dtype=torch.long)
673
+ position_ids = prompt_mask.cumsum(-1).sub(1).clamp_min(0)
674
+ logits, slow_hidden = self._slow_step(
675
+ prompt, cache_position, position_ids, prompt_mask
676
+ )
677
+ semantic_history = prompt[:, 0]
678
+ prompt_lengths = prompt_mask.sum(-1)
679
+ previous = None
680
+ finished = torch.zeros(batch_size, dtype=torch.bool, device=self.device)
681
+ code_lengths = torch.zeros(batch_size, dtype=torch.long, device=self.device)
682
+ generated_frames = []
683
+
684
+ for step in range(max_new_tokens):
685
+ active_before = ~finished
686
+ semantic = self._sample_semantic(
687
+ semantic_history, logits, semantic_processors, top_k, top_p,
688
+ temperature, previous, do_sample, generator,
689
+ )
690
+ codebooks = self._generate_codebooks(
691
+ slow_hidden, semantic, codebook_processors, top_k, top_p,
692
+ temperature, do_sample, generator,
693
+ )
694
+ emitted = active_before & (semantic != self.config.eos_token_id)
695
+ frame = torch.where(emitted[:, None], codebooks, -1)
696
+ generated_frames.append(frame)
697
+ code_lengths += emitted.long()
698
+ semantic_history = torch.cat((semantic_history, semantic[:, None]), dim=1)
699
+
700
+ if previous is None:
701
+ previous = torch.zeros(
702
+ (batch_size, self.config.ras_window_size),
703
+ dtype=torch.long,
704
+ device=self.device,
705
+ )
706
+ else:
707
+ previous = previous.roll(-1, dims=1)
708
+ previous[:, -1] = semantic
709
+ finished |= semantic.eq(self.config.eos_token_id)
710
+ if criteria:
711
+ stopped = criteria(semantic_history, logits)
712
+ if not isinstance(stopped, Tensor):
713
+ stopped = torch.full_like(finished, bool(stopped))
714
+ finished |= stopped.to(device=self.device, dtype=torch.bool)
715
+ if finished.all():
716
+ break
717
+
718
+ next_column = torch.cat((semantic[:, None], codebooks), dim=1).unsqueeze(-1)
719
+ new_valid = active_before.long()[:, None]
720
+ prompt_mask = torch.cat((prompt_mask, new_valid), dim=1)
721
+ physical_position = torch.tensor([prompt_width + step], device=self.device)
722
+ token_position = (prompt_lengths + step)[:, None]
723
+ with sdpa_kernel(SDPBackend.MATH):
724
+ logits, slow_hidden = self._slow_step(
725
+ next_column, physical_position, token_position, prompt_mask
726
+ )
727
+
728
+ if generated_frames:
729
+ codes = torch.stack(generated_frames, dim=2)
730
+ max_valid = int(code_lengths.max().item()) if code_lengths.numel() else 0
731
+ codes = codes[:, :, :max_valid]
732
+ else:
733
+ codes = torch.empty(
734
+ (batch_size, self.config.num_codebooks, 0), dtype=torch.long, device=self.device
735
+ )
736
+ result = ArkttsGenerateOutput(codes=codes, code_lengths=code_lengths, finished=finished)
737
+ return result if return_dict_in_generate else codes
738
+
739
+ def _codec_path(self) -> str:
740
+ source = str(getattr(self.config, "_name_or_path", ""))
741
+ local = Path(source)
742
+ if local.is_dir() and (local / self.config.codec_filename).is_file():
743
+ return str(local / self.config.codec_filename)
744
+ resolved = cached_file(source, self.config.codec_filename)
745
+ if resolved is None:
746
+ raise FileNotFoundError(f"Could not resolve {self.config.codec_filename} from {source}")
747
+ return resolved
748
+
749
+ def save_pretrained(self, save_directory, *args, **kwargs):
750
+ result = super().save_pretrained(save_directory, *args, **kwargs)
751
+ source = Path(self._codec_path()).resolve()
752
+ destination = Path(save_directory) / self.config.codec_filename
753
+ destination.parent.mkdir(parents=True, exist_ok=True)
754
+ if source != destination.resolve():
755
+ shutil.copy2(source, destination)
756
+ return result
757
+
758
+ def load_codec(self, device=None, dtype=None):
759
+ codec = self.__dict__.get("_arktts_codec")
760
+ target_device = torch.device(device) if device is not None else self.device
761
+ target_dtype = dtype or self.dtype
762
+ if target_device.type == "cpu":
763
+ target_dtype = torch.float32
764
+ if codec is None:
765
+ from .modeling_arktts_codec import ArkttsCodec
766
+
767
+ codec = ArkttsCodec(self.config)
768
+ state = torch.load(self._codec_path(), map_location="cpu", weights_only=True)
769
+ if "state_dict" in state:
770
+ state = state["state_dict"]
771
+ if any("generator." in key for key in state):
772
+ state = {
773
+ key.replace("generator.", ""): value
774
+ for key, value in state.items() if "generator." in key
775
+ }
776
+ state = {
777
+ key: value
778
+ for key, value in state.items()
779
+ if not key.endswith(("freqs_cis", "causal_mask"))
780
+ }
781
+ codec.load_state_dict(state, strict=True)
782
+ codec.eval()
783
+ self.__dict__["_arktts_codec"] = codec
784
+ codec.to(device=target_device, dtype=target_dtype)
785
+ return codec
786
+
787
+ @torch.inference_mode()
788
+ def encode_audio(self, audio_values: Tensor, audio_lengths: Optional[Tensor] = None):
789
+ codec = self.load_codec(device=audio_values.device)
790
+ audio_values = audio_values.to(dtype=next(codec.parameters()).dtype)
791
+ return codec.encode(audio_values, audio_lengths)
792
+
793
+ @torch.inference_mode()
794
+ def decode_audio(self, codes: Tensor):
795
+ if codes.ndim == 2:
796
+ codes = codes.unsqueeze(0)
797
+ if codes.ndim != 3 or codes.shape[1] != self.config.num_codebooks:
798
+ raise ValueError(f"codes must have shape [B, {self.config.num_codebooks}, T]")
799
+ codec = self.load_codec(device=codes.device)
800
+ waveforms = []
801
+ lengths = []
802
+ for item in codes:
803
+ valid = (item >= 0).all(dim=0)
804
+ length = int(valid.sum().item())
805
+ if length == 0:
806
+ waveform = torch.empty(0, device=codes.device, dtype=torch.float32)
807
+ else:
808
+ waveform = codec.decode(item[:, :length].unsqueeze(0))[0, 0].float()
809
+ waveforms.append(waveform)
810
+ lengths.append(waveform.numel())
811
+ max_length = max(lengths, default=0)
812
+ padded = torch.zeros((len(waveforms), max_length), dtype=torch.float32, device=codes.device)
813
+ for index, waveform in enumerate(waveforms):
814
+ padded[index, : waveform.numel()] = waveform
815
+ return padded, torch.tensor(lengths, dtype=torch.long, device=codes.device)
816
+
817
+ @torch.inference_mode()
818
+ def generate_audio(self, **kwargs):
819
+ codes = self.generate(**kwargs)
820
+ waveforms, lengths = self.decode_audio(codes)
821
+ return waveforms, lengths, codes
822
+
823
+
824
+ __all__ = [
825
+ "ArkttsConfig",
826
+ "ArkttsGenerateOutput",
827
+ "ArkttsLegacyTopKTopPLogitsProcessor",
828
+ "ArkttsModel",
829
+ "ArkttsModelOutput",
830
+ "ArkttsSemanticLogitsProcessor",
831
+ ]
modeling_arktts_codec.py ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from dataclasses import dataclass
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+ from torch import Tensor
10
+ from torch.nn.utils import weight_norm as legacy_weight_norm
11
+ from torch.nn.utils.parametrizations import weight_norm
12
+
13
+
14
+ def _rope(length: int, head_dim: int, base: float, device=None) -> Tensor:
15
+ frequencies = 1.0 / (
16
+ base ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim)
17
+ )
18
+ phases = torch.outer(torch.arange(length, device=device), frequencies)
19
+ values = torch.polar(torch.ones_like(phases), phases)
20
+ return torch.stack((values.real, values.imag), dim=-1).to(torch.bfloat16)
21
+
22
+
23
+ def _apply_rope(x: Tensor, values: Tensor) -> Tensor:
24
+ shaped = x.float().reshape(*x.shape[:-1], -1, 2)
25
+ values = values.view(1, shaped.shape[1], 1, shaped.shape[3], 2)
26
+ output = torch.stack(
27
+ (
28
+ shaped[..., 0] * values[..., 0] - shaped[..., 1] * values[..., 1],
29
+ shaped[..., 1] * values[..., 0] + shaped[..., 0] * values[..., 1],
30
+ ),
31
+ dim=-1,
32
+ )
33
+ return output.flatten(3).to(x.dtype)
34
+
35
+
36
+ class ArkttsCodecRMSNorm(nn.Module):
37
+ def __init__(self, dim: int, eps: float = 1e-5):
38
+ super().__init__()
39
+ self.eps = float(eps)
40
+ self.weight = nn.Parameter(torch.ones(dim))
41
+
42
+ def forward(self, x):
43
+ output = x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + self.eps)
44
+ return output.to(x.dtype) * self.weight
45
+
46
+
47
+ class ArkttsCodecLayerScale(nn.Module):
48
+ def __init__(self, dim: int, init_values: float = 1e-2, inplace: bool = False):
49
+ super().__init__()
50
+ self.inplace = bool(inplace)
51
+ self.gamma = nn.Parameter(init_values * torch.ones(dim))
52
+
53
+ def forward(self, x):
54
+ return x.mul_(self.gamma) if self.inplace else x * self.gamma
55
+
56
+
57
+ @dataclass
58
+ class ArkttsCodecTransformerConfig:
59
+ n_layer: int
60
+ n_head: int
61
+ dim: int
62
+ intermediate_size: int
63
+ n_local_heads: int = -1
64
+ head_dim: int = 64
65
+ rope_base: float = 10000
66
+ norm_eps: float = 1e-5
67
+ dropout_rate: float = 0.1
68
+ attn_dropout_rate: float = 0.1
69
+ channels_first: bool = True
70
+
71
+ def __post_init__(self):
72
+ if self.n_local_heads == -1:
73
+ self.n_local_heads = self.n_head
74
+
75
+
76
+ class ArkttsCodecAttention(nn.Module):
77
+ def __init__(self, config: ArkttsCodecTransformerConfig):
78
+ super().__init__()
79
+ total = (config.n_head + 2 * config.n_local_heads) * config.head_dim
80
+ self.wqkv = nn.Linear(config.dim, total, bias=False)
81
+ self.wo = nn.Linear(config.head_dim * config.n_head, config.dim, bias=False)
82
+ self.n_head = config.n_head
83
+ self.n_local_heads = config.n_local_heads
84
+ self.head_dim = config.head_dim
85
+ self.attn_dropout_rate = config.attn_dropout_rate
86
+
87
+ def forward(self, x, rope_values, mask):
88
+ batch, length, _ = x.shape
89
+ query_size = self.n_head * self.head_dim
90
+ kv_size = self.n_local_heads * self.head_dim
91
+ query, key, value = self.wqkv(x).split((query_size, kv_size, kv_size), dim=-1)
92
+ query = query.view(batch, length, self.n_head, self.head_dim)
93
+ key = key.view(batch, length, self.n_local_heads, self.head_dim)
94
+ value = value.view(batch, length, self.n_local_heads, self.head_dim)
95
+ query = _apply_rope(query, rope_values).transpose(1, 2)
96
+ key = _apply_rope(key, rope_values).transpose(1, 2)
97
+ value = value.transpose(1, 2)
98
+ repeat = self.n_head // self.n_local_heads
99
+ key = key.repeat_interleave(repeat, dim=1)
100
+ value = value.repeat_interleave(repeat, dim=1)
101
+ output = F.scaled_dot_product_attention(
102
+ query,
103
+ key,
104
+ value,
105
+ attn_mask=mask,
106
+ dropout_p=self.attn_dropout_rate if self.training else 0.0,
107
+ )
108
+ output = output.transpose(1, 2).contiguous().view(batch, length, query_size)
109
+ return self.wo(output)
110
+
111
+
112
+ class ArkttsCodecFeedForward(nn.Module):
113
+ def __init__(self, config: ArkttsCodecTransformerConfig):
114
+ super().__init__()
115
+ self.w1 = nn.Linear(config.dim, config.intermediate_size, bias=False)
116
+ self.w3 = nn.Linear(config.dim, config.intermediate_size, bias=False)
117
+ self.w2 = nn.Linear(config.intermediate_size, config.dim, bias=False)
118
+ self.dropout = nn.Dropout(config.dropout_rate)
119
+
120
+ def forward(self, x):
121
+ return self.w2(self.dropout(F.silu(self.w1(x)) * self.w3(x)))
122
+
123
+
124
+ class ArkttsCodecTransformerBlock(nn.Module):
125
+ def __init__(self, config):
126
+ super().__init__()
127
+ self.attention = ArkttsCodecAttention(config)
128
+ self.feed_forward = ArkttsCodecFeedForward(config)
129
+ self.ffn_norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
130
+ self.attention_norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
131
+ self.attention_layer_scale = ArkttsCodecLayerScale(config.dim, inplace=True)
132
+ self.ffn_layer_scale = ArkttsCodecLayerScale(config.dim, inplace=True)
133
+
134
+ def forward(self, x, rope_values, mask):
135
+ hidden = x + self.attention_layer_scale(
136
+ self.attention(self.attention_norm(x), rope_values, mask)
137
+ )
138
+ return hidden + self.ffn_layer_scale(self.feed_forward(self.ffn_norm(hidden)))
139
+
140
+
141
+ class ArkttsCodecWindowTransformer(nn.Module):
142
+ def __init__(self, config, input_dim: int, window_size: int | None, causal: bool = True):
143
+ super().__init__()
144
+ self.layers = nn.ModuleList([ArkttsCodecTransformerBlock(config) for _ in range(config.n_layer)])
145
+ self.norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
146
+ self.window_size = window_size
147
+ self.causal = causal
148
+ self.channels_first = config.channels_first
149
+ self.input_proj = nn.Linear(input_dim, config.dim) if input_dim != config.dim else nn.Identity()
150
+ self.output_proj = nn.Linear(config.dim, input_dim) if input_dim != config.dim else nn.Identity()
151
+ self.look_ahead_conv = nn.Identity()
152
+ self.head_dim = config.head_dim
153
+ self.rope_base = config.rope_base
154
+
155
+ def forward(self, x, x_lens=None):
156
+ del x_lens
157
+ if self.channels_first:
158
+ x = x.transpose(1, 2)
159
+ x = self.look_ahead_conv(self.input_proj(x))
160
+ length = x.shape[1]
161
+ row = torch.arange(length, device=x.device)[:, None]
162
+ column = torch.arange(length, device=x.device)[None, :]
163
+ mask = column <= row
164
+ if self.window_size is not None:
165
+ mask &= column >= (row - self.window_size + 1).clamp_min(0)
166
+ mask = mask[None, None]
167
+ rope_values = _rope(length, self.head_dim, self.rope_base, x.device)
168
+ for layer in self.layers:
169
+ x = layer(x, rope_values, mask)
170
+ x = self.output_proj(self.norm(x))
171
+ return x.transpose(1, 2) if self.channels_first else x
172
+
173
+
174
+ def _extra_padding(x, kernel_size: int, stride: int, padding_total: int = 0) -> int:
175
+ length = x.shape[-1]
176
+ frames = (length - kernel_size + padding_total) / stride + 1
177
+ ideal = (math.ceil(frames) - 1) * stride + kernel_size - padding_total
178
+ return ideal - length
179
+
180
+
181
+ class ArkttsCausalConv1d(nn.Module):
182
+ def __init__(self, in_channels, out_channels, kernel_size, dilation=1, stride=1, groups=1):
183
+ super().__init__()
184
+ self.conv = nn.Conv1d(
185
+ in_channels, out_channels, kernel_size, stride=stride, dilation=dilation, groups=groups
186
+ )
187
+ self.stride = stride
188
+ self.kernel_size = (kernel_size - 1) * dilation + 1
189
+ self.padding = self.kernel_size - self.stride
190
+
191
+ def forward(self, x):
192
+ right = _extra_padding(x, self.kernel_size, self.stride, self.padding)
193
+ return self.conv(F.pad(x, (self.padding, right))).contiguous()
194
+
195
+ def apply_weight_norm(self):
196
+ self.conv = weight_norm(self.conv)
197
+ return self
198
+
199
+
200
+ class ArkttsCausalConvTranspose1d(nn.Module):
201
+ def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1):
202
+ super().__init__()
203
+ self.conv = nn.ConvTranspose1d(
204
+ in_channels, out_channels, kernel_size, stride=stride, dilation=dilation
205
+ )
206
+ self.stride = stride
207
+ self.kernel_size = kernel_size
208
+
209
+ def forward(self, x):
210
+ x = self.conv(x)
211
+ crop = self.kernel_size - self.stride
212
+ return x[..., : x.shape[-1] - crop].contiguous() if crop else x.contiguous()
213
+
214
+ def apply_weight_norm(self):
215
+ self.conv = weight_norm(self.conv)
216
+ return self
217
+
218
+
219
+ def _causal_wn_conv(*args, **kwargs):
220
+ return ArkttsCausalConv1d(*args, **kwargs).apply_weight_norm()
221
+
222
+
223
+ def _causal_wn_transpose(*args, **kwargs):
224
+ return ArkttsCausalConvTranspose1d(*args, **kwargs).apply_weight_norm()
225
+
226
+
227
+ @torch.jit.script
228
+ def _arktts_snake(x: Tensor, alpha: Tensor) -> Tensor:
229
+ shape = x.shape
230
+ x = x.reshape(shape[0], shape[1], -1)
231
+ x = x + (alpha + 1e-9).reciprocal() * torch.sin(alpha * x).pow(2)
232
+ return x.reshape(shape)
233
+
234
+
235
+ class ArkttsSnake1d(nn.Module):
236
+ def __init__(self, channels):
237
+ super().__init__()
238
+ self.alpha = nn.Parameter(torch.ones(1, channels, 1))
239
+
240
+ def forward(self, x):
241
+ return _arktts_snake(x, self.alpha)
242
+
243
+
244
+ class ArkttsResidualUnit(nn.Module):
245
+ def __init__(self, dim: int, dilation: int):
246
+ super().__init__()
247
+ self.block = nn.Sequential(
248
+ ArkttsSnake1d(dim),
249
+ _causal_wn_conv(dim, dim, kernel_size=7, dilation=dilation),
250
+ ArkttsSnake1d(dim),
251
+ _causal_wn_conv(dim, dim, kernel_size=1),
252
+ )
253
+
254
+ def forward(self, x):
255
+ output = self.block(x)
256
+ difference = x.shape[-1] - output.shape[-1]
257
+ if difference > 0:
258
+ x = x[..., :-difference]
259
+ return x + output
260
+
261
+
262
+ class ArkttsEncoderBlock(nn.Module):
263
+ def __init__(self, dim: int, stride: int, transformer_layers: int):
264
+ super().__init__()
265
+ modules = [
266
+ ArkttsResidualUnit(dim // 2, 1),
267
+ ArkttsResidualUnit(dim // 2, 3),
268
+ ArkttsResidualUnit(dim // 2, 9),
269
+ ArkttsSnake1d(dim // 2),
270
+ _causal_wn_conv(dim // 2, dim, kernel_size=2 * stride, stride=stride),
271
+ ]
272
+ if transformer_layers:
273
+ config = ArkttsCodecTransformerConfig(
274
+ n_layer=transformer_layers,
275
+ n_head=dim // 64,
276
+ dim=dim,
277
+ intermediate_size=dim * 3,
278
+ )
279
+ modules.append(ArkttsCodecWindowTransformer(config, dim, window_size=512))
280
+ else:
281
+ modules.append(nn.Identity())
282
+ self.block = nn.Sequential(*modules)
283
+
284
+ def forward(self, x):
285
+ return self.block(x)
286
+
287
+
288
+ class ArkttsEncoder(nn.Module):
289
+ def __init__(self):
290
+ super().__init__()
291
+ dim = 64
292
+ modules = [_causal_wn_conv(1, dim, kernel_size=7)]
293
+ for stride, transformer_layers in zip((2, 4, 8, 8), (0, 0, 0, 4)):
294
+ dim *= 2
295
+ modules.append(ArkttsEncoderBlock(dim, stride, transformer_layers))
296
+ modules.extend((ArkttsSnake1d(dim), _causal_wn_conv(dim, 1024, kernel_size=3)))
297
+ self.block = nn.Sequential(*modules)
298
+
299
+ def forward(self, x):
300
+ return self.block(x)
301
+
302
+
303
+ class ArkttsDecoderBlock(nn.Module):
304
+ def __init__(self, input_dim: int, output_dim: int, stride: int):
305
+ super().__init__()
306
+ self.block = nn.Sequential(
307
+ ArkttsSnake1d(input_dim),
308
+ _causal_wn_transpose(input_dim, output_dim, kernel_size=2 * stride, stride=stride),
309
+ ArkttsResidualUnit(output_dim, 1),
310
+ ArkttsResidualUnit(output_dim, 3),
311
+ ArkttsResidualUnit(output_dim, 9),
312
+ )
313
+
314
+ def forward(self, x):
315
+ return self.block(x)
316
+
317
+
318
+ class ArkttsDecoder(nn.Module):
319
+ def __init__(self):
320
+ super().__init__()
321
+ channels = 1536
322
+ modules = [_causal_wn_conv(1024, channels, kernel_size=7)]
323
+ for index, stride in enumerate((8, 8, 4, 2)):
324
+ input_dim = channels // (2**index)
325
+ output_dim = channels // (2 ** (index + 1))
326
+ modules.append(ArkttsDecoderBlock(input_dim, output_dim, stride))
327
+ modules.extend((ArkttsSnake1d(output_dim), _causal_wn_conv(output_dim, 1, kernel_size=7), nn.Tanh()))
328
+ self.model = nn.Sequential(*modules)
329
+
330
+ def forward(self, x):
331
+ return self.model(x)
332
+
333
+
334
+ class ArkttsVectorQuantizer(nn.Module):
335
+ def __init__(self, input_dim: int, codebook_size: int, codebook_dim: int):
336
+ super().__init__()
337
+ self.codebook_size = int(codebook_size)
338
+ self.codebook_dim = int(codebook_dim)
339
+ self.in_proj = legacy_weight_norm(nn.Conv1d(input_dim, codebook_dim, kernel_size=1))
340
+ self.out_proj = legacy_weight_norm(nn.Conv1d(codebook_dim, input_dim, kernel_size=1))
341
+ self.codebook = nn.Embedding(codebook_size, codebook_dim)
342
+
343
+ def decode_code(self, indices):
344
+ return F.embedding(indices, self.codebook.weight).transpose(1, 2)
345
+
346
+ def decode_latents(self, latents):
347
+ batch, _, length = latents.shape
348
+ flattened = latents.transpose(1, 2).reshape(batch * length, -1)
349
+ flattened = F.normalize(flattened)
350
+ codebook = F.normalize(self.codebook.weight)
351
+ distances = (
352
+ flattened.pow(2).sum(1, keepdim=True)
353
+ - 2 * flattened @ codebook.t()
354
+ + codebook.pow(2).sum(1, keepdim=True).t()
355
+ )
356
+ indices = (-distances).argmax(1).view(batch, length)
357
+ return self.decode_code(indices), indices
358
+
359
+ def forward(self, z):
360
+ projected = self.in_proj(z)
361
+ quantized, indices = self.decode_latents(projected)
362
+ quantized_st = projected + (quantized - projected).detach()
363
+ return self.out_proj(quantized_st), indices, projected
364
+
365
+
366
+ class ArkttsResidualQuantizer(nn.Module):
367
+ def __init__(self, input_dim: int, n_codebooks: int, codebook_size: int, codebook_dim: int):
368
+ super().__init__()
369
+ self.n_codebooks = int(n_codebooks)
370
+ self.codebook_size = int(codebook_size)
371
+ self.quantizers = nn.ModuleList([
372
+ ArkttsVectorQuantizer(input_dim, codebook_size, codebook_dim)
373
+ for _ in range(n_codebooks)
374
+ ])
375
+
376
+ def forward(self, z):
377
+ quantized_sum = 0.0
378
+ residual = z
379
+ codes = []
380
+ for quantizer in self.quantizers:
381
+ quantized, indices, _ = quantizer(residual)
382
+ quantized_sum = quantized_sum + quantized
383
+ residual = residual - quantized
384
+ codes.append(indices)
385
+ return quantized_sum, torch.stack(codes, dim=1)
386
+
387
+ def from_codes(self, codes):
388
+ output = 0.0
389
+ for index in range(codes.shape[1]):
390
+ projected = self.quantizers[index].decode_code(codes[:, index])
391
+ output = output + self.quantizers[index].out_proj(projected)
392
+ return output
393
+
394
+
395
+ class ArkttsConvNeXtBlock(nn.Module):
396
+ def __init__(self, dim: int):
397
+ super().__init__()
398
+ self.dwconv = ArkttsCausalConv1d(dim, dim, kernel_size=7, groups=dim)
399
+ self.norm = nn.LayerNorm(dim, eps=1e-6)
400
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
401
+ self.act = nn.GELU()
402
+ self.pwconv2 = nn.Linear(4 * dim, dim)
403
+ self.gamma = nn.Parameter(1e-6 * torch.ones(dim))
404
+
405
+ def forward(self, x):
406
+ residual = x
407
+ x = self.dwconv(x).permute(0, 2, 1)
408
+ x = self.pwconv2(self.act(self.pwconv1(self.norm(x))))
409
+ x = (self.gamma * x).permute(0, 2, 1)
410
+ return residual + x
411
+
412
+
413
+ class ArkttsDownsampleQuantizer(nn.Module):
414
+ def __init__(self, config=None):
415
+ super().__init__()
416
+ self.semantic_quantizer = ArkttsResidualQuantizer(1024, 1, 4096, 8)
417
+ self.quantizer = ArkttsResidualQuantizer(1024, 9, 1024, 8)
418
+ self.downsample = nn.Sequential(
419
+ nn.Sequential(
420
+ ArkttsCausalConv1d(1024, 1024, kernel_size=2, stride=2),
421
+ ArkttsConvNeXtBlock(1024),
422
+ ),
423
+ nn.Sequential(
424
+ ArkttsCausalConv1d(1024, 1024, kernel_size=2, stride=2),
425
+ ArkttsConvNeXtBlock(1024),
426
+ ),
427
+ )
428
+ self.upsample = nn.Sequential(
429
+ nn.Sequential(
430
+ ArkttsCausalConvTranspose1d(1024, 1024, kernel_size=2, stride=2),
431
+ ArkttsConvNeXtBlock(1024),
432
+ ),
433
+ nn.Sequential(
434
+ ArkttsCausalConvTranspose1d(1024, 1024, kernel_size=2, stride=2),
435
+ ArkttsConvNeXtBlock(1024),
436
+ ),
437
+ )
438
+ pre_transformer_config = ArkttsCodecTransformerConfig(
439
+ n_layer=8,
440
+ n_head=16,
441
+ dim=1024,
442
+ intermediate_size=3072,
443
+ )
444
+ post_transformer_config = ArkttsCodecTransformerConfig(
445
+ n_layer=int(getattr(config, "codec_post_n_layer", 8)),
446
+ n_head=int(getattr(config, "codec_post_n_head", 16)),
447
+ n_local_heads=int(getattr(config, "codec_post_n_local_heads", 8)),
448
+ dim=1024,
449
+ intermediate_size=int(getattr(config, "codec_post_intermediate_size", 1216)),
450
+ )
451
+ self.pre_module = ArkttsCodecWindowTransformer(
452
+ pre_transformer_config, 1024, window_size=128
453
+ )
454
+ self.post_module = ArkttsCodecWindowTransformer(
455
+ post_transformer_config, 1024, window_size=128
456
+ )
457
+ self.semantic_predictor_module = nn.Identity()
458
+
459
+ def forward(self, z):
460
+ original_length = z.shape[-1]
461
+ z = self.pre_module(self.downsample(z))
462
+ semantic, semantic_codes = self.semantic_quantizer(z)
463
+ residual, residual_codes = self.quantizer(z - semantic)
464
+ z = self.upsample(self.post_module(semantic + residual))
465
+ difference = original_length - z.shape[-1]
466
+ if difference > 0:
467
+ z = F.pad(z, (difference, 0))
468
+ elif difference < 0:
469
+ z = z[..., -difference:]
470
+ return z, torch.cat((semantic_codes, residual_codes), dim=1)
471
+
472
+ def decode(self, indices):
473
+ indices = indices.clone()
474
+ indices[:, 0].clamp_(0, self.semantic_quantizer.codebook_size - 1)
475
+ indices[:, 1:].clamp_(0, self.quantizer.codebook_size - 1)
476
+ semantic = self.semantic_quantizer.from_codes(indices[:, :1])
477
+ residual = self.quantizer.from_codes(indices[:, 1:])
478
+ return self.upsample(self.post_module(semantic + residual))
479
+
480
+
481
+ class ArkttsCodec(nn.Module):
482
+ sample_rate = 44100
483
+ hop_length = 512
484
+ frame_length = 2048
485
+
486
+ def __init__(self, config=None):
487
+ super().__init__()
488
+ self.encoder = ArkttsEncoder()
489
+ self.quantizer = ArkttsDownsampleQuantizer(config)
490
+ self.decoder = ArkttsDecoder()
491
+
492
+ @torch.inference_mode()
493
+ def encode(self, audio, audio_lengths=None):
494
+ if audio.ndim == 2:
495
+ audio = audio[:, None]
496
+ if audio.ndim != 3 or audio.shape[1] != 1:
497
+ raise ValueError("audio must have shape [B, 1, samples]")
498
+ original_length = audio.shape[-1]
499
+ right = math.ceil(original_length / self.frame_length) * self.frame_length - original_length
500
+ if right:
501
+ audio = F.pad(audio, (0, right))
502
+ if audio_lengths is None:
503
+ audio_lengths = torch.full(
504
+ (audio.shape[0],), original_length, device=audio.device, dtype=torch.long
505
+ )
506
+ encoded = self.encoder(audio)
507
+ _, codes = self.quantizer(encoded)
508
+ code_lengths = torch.ceil(audio_lengths.float() / self.frame_length).long()
509
+ max_codes = codes.shape[-1]
510
+ padded = torch.full_like(codes, -1)
511
+ for index, length in enumerate(code_lengths.tolist()):
512
+ padded[index, :, : min(length, max_codes)] = codes[index, :, : min(length, max_codes)]
513
+ return padded, code_lengths.clamp_max(max_codes)
514
+
515
+ @torch.inference_mode()
516
+ def decode(self, codes):
517
+ return self.decoder(self.quantizer.decode(codes.long()))
518
+
519
+
520
+ __all__ = ["ArkttsCodec"]
preprocessor_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "audio_sampling_rate": 44100,
3
+ "auto_map": {
4
+ "AutoProcessor": "processing_arktts.ArkttsProcessor"
5
+ },
6
+ "feature_extractor_type": "ArkttsProcessor",
7
+ "num_codebooks": 10,
8
+ "processor_class": "ArkttsProcessor",
9
+ "semantic_begin_id": 151678
10
+ }
processing_arktts.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import json
5
+ import os
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Any, Sequence
9
+
10
+ import numpy as np
11
+ import torch
12
+ from transformers import AutoTokenizer
13
+ from transformers.feature_extraction_utils import BatchFeature
14
+ from transformers.processing_utils import ProcessorMixin
15
+
16
+
17
+ def _clean_text(text: str) -> str:
18
+ return " ".join(str(text).strip().split())
19
+
20
+
21
+ def _as_list(value: Any, batch_size: int, name: str) -> list[Any]:
22
+ if isinstance(value, (str, Path)) or value is None or np.isscalar(value):
23
+ return [value] * batch_size
24
+ if isinstance(value, torch.Tensor) and value.ndim <= 2:
25
+ return [value] if batch_size == 1 else list(value)
26
+ if isinstance(value, np.ndarray) and value.ndim <= 2:
27
+ return [value] if batch_size == 1 else list(value)
28
+ values = list(value)
29
+ if len(values) != batch_size:
30
+ raise ValueError(f"{name} must contain {batch_size} items, got {len(values)}")
31
+ return values
32
+
33
+
34
+ def _pad_1d(rows: list[torch.Tensor], pad_value: int) -> tuple[torch.Tensor, torch.Tensor]:
35
+ max_len = max((row.numel() for row in rows), default=0)
36
+ values = torch.full((len(rows), max_len), pad_value, dtype=torch.long)
37
+ mask = torch.zeros((len(rows), max_len), dtype=torch.long)
38
+ for idx, row in enumerate(rows):
39
+ length = row.numel()
40
+ values[idx, :length] = row
41
+ mask[idx, :length] = 1
42
+ return values, mask
43
+
44
+
45
+ class ArkttsProcessor(ProcessorMixin):
46
+ attributes = ["tokenizer"]
47
+ tokenizer_class = ("PreTrainedTokenizerFast", "PreTrainedTokenizer")
48
+ valid_kwargs = ["num_codebooks", "semantic_begin_id", "audio_sampling_rate"]
49
+
50
+ def __init__(
51
+ self,
52
+ tokenizer,
53
+ num_codebooks: int = 10,
54
+ semantic_begin_id: int = 151678,
55
+ audio_sampling_rate: int = 44100,
56
+ **kwargs,
57
+ ):
58
+ super().__init__(tokenizer)
59
+ self.num_codebooks = int(num_codebooks)
60
+ self.semantic_begin_id = int(semantic_begin_id)
61
+ self.audio_sampling_rate = int(audio_sampling_rate)
62
+
63
+ @classmethod
64
+ def from_pretrained(cls, pretrained_model_name_or_path: str, **kwargs) -> "ArkttsProcessor":
65
+ trust_remote_code = bool(kwargs.pop("trust_remote_code", False))
66
+ shared_names = {
67
+ "cache_dir", "force_download", "local_files_only", "token", "revision", "subfolder"
68
+ }
69
+ shared = {key: kwargs[key] for key in list(kwargs) if key in shared_names}
70
+ config = {}
71
+ local_config = os.path.join(str(pretrained_model_name_or_path), "processor_config.json")
72
+ if os.path.isfile(local_config):
73
+ with open(local_config, "r", encoding="utf-8") as handle:
74
+ config = json.load(handle)
75
+ tokenizer = AutoTokenizer.from_pretrained(
76
+ pretrained_model_name_or_path,
77
+ use_fast=True,
78
+ trust_remote_code=trust_remote_code,
79
+ fix_mistral_regex=False,
80
+ **shared,
81
+ )
82
+ return cls(
83
+ tokenizer=tokenizer,
84
+ num_codebooks=config.get("num_codebooks", 10),
85
+ semantic_begin_id=config.get("semantic_begin_id", 151678),
86
+ audio_sampling_rate=config.get("audio_sampling_rate", 44100),
87
+ )
88
+
89
+ def _encode(self, text: str) -> torch.Tensor:
90
+ encode_kwargs = {"add_special_tokens": False}
91
+ if "allowed_special" in inspect.signature(self.tokenizer.encode).parameters:
92
+ encode_kwargs["allowed_special"] = "all"
93
+ return torch.tensor(self.tokenizer.encode(text, **encode_kwargs), dtype=torch.long)
94
+
95
+ @staticmethod
96
+ def _format_reference_text(text: str) -> str:
97
+ cleaned = _clean_text(text)
98
+ if re.search(r"<\|speaker:\d+\|>", cleaned):
99
+ return cleaned
100
+ return f"<|speaker:0|>{cleaned}"
101
+
102
+ def _prompt_segments(self, text: str, reference_text: str | None, has_reference: bool):
103
+ target = _clean_text(text)
104
+ if not target:
105
+ raise ValueError("text must not be empty")
106
+ def encode_parts(parts: list[str]) -> torch.Tensor:
107
+ return torch.cat([self._encode(part) for part in parts])
108
+
109
+ if not has_reference:
110
+ full = encode_parts([
111
+ "<|im_start|>system\n",
112
+ "convert the provided text to speech",
113
+ "<|im_end|>\n",
114
+ "<|im_start|>user\n",
115
+ target,
116
+ "<|im_end|>\n",
117
+ "<|im_start|>assistant\n<|voice|>",
118
+ ])
119
+ return full, self._encode("")
120
+ if not reference_text:
121
+ raise ValueError("reference_text is required when a reference voice is provided")
122
+ prefix = encode_parts([
123
+ "<|im_start|>system\n",
124
+ "convert the provided text to speech reference to the following:\n\nText:\n",
125
+ self._format_reference_text(reference_text),
126
+ "\n\nSpeech:\n",
127
+ ])
128
+ suffix = encode_parts([
129
+ "<|im_end|>\n",
130
+ "<|im_start|>user\n",
131
+ target,
132
+ "<|im_end|>\n",
133
+ "<|im_start|>assistant\n<|voice|>",
134
+ ])
135
+ return prefix, suffix
136
+
137
+ def _load_audio(self, value: Any, sampling_rate: int | None) -> torch.Tensor:
138
+ source_rate = sampling_rate
139
+ if isinstance(value, (str, Path)):
140
+ try:
141
+ import soundfile as sf
142
+ except ImportError as exc:
143
+ raise ImportError("soundfile is required for reference audio paths") from exc
144
+ array, source_rate = sf.read(str(value), dtype="float32", always_2d=True)
145
+ array = array.mean(axis=1)
146
+ audio = torch.from_numpy(np.asarray(array, dtype=np.float32))
147
+ else:
148
+ if isinstance(value, dict):
149
+ source_rate = value.get("sampling_rate", source_rate)
150
+ value = value.get("array")
151
+ if isinstance(value, (tuple, list)) and len(value) == 2 and np.isscalar(value[1]):
152
+ value, source_rate = value
153
+ audio = torch.as_tensor(value, dtype=torch.float32)
154
+ if audio.ndim == 2:
155
+ audio = audio.mean(dim=0)
156
+ if audio.ndim != 1:
157
+ raise ValueError(f"reference audio must be mono or channels-first, got {tuple(audio.shape)}")
158
+ if audio.numel() == 0:
159
+ raise ValueError("reference audio must not be empty")
160
+ if source_rate is None:
161
+ raise ValueError("sampling_rate is required for reference audio arrays")
162
+ if int(source_rate) != self.audio_sampling_rate:
163
+ try:
164
+ from torchaudio.functional import resample
165
+ except ImportError as exc:
166
+ raise ImportError("torchaudio is required to resample reference audio") from exc
167
+ audio = resample(audio, int(source_rate), self.audio_sampling_rate)
168
+ return audio.contiguous()
169
+
170
+ def __call__(
171
+ self,
172
+ text: str | Sequence[str],
173
+ reference_text: str | Sequence[str] | None = None,
174
+ reference_audio: Any = None,
175
+ reference_codes: Any = None,
176
+ sampling_rate: int | Sequence[int] | None = None,
177
+ return_tensors: str = "pt",
178
+ **kwargs,
179
+ ) -> BatchFeature:
180
+ if kwargs:
181
+ raise TypeError(f"Unexpected processor arguments: {sorted(kwargs)}")
182
+ if return_tensors != "pt":
183
+ raise ValueError("ArkttsProcessor currently supports return_tensors='pt' only")
184
+ texts = [text] if isinstance(text, str) else list(text)
185
+ if not texts:
186
+ raise ValueError("text batch must not be empty")
187
+ batch_size = len(texts)
188
+ ref_texts = _as_list(reference_text, batch_size, "reference_text")
189
+ if reference_audio is not None and reference_codes is not None:
190
+ raise ValueError("Provide reference_audio or reference_codes, not both")
191
+ has_reference = reference_audio is not None or reference_codes is not None
192
+
193
+ prefix_rows, suffix_rows = zip(*[
194
+ self._prompt_segments(item, ref_texts[idx], has_reference)
195
+ for idx, item in enumerate(texts)
196
+ ])
197
+ prefix_ids, prefix_mask = _pad_1d(list(prefix_rows), self.tokenizer.pad_token_id)
198
+ suffix_ids, suffix_mask = _pad_1d(list(suffix_rows), self.tokenizer.pad_token_id)
199
+ data: dict[str, torch.Tensor] = {
200
+ "prefix_input_ids": prefix_ids,
201
+ "prefix_attention_mask": prefix_mask,
202
+ "suffix_input_ids": suffix_ids,
203
+ "suffix_attention_mask": suffix_mask,
204
+ }
205
+
206
+ if reference_codes is not None:
207
+ code_items = _as_list(reference_codes, batch_size, "reference_codes")
208
+ loaded = []
209
+ for item in code_items:
210
+ if isinstance(item, (str, Path)):
211
+ item = np.load(str(item))
212
+ codes = torch.as_tensor(item, dtype=torch.long)
213
+ if codes.ndim != 2 or codes.shape[0] != self.num_codebooks or codes.shape[1] == 0:
214
+ raise ValueError(
215
+ f"reference codes must have shape [{self.num_codebooks}, T>0], got {tuple(codes.shape)}"
216
+ )
217
+ if codes.min() < 0 or codes.max() >= 4096:
218
+ raise ValueError("reference codes must be in [0, 4095]")
219
+ loaded.append(codes)
220
+ max_frames = max(item.shape[1] for item in loaded)
221
+ padded = torch.full((batch_size, self.num_codebooks, max_frames), -1, dtype=torch.long)
222
+ lengths = torch.empty(batch_size, dtype=torch.long)
223
+ for idx, codes in enumerate(loaded):
224
+ lengths[idx] = codes.shape[1]
225
+ padded[idx, :, : codes.shape[1]] = codes
226
+ data["reference_codes"] = padded
227
+ data["reference_code_lengths"] = lengths
228
+
229
+ if reference_audio is not None:
230
+ audio_items = _as_list(reference_audio, batch_size, "reference_audio")
231
+ rate_items = _as_list(sampling_rate, batch_size, "sampling_rate")
232
+ loaded_audio = [self._load_audio(item, rate_items[idx]) for idx, item in enumerate(audio_items)]
233
+ max_samples = max(item.numel() for item in loaded_audio)
234
+ padded_audio = torch.zeros((batch_size, 1, max_samples), dtype=torch.float32)
235
+ lengths = torch.empty(batch_size, dtype=torch.long)
236
+ for idx, audio in enumerate(loaded_audio):
237
+ lengths[idx] = audio.numel()
238
+ padded_audio[idx, 0, : audio.numel()] = audio
239
+ data["reference_audio_values"] = padded_audio
240
+ data["reference_audio_lengths"] = lengths
241
+
242
+ return BatchFeature(data=data)
243
+
244
+ @property
245
+ def model_input_names(self) -> list[str]:
246
+ return [
247
+ "prefix_input_ids", "prefix_attention_mask", "suffix_input_ids",
248
+ "suffix_attention_mask", "reference_codes", "reference_code_lengths",
249
+ "reference_audio_values", "reference_audio_lengths",
250
+ ]
251
+
252
+ def batch_decode(self, *args, **kwargs):
253
+ return self.tokenizer.batch_decode(*args, **kwargs)
254
+
255
+ def decode(self, *args, **kwargs):
256
+ return self.tokenizer.decode(*args, **kwargs)
257
+
258
+
259
+ __all__ = ["ArkttsProcessor"]
processor_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "audio_sampling_rate": 44100,
3
+ "auto_map": {
4
+ "AutoProcessor": "processing_arktts.ArkttsProcessor"
5
+ },
6
+ "num_codebooks": 10,
7
+ "processor_class": "ArkttsProcessor",
8
+ "semantic_begin_id": 151678
9
+ }
special_tokens_map.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f24e08099d45a8adf3f52f5f0b03276e433bb9d689bb15fcbcc48ce58744588b
3
+ size 12217872
tokenizer_config.json ADDED
The diff for this file is too large to render. See raw diff