Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,19 @@ audiocpp_add_model(ace_step
LOADERS
engine::models::ace_step::make_ace_step_loader
)
audiocpp_add_model(soprano_tts
SOURCES
src/community_models/soprano_tts/assets.cpp
src/community_models/soprano_tts/generator.cpp
src/community_models/soprano_tts/session.cpp
src/community_models/soprano_tts/tokenizer_text.cpp
src/community_models/soprano_tts/vocoder.cpp
INCLUDES
engine/community_models/soprano_tts/session.h
LOADERS
engine::community_models::soprano_tts::make_soprano_tts_loader
)
audiocpp_add_model(midashenglm_gen
SOURCES
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,12 @@ Community model ports live under `community_models` to make the ownership bounda
| **minimax_music3** | Music | auto | GGUF Q4/Q8 | [@0xShug0](https://github.com/0xShug0), [@JoeMattie](https://github.com/JoeMattie) | [MiniMax Music 3](docs/community_models/minimax_music3.md) text-to-music generation with lyrics conditioning |
| **mms_forced_aligner** | Align | nl (nld), en (eng); pre-romanized Latin | Safetensors, GGUF 16/Q8 | Community | [MMS-300M-1130 Forced Aligner](docs/community_models/mms_forced_aligner.md) word-timestamp alignment from a wav2vec2 CTC checkpoint (safetensors or local GGUF) |
| **moss_tts_local** | TTS, Clone, Ctrl | auto, optional language hint | GGUF | [@justinjohn0306](https://github.com/justinjohn0306) | MOSS-TTS-Local Transformer v1.5 support |
| **moss_tts_local** | TTS, Clone, Ctrl | auto, optional language hint | GGUF | [@justinjohn0306](https://github.com/justinjohn0306) | MOSS-TTS-Local Transformer v1.5 support |
| **moss_voicegen** | Voice Design | en, zh | GGUF | Joost [@jrohde](https://github.com/jrohde) | [MOSS-VoiceGenerator](docs/community_models/moss_voicegen.md) speech in a voice designed from a written instruction |
| **outetts** | TTS, Clone | en, ar, zh, nl, fr, de, it, ja, ko, lt, ru, es, pt, be, bn, ka, hu, lv, fa, pl, sw, ta, uk | GGUF | Mirek [@mirek190](https://github.com/mirek190) | Llama-OuteTTS-1.0-1B TTS and voice cloning support |
| **parakeet_tdt** | ASR | auto, bg, cs, da, de, el, en, es, et, fi, fr, hr, hu, it, lt, lv, mt, nl, pl, pt, ro, ru, sk, sl, sv, uk | GGUF F32/16/Q8, Stream | [@dleiferives](https://github.com/dleiferives) | [Parakeet-TDT 0.6B v3](docs/community_models/parakeet_tdt.md) offline, long-form, and buffered-streaming ASR support |
| **sense_asr** | ASR | auto, zh, en, yue, ja, ko, pt, ru, es, it, fr, de, nl, pl, tr, ar, hi, vi, th, id, ms, fa, nospeech | GGUF Q8, Stream | Jason Chen [@jasonchen31](https://github.com/jasonchen31), [@LauraGPT](https://github.com/LauraGPT) / FunASR | [SenseVoice-Small](docs/community_models/sense_asr.md) offline/streaming SAN-M + CTC transcription with event/emotion/language tags and ITN |
| **soprano_tts** | TTS | en | GGUF Q8, Stream | [@WalkingCat](https://github.com/WalkingCat) | [Soprano-1.1-80M](https://huggingface.co/WalkingCat/Soprano-1.1-80M-GGUF) ultra-lightweight TTS with Qwen3 LM + Vocos decoder |
| **vietneu_tts** | TTS, Clone | vi, en | GGUF | Phuoc [@phuocnguyen90](https://github.com/phuocnguyen90) | [VieNeu-TTS-v3-Turbo](docs/community_models/vietneu_tts.md) TTS and voice cloning support |

## Docker
Expand Down
1 change: 1 addition & 0 deletions docs/gguf.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Status labels:
| `qwen3_tts` voice design | Done | Pass | --- | Pass (ASR match, drift) | Pass (ASR match, drift) |
| `rvc` | Done | --- | --- | Pass | --- |
| `seed_vc` | Done | Pass | --- | Pass (drift) | Pass (drift) |
| `soprano_tts` | Done | Pass | --- | Pass | Pass (drift) |
| `silero_vad` | Skip (tiny model) | --- | --- | --- | --- |
| `sortformer_diar` | Done | Pass | --- | Pass | Pass |
| `stable_audio` | Done | Pass | --- | Pass (drift) | Pass (drift) |
Expand Down
265 changes: 265 additions & 0 deletions docs/soprano_tts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# Soprano TTS

Soprano is an ultra-lightweight (~80M parameter) English-only text-to-speech model
using a two-stage architecture: a Qwen3-style causal LM (17 layers, hidden 512,
vocab 8192) that autoregressively emits per-frame 512-dimensional features, and a
non-iterative Vocos-style decoder (ConvNeXt backbone + single ISTFT head, n_fft 2048 /
hop 512) that turns those features into 32 kHz audio. No diffusion refinement is
performed in the decoder.

| Field | Value |
|---|---|
| Family | `soprano_tts` |
| Task | `tts` |
| Mode | `offline`, `streaming` |
| Languages | `en` |
| Audio | WAV; 32 kHz mono |
| Streaming | Pull events (per-chunk audio) |

---

## Install

The model-spec manager installs the original safetensors package from the official
Hugging Face repository:

```bash
python3 tools/model_manager_v2.py install soprano_1_1_80m_original
```

Or download the checkpoint directly and convert the decoder manually:

```bash
# Download the official checkpoint
git lfs install
git clone https://huggingface.co/ekwek/Soprano-1.1-80M models/Soprano-1.1-80M

# Convert the decoder (folds weight-norm from decoder.pth, emits plain safetensors)
pip install torch numpy safetensors
python3 tools/soprano_tts/convert_soprano.py \
--input-dir models/Soprano-1.1-80M \
--output-dir models/Soprano-1.1-80M-converted
```

---

## Build

Build audio.cpp with Soprano support:

```bash
# Soprano only (avoids OOM from 45-model parallel compilation)
cmake -B build -DCMAKE_BUILD_TYPE=Release \
-DAUDIOCPP_MODEL_SET=custom -DAUDIOCPP_MODELS=soprano_tts
cmake --build build --target audiocpp_cli --parallel

# With Vulkan backend
cmake -B build -DCMAKE_BUILD_TYPE=Release \
-DAUDIOCPP_MODEL_SET=custom -DAUDIOCPP_MODELS=soprano_tts \
-DENGINE_ENABLE_VULKAN=ON
cmake --build build --target audiocpp_cli --parallel
```

---

## CLI

### Basic inference

```bash
build/bin/audiocpp_cli --task tts --family soprano_tts \
--model models/Soprano-1.1-80M-converted \
--text "Soprano is an extremely lightweight text to speech model." \
--out soprano.wav
```

### With Vulkan backend

```bash
build/bin/audiocpp_cli --task tts --family soprano_tts \
--model models/Soprano-1.1-80M-converted \
--backend vulkan \
--text "Soprano runs on CPU and Vulkan backends." \
--out soprano_vulkan.wav
```

### Custom generation parameters

```bash
build/bin/audiocpp_cli --task tts --family soprano_tts \
--model models/Soprano-1.1-80M-converted \
--text "Warmer temperature and higher max tokens produce longer audio." \
--request-option temperature=0.5 \
--request-option max_tokens=256 \
--seed 42 \
--out custom.wav
```

### Long-form with custom chunk size

```bash
build/bin/audiocpp_cli --task tts --family soprano_tts \
--model models/Soprano-1.1-80M-converted \
--text "This is a longer text that will be split into sentence-aware chunks by the framework text chunker. Each chunk is generated and decoded separately, then concatenated into the final audio output." \
--session-option soprano_tts.text_chunk_size=320 \
--out longform.wav
```

### Streaming mode

```bash
build/bin/audiocpp_cli --task tts --mode streaming --family soprano_tts \
--model models/Soprano-1.1-80M-converted \
--text "Streaming mode emits audio chunks as they are generated." \
--out stream.wav \
--out-dir stream_chunks
```

---

## Options

### Request options

| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--request-option max_tokens=<n>` | integer | `512` | Maximum generated audio frames per chunk. |
| `--temperature` / `--request-option temperature=<f>` | float | `0.3` | AR sampling temperature. |
| `--top-p` / `--request-option top_p=<f>` | float | `0.95` | Nucleus sampling threshold. |
| `--repetition-penalty` / `--request-option repetition_penalty=<f>` | float | `1.2` | Repetition penalty. |
| `--request-option eos_bias=<f>` | float | `0.0` | Additive bias on EOS logit; positive stops sooner. |
| `--seed` / `--request-option seed=<n>` | integer | random | AR sampling seed. |

### Session options

| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--session-option soprano_tts.text_chunk_size=<n>` | chars | `200` | Max codepoints per chunk. |

### Load options

| Option | Values | Default | Meaning |
|---|---|---:|---|
| `--session-option soprano_tts.backbone_weight_type=<type>` | `native`, `f32`, `f16`, `bf16`, `q8_0` | `f32` | LM weight storage. F32 required on CPU. |
| `--session-option soprano_tts.decoder_weight_type=<type>` | `native`, `f32`, `f16` | `f32` | Decoder weight storage. |

---

## Server

```json
{
"host": "127.0.0.1",
"port": 8080,
"models": [
{
"id": "soprano",
"family": "soprano_tts",
"path": "models/Soprano-1.1-80M-converted",
"task": "tts",
"mode": "offline"
}
]
}
```

```bash
audiocpp_server --config server.json

# OpenAI-compatible TTS endpoint
curl http://127.0.0.1:8080/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "soprano",
"input": "Soprano is an extremely lightweight text to speech model.",
"response_format": "wav"
}' \
-o server_output.wav
```

---

## GGUF package

Standalone GGUF packages are available on Hugging Face:

```bash
# Install with the model manager
python3 tools/model_manager_v2.py install soprano_1_1_80m_q8_0

# Or install the BF16 variant
python3 tools/model_manager_v2.py install soprano_1_1_80m_bf16
```

Inference with the GGUF package:

```bash
build/bin/audiocpp_cli --task tts --family soprano_tts \
--model models/Soprano-1.1-80M-GGUF/soprano-1.1-80m-q8_0.gguf \
--text "GGUF packages are standalone and self-describing." \
--out gguf_soprano.wav
```

To create a GGUF package from the converted safetensors yourself:

```bash
build/bin/audiocpp_gguf \
--input models/Soprano-1.1-80M-converted/model.safetensors \
--output Soprano-1.1-80M-GGUF/soprano-1.1-80m-q8_0.gguf \
--type q8_0 \
--root models/Soprano-1.1-80M-converted \
--family soprano_tts \
--overwrite
```
## Performance

| Backend | RTF | Details |
|---------|---:|--------|
| CPU (warm) | ~0.22-0.23 | ~4-4.6x realtime. F32 storage required for correct output. |
| Vulkan (RX Vega) | ~0.08-0.12 | ~8-13x realtime after one-time shader warmup. Decoder output has numerical drift on this GPU (Vega lacks matrix-core ops). |

Timing logs are available through `--log`:
- `soprano_tts.lm.generate_ms` -- LM AR decode time
- `soprano_tts.lm.frames` -- generated frames
- `soprano_tts.decoder.decode_ms` -- Vocos decoder time
- `soprano_tts.lm.decode.plan_cached` -- plan caching status

---

## Memory

| Metric | Value | Conditions |
|--------|-------|------------|
| Model size (safetensors) | ~380 MB (backbone BF16) + ~18 MB (decoder F32) | Original HF checkpoint |
| Peak RSS (CPU) | ~1.2 GB | Graph arena (512 MB) + weight context (256 MB) + runtime overhead |
| Peak VRAM (Vulkan) | Not measured | Vega ~1.2 GB reported system RAM usage |

---

## Known limitations

- English-only (model limitation)
- No voice cloning
- EOS sampling unreliable at low temperature (C++ RNG != PyTorch RNG)
- Full composite build may OOM; use AUDIOCPP_MODEL_SET=custom with AUDIOCPP_MODELS=soprano_tts

---

## Architecture

Soprano uses a two-stage architecture:

1. **Qwen3 causal LM** (17 layers, hidden 512, 4 heads, 1 KV head, head_dim 128, vocab 8192,
intermediate 2304, rope_theta 10000). Takes prompt `[STOP][TEXT]<text>[START]` and
autoregressively generates tokens. Each step's last-layer hidden state (512-dim) equals
one audio frame.

2. **Vocos decoder** (non-iterative): Interpolate x4 linear align_corners -> Conv1d(512->768,k=1)
-> LN -> 8x ConvNeXt(dwconv k=3 groups, LN, Linear->2304, GELU, Linear->768, gamma) -> LN ->
Linear(768->2050) -> split mag/phase -> exp*exp(i*phi) -> istft(center=True) with Hann window
(n_fft=2048, hop=512).

Output: 32 kHz mono. Token ~ 2048 samples ~ 64 ms.

Reference: https://github.com/ekwek1/soprano
Weights: https://huggingface.co/ekwek/Soprano-1.1-80M
Loading
Loading