diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 301 |
1 files changed, 60 insertions, 241 deletions
@@ -1,16 +1,14 @@ -# Qwen3 Audiobook Converter +# tts-audiobook-generator -Convert TXT, PDF, and EPUB files into audiobooks using the Qwen3-TTS voice model. - -This builds upon [WhiskeyCoder/Qwen3-Audiobook-Converter](https://github.com/WhiskeyCoder/Qwen3-Audiobook-Converter) adding more output files, support for faster backends, metadata, generated cover art, transcription/speed/language options, better text cleanup, and clearer instructions. The converter supports three TTS backends, selected with `--backend`: the original Qwen3-TTS Gradio demos (`gradio`, default), [faster-qwen3-tts](https://github.com/andimarafioti/faster-qwen3-tts) (`faster`), and [audio.cpp](https://github.com/0xShug0/audio.cpp) (`audiocpp`) — the last of which can serve either the same Qwen3-TTS 1.7B model (Option 3) or any of audio.cpp's larger non-Qwen TTS families like Higgs Audio v3 4B, VoxCPM2, and IndexTTS-2/2.5 (Option 4). +Generate audiobooks from books (epub, txt, pdf) using text-to-speech backends such as `audio.cpp` and `qwen-tts`. ## Overview -The converter sends text extracted from your books to a locally running Qwen3-TTS server and assembles the returned audio into a single audiobook file. +The converter sends text extracted from your books to a locally running TTS server and assembles the returned audio into a single audiobook file. - Input: `.txt`, `.pdf`, or `.epub` - Output: `.m4b`, `.mp3`, `.ogg`, or `.flac` -- Supports [qwen-tts](https://pypi.org/project/qwen-tts/), [faster-qwen3-tts](https://github.com/andimarafioti/faster-qwen3-tts), and [audio.cpp](https://github.com/0xShug0/audio.cpp) backend servers (audio.cpp can host any of its TTS model families, not just Qwen3-TTS) +- Supports [audio.cpp](https://github.com/0xShug0/audio.cpp), [qwen-tts](https://pypi.org/project/qwen-tts/), and [faster-qwen3-tts](https://github.com/andimarafioti/faster-qwen3-tts) backend servers (audio.cpp can host any of its TTS model families, not just Qwen3-TTS) - Output a single file or one per chapter - Automatic metadata (title/artist/album tags, chapter track numbers) and a generated cover - Clone voices from .wav reference files or use the built-in speaker in the CustomVoice model. @@ -25,10 +23,10 @@ The converter sends text extracted from your books to a locally running Qwen3-TT Create a python 3.12 environment, clone the repo, and install the requirements. ```bash -conda create -n qwen3-tts python=3.12 -y -conda activate qwen3-tts -git clone https://git.historia.vg/git/qwen3-audiobook-converter -cd qwen3-audiobook-converter +conda create -n audiobook python=3.12 -y +conda activate audiobook +git clone https://git.historia.vg/git/tts-audiobook-generator +cd tts-audiobook-generator pip install -r requirements.txt ``` @@ -38,119 +36,36 @@ You need to install one of the following backends (see below for installation/us | Backend | Description | | -------------------------------------------------------------------- | ------------------------------------------------------ | -| [Qwen-TTS](https://pypi.org/project/qwen-tts/) | Gradio server released by Qwen | -| [Faster-Qwen-TTS](https://github.com/andimarafioti/faster-qwen3-tts) | Qwen server with 2-8x faster inference for NVidia GPUs | | [audio.cpp](https://github.com/0xShug0/audio.cpp) | Newer C++ TTS backend that supports many recent models | +| [Qwen-TTS](https://pypi.org/project/qwen-tts/) | Qwen demo server (qwen-tts-demo) | +| [Faster-Qwen-TTS](https://github.com/andimarafioti/faster-qwen3-tts) | Qwen server with 2-8x faster inference for NVidia GPUs | ## Options -| Flag | Description | -| ----------------------------- | ------------------------------------------------------------------------------------------------ | -| `--format {mp3,m4b,ogg,flac}` | Output format (default `m4b`). `m4b` uses AAC audio and has built-in chapters. | -| `--clone <path>` | Reference audio (`wav`) for voice cloning. | -| `--transcription "..."` | Override whisper auto-transcription with manual audio transcript. | -| `--no-transcription` | Skip auto-transcription of the reference audio. | -| `--speed <n>` | Playback speed, pitch-preserving (`1.0` = normal). A normal-speed copy is also output. | -| `--single-file` | Merge all chapters into a single file (default: one file per chapter). `m4b` is always one file. | -| `--language <lang>` | Output language for the synthesized speech. Can add an accent even if the text is English. | -| `--backend {gradio,faster,audiocpp}` | TTS server to use (default `gradio`). `faster` and `audiocpp` require their server running first — see the backend sections above. With `audiocpp` the server may host any audio.cpp TTS model family (see Option 4). | -| `--voice <name>` | Voice to request from a server-side voice configuration (`--backend faster` or `audiocpp` only). Required for audio.cpp families without built-in speakers (everything except Qwen3-TTS). | -| `--chunk` | Force client-side chunking into `CHUNK_SIZE`-word requests. Only matters for `--backend audiocpp`, which otherwise sends each chapter as one request and lets the server chunk long text itself (may double-chunk); the `gradio` and `faster` backends always chunk. | -| `--model <id>` | `--backend audiocpp` only: audio.cpp server model entry id to use for this run. Overrides `AUDIOCPP_MODEL_ID` in `converter/config.py`, so a server hosting several lazily-loaded models (one `server.json`, see Option 4) can be used without editing config — pick the model per run. Leave unset to use the config id, or to auto-select when the server hosts exactly one entry. | -| `--debug` | Troubleshooting: dump each chunk's raw audio and sent text to `debug/` and log every request. | +| Flag | Description | +| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `--backend {audiocpp,qwen,faster}` | TTS server to use (default `audiocpp`). | +| `--format {mp3,m4b,ogg,flac}` | Output format (default `m4b`). | +| `--speed <n>` | Playback speed, pitch-preserving (`1.0` = normal). A normal-speed copy is also output. | +| `--single-file` | Merge all chapters into a single file. `m4b` is always one file. | +| `--language <lang>` | Output language for the synthesized speech. Can add an accent even if the text is English. | +| `--chunk` | Force client-side chunking into `CHUNK_SIZE`-word requests. Only matters for `--backend audiocpp`, which otherwise sends each chapter as one request and lets the server chunk long text itself (may double-chunk); the `qwen` and `faster` backends always chunk. | +| `--model <id>` | `--backend audiocpp` only: Choose the model from `server.json` | +| `--debug` | Dump each chunk's raw audio and sent text to `debug/` and log every request. | +| `--voice <name>` | `audiocpp`, `faster`: Server-side voice to request | +| `--clone <path>` | `qwen`: Reference audio (`wav`) for voice cloning. | +| `--transcription "..."` | `qwen`: Override whisper auto-transcription with manual audio transcript. | +| `--no-transcription` | `qwen`: Skip auto-transcription of the reference audio. | Other options including backend server URLs/ports are configured in `converter/config.py` -## Backend Option 1: Qwen3-TTS - -Install qwen-tts with pip: - -```bash -conda activate qwen3-tts -pip install -U qwen-tts -``` - -Run the backend with `qwen-tts-demo`. Add `--no-flash-attn` if FlashAttention isn't installed (see below). Note that the Base model and CustomVoice model run on different ports. - -### Voice clone - -```bash -qwen-tts-demo Qwen/Qwen3-TTS-12Hz-1.7B-Base --ip 127.0.0.1 --port 7861 [--no-flash-attn] -``` - -Then in another terminal: - -```bash -python audiobook.py --clone reference.wav -``` - -The reference `.wav` should be ~10-15 seconds (3 second minimum, 60 second maximum; ~15 seconds is ideal). Longer is **not** better. - -Whisper (`faster_whisper` or `whisper`) is used automatically to transcribe the reference audio. Without a Whisper backend it falls back to x-vector-only cloning. Override with `--transcription "What the .wav says"` or skip transcription with `--no-transcription`. - - -### Custom voice (i.e. built-in voice) - -```bash -conda activate qwen3-tts -qwen-tts-demo Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice --ip 127.0.0.1 --port 7860 [--no-flash-attn] -``` - -```bash -python audiobook.py -``` - -Change the voice settings in `converter/config.py`. - - -## Backend Option 2: faster-qwen-tts - -`--backend faster` talks to the OpenAI-compatible server from [faster-qwen3-tts](https://github.com/andimarafioti/faster-qwen3-tts), which uses CUDA graph capture for roughly 5-10x faster inference with the same models. **It requires an NVIDIA GPU**. - +## Backend Option 1: audio.cpp -Install into the **same `qwen3-tts` conda environment** used for qwen-tts. +`audiocpp` is an easy to use server that hosts numerous TTS model families. -```bash -conda activate qwen3-tts -pip install -U qwen-tts -pip install "faster-qwen3-tts[demo]" -``` - -### Voice Clone - -**This backend always uses voice cloning**. The reference voice and language are configured on the **server**, not through the converter. The server does not transcribe reference audio itself, so do it manually or use the `tools/make_faster_voices_json.py` helper (see below). - -The pip package does not include the server script, so clone the repository: - -```bash -git clone https://github.com/andimarafioti/faster-qwen3-tts -cd faster-qwen3-tts -``` +### Download and build audiocpp_server -Create a `voices.json` mapping names to reference configurations (.wav to clone, transcript, language). Optionally run `python ./tools/make_faster_voices_json.py path/to/clone/wavs` to automatically create a `voices.json` using whisper to automatically transcribe the test audio. - -```json -{ - "default": {"ref_audio": "voice1.wav", "ref_text": "Transcript of voice 1.", "language": "English"}, - "obama": {"ref_audio": "voice2.wav", "ref_text": "Transcript of voice 2.", "language": "English"} -} -``` - -Run the server - -```bash -python examples/openai_server.py --voices voices.json --port 8000 -``` - -Then from another terminal, run audiobook.py with `--backend faster` - -```bash -python audiobook.py --backend faster [--voice NAME] -``` - -## Backend Option 3: audio.cpp with Qwen3-TTS - -Build `audiocpp_server` for your platform and backend `(cuda, vulkan, hip, cpu)`. Check [audio.cpp's readme](https://github.com/0xShug0/audio.cpp) for details. I'm using one of the helper scripts: +Download and build `audiocpp_server` for your platform and backend `(cuda, vulkan, hip, cpu)`. Check [audio.cpp's readme](https://github.com/0xShug0/audio.cpp) for details. I'm using one of the helper scripts: ```bash git clone https://github.com/0xShug0/audio.cpp @@ -158,26 +73,40 @@ cd audio.cpp scripts/build_linux.sh --backend cuda --target audiocpp_server ``` -Download the Qwen3-TTS GGUF packages (Base for cloning, CustomVoice for built-in speakers) with the python model manager script. This will download these to `./models` +### Install models + +Download model packages with the python model manager script from the audio.cpp checkout. Each installs to `./models`. Here are two examples, Higgs Audio and Qwen3-TTS: ```bash -python3 tools/model_manager_v2.py install qwen3_tts_1_7b_base_q8_0 -python3 tools/model_manager_v2.py install qwen3_tts_1_7b_customvoice_q8_0 +python tools/model_manager_v2.py install higgs_audio_tts_4b_q8_0 +python tools/model_manager_v2.py install qwen3_tts_1_7b_base_q8_0 +python tools/model_manager_v2.py install qwen3_tts_1_7b_customvoice_q8_0 ``` -Create a `server.json` file. One server can host multiple models. Note that the `id:` field(s) must match `AUDIOCPP_MODEL_ID` and `AUDIOCPP_CLONE_MODEL_ID` in qwen3_ebook_converter's .`converter/config.py`. Optionally run `tools/make_audiocpp_server_json.py path/to/clone/wavs --audiocpp-dir /path/to/audio.cpp` to make `server.json` for you with automatic whisper transcription (see [Generating server.json](#generating-serverjson-with-make_audiocpp_server_json)). +You can run `python tools/model_manager_v2.py list` to see all available models. -Cloning voices can be configured per model entry (`voice_presets`) or once at the server level (`voice_dir` + a `prompt_text` file), which every hosted model can clone from. The generator uses the server-level form: +### Create server.json + +Create a `server.json` config file. One server can host multiple models and multiple cloned voices. The `id:` fields are the model names you will set for `tts-audiobook-generator` with `--model`. + +A helper tool is available in this repo `tools/make_audiocpp_server_json.py path/to/clone/wavs` that will interactively make this file for you, including automatically transcribing `.wav` voices to clone with `whisper`. Just pass it a path of `.wav` files. Make sure you're in a Python environment that has `whisper` (i.e. `conda activate audiobook` before running) ```json { "host": "127.0.0.1", "port": 8080, "backend": "cuda", - "lazy_load": false, + "lazy_load": true, "voice_dir": "/path/to/clone/wavs", "models": [ { + "id": "higgs", + "family": "higgs_audio_tts", + "path": "models/Higgs-Audio-v3-TTS-4B-GGUF", + "task": "tts", + "mode": "offline" + }, + { "id": "qwen", "family": "qwen3_tts", "path": "models/Qwen3-TTS-12Hz-1.7B-CustomVoice-GGUF", @@ -185,7 +114,7 @@ Cloning voices can be configured per model entry (`voice_presets`) or once at th "mode": "offline" }, { - "id": "qwen3-clone", + "id": "qwen-clone", "family": "qwen3_tts", "path": "models/Qwen3-TTS-12Hz-1.7B-Base-GGUF", "task": "tts", @@ -195,141 +124,31 @@ Cloning voices can be configured per model entry (`voice_presets`) or once at th } ``` -`voice_dir` points at a directory of `.wav` reference files plus a `prompt_text` file with one `<basename>|<transcript>` line per voice: - -``` -narrator|Transcript of the reference audio. -obama|Transcript of reference audio 2. -``` - -A request with `"voice": "narrator"` then clones `voice_dir/narrator.wav` using that transcript. (Per-entry `voice_presets` work too — see audio.cpp's server readme.) - -Run the server. The `audiocpp_server` path will be slightly different depending on your platform and build options: +Run the server with this config file. The `audiocpp_server` path will be slightly different depending on your platform and build options: ```bash ./build/linux-cuda-release/bin/audiocpp_server --config server.json ``` -Then in a different terminal, run `audiobook.py` - -```bash -# Built-in speaker -python audiobook.py --backend audiocpp - -# Voice cloning -python audiobook.py --backend audiocpp --voice narrator -``` - -## Backend Option 4: audio.cpp with non-Qwen models - -The same `audiocpp_server` can host most of audio.cpp's other TTS model families, including models that are larger or higher quality than Qwen3-TTS 1.7B. The converter detects the model family from the server at startup and adapts its requests automatically (language codes, style instructions, etc.), so no other converter settings change: point `AUDIOCPP_MODEL_ID` at the entry you want, start the server, and convert with `--backend audiocpp --voice <name>`. - -One difference from Qwen3-TTS: **all of these families are clone-only** — they have no built-in speakers, so a reference voice must be configured on the server and selected with `--voice`. Running without `--voice` fails fast with a hint instead of synthesizing a random voice. - -Supported families (see [audio.cpp's model list](https://github.com/0xShug0/audio.cpp#supported-models) for the full catalog): - -| Family | Model | Languages | Notes | -| ------------------------------------------- | --------------------- | ------------------------ | ------------------------------------------------ | -| `higgs_audio_tts` | Higgs Audio v3 TTS 4B | 100+ | Largest TTS in audio.cpp; expressive, inline emotion/style control | -| `voxcpm2` | VoxCPM2-2B | 29 listed | 48 kHz output (others are 24 kHz); cloning + "ultimate clone" (audio + transcript) | -| `index_tts2` | IndexTTS-2 | zh, en | Top-tier cloning quality | -| `index_tts2` (package `index_tts2_5_*`) | IndexTTS-2.5 | zh, en, ja, es, ar | Multilingual IndexTTS variant | - -The converter also works with families not in this table (Fish Audio, Chatterbox, DotTTS, OmniVoice, ...) through its generic profile: clone-only, voice from `--voice`, language detected by the model itself. Anything you can host in `audiocpp_server` with `"task": "tts"` should work, and `make_audiocpp_server_json.py` reads the full catalog from your audio.cpp checkout, so every TTS family audio.cpp supports is offered — not just the ones listed above. - -### Install and run - -Build `audiocpp_server` exactly as in Option 3 (same binary), then download a model package with audio.cpp's model manager from the audio.cpp checkout: - -```bash -python3 tools/model_manager_v2.py install higgs_audio_tts_4b_q8_0 -# or: python3 tools/model_manager_v2.py install voxcpm2_q8_0 -# or: python3 tools/model_manager_v2.py install index_tts2_q8_0 -# or: python3 tools/model_manager_v2.py install index_tts2_5_q8_0 -``` - -Create a `server.json` hosting the model. Cloning voices go in a server-level `voice_dir` (a directory of `.wav` files plus a `prompt_text` file — see Option 3) so every hosted model can use them. Note that `id` must match `AUDIOCPP_MODEL_ID` in `converter/config.py` (set `AUDIOCPP_CLONE_MODEL_ID` to the same id — single-model servers use one entry for both): - -```json -{ - "host": "127.0.0.1", - "port": 8080, - "backend": "cuda", - "lazy_load": false, - "voice_dir": "/path/to/clone/wavs", - "models": [ - { - "id": "higgs", - "family": "higgs_audio_tts", - "path": "models/Higgs-Audio-v3-TTS-4B-GGUF", - "task": "tts", - "mode": "offline" - } - ] -} -``` - -One `server.json` can host several families at once (add more entries to `models` and set `"lazy_load": true` so each loads only on first use). Then pick the entry per run with `--model <id>` (see below). - -Start the server and convert: +In a different terminal, run `audiobook.py`. Pick the TTS `--model` and `--voice` from server.json: ```bash -./build/linux-cuda-release/bin/audiocpp_server --config server.json - -# In another terminal -python audiobook.py --backend audiocpp --voice narrator -# or, on a multi-model server: +# Higgs Audio (clone-only) python audiobook.py --backend audiocpp --model higgs --voice narrator -``` - -VRAM note: the 4B Higgs Audio Q8_0 package needs roughly 2.5x the memory of the 1.7B Qwen3-TTS packages; VoxCPM2-2B and IndexTTS-2 sit in between. BF16/F16 packages roughly double the footprint again. -### Generating server.json with make_audiocpp_server_json +# Qwen3-TTS built-in speaker +python audiobook.py --backend audiocpp --model qwen -`tools/make_audiocpp_server_json.py` reads the model catalog (`model_specs/*.json`) from a local audio.cpp checkout and offers every TTS family it supports as a multi-select checklist, so one `server.json` can host several lazily-loaded models. It transcribes your reference wavs with whisper and writes a server-level `voice_dir` + `prompt_text` file automatically. - -```bash -# Interactive: point at your audio.cpp checkout and pick families from a checklist -python tools/make_audiocpp_server_json.py path/to/clone/wavs --audiocpp-dir /path/to/audio.cpp - -# Non-interactive: host Higgs Audio + VoxCPM2 in one lazily-loaded server -python tools/make_audiocpp_server_json.py path/to/clone/wavs \ - --audiocpp-dir /path/to/audio.cpp \ - --families higgs_audio_tts,voxcpm2 \ - --backend cuda --output server.json --force +# Qwen3-TTS voice cloning +python audiobook.py --backend audiocpp --model qwen-clone --voice narrator ``` -The checkout can also be auto-detected (an `audio.cpp` directory next to/above your working directory, or the `AUDIOCPP_DIR` environment variable), so `--audiocpp-dir` is optional when you run from there. Pressing Enter at the checklist selects the default Qwen3-TTS flow (built-in speakers + cloning); otherwise enter comma-separated numbers for any combination of families. With more than one family the tool defaults to `"lazy_load": true` (models load on first use and stay in memory until the server exits — restart the server, or `POST /v1/tasks/unload_models`, before switching to a large model to free VRAM). - -For a single hosted entry the tool offers to rewrite `AUDIOCPP_MODEL_ID`/`AUDIOCPP_CLONE_MODEL_ID` in `converter/config.py` to the new id so `audiobook.py` talks to it without manual editing. With several entries it instead prints the available ids — pick one per run with `--model` (or set `AUDIOCPP_MODEL_ID`). Transcripts matter a lot for cloning quality — fill in any empty lines in `prompt_text` by hand before starting the server. - -### Language handling - -`--language` works with these families too, adapted per family: IndexTTS sends a language code (`English` → `en`), while Higgs Audio and VoxCPM2 detect the language from the text themselves and omit the field. `--language Auto` never sends a language. - -## Optional: FlashAttention for qwen-tts-demo server - -FlashAttention provides a small speed boost on the `qwen-tts-demo` backend. It is **not** relevant with the `faster` or `audiocpp` backends, and switching to either of those will provide a bigger speed boost. - -`qwen-tts-demo` server tries to use FlashAttention 2 by default and requires `--no-flash-attn` without it. You have two options to install FlashAttention in your python environment: +## Other TTS Backends -1. Build from source (takes absolutely forever). If you run out of memory, lower MAX_JOBS until you don't. - -```bash -conda activate qwen3-tts -pip install ninja packaging psutil -MAX_JOBS=4 pip install --no-build-isolation flash-attn -``` - -2. pip install a prebuilt wheel matching your torch / CUDA / Python / CXX11-ABI combination: - -```bash -conda activate qwen3-tts -python -c "import torch; print(torch.__version__, torch.version.cuda, torch._C._GLIBCXX_USE_CXX11_ABI)" -``` +Installation and usage documentation for other supported TTS backends is in the `docs/` directory: -- [Official wheels](https://github.com/Dao-AILab/flash-attention/releases) - Pick `cp312` + matching `cuX` + `torchX.Y` + `cxx11abiTRUE/FALSE` -- [Third-party wheels](https://mjunya.com/flash-attention-prebuild-wheels/) +- [qwen-tts instructions](docs/backend-qwen.md) +- [faster-qwen-tts instructions](docs/backend-faster.md) ## Tips |
