diff --git a/README.md b/README.md index cab0d9a..8482c52 100644 --- a/README.md +++ b/README.md @@ -1,471 +1,485 @@ +
+ +BigLinux TTS icon + # BigLinux TTS -

- BigLinux TTS -

- -

- Select any text, press Alt+V, and hear it — native text-to-speech for the Linux desktop -

- -

- License - Version - GTK4 - libadwaita 1.5+ - Rust - Python - 4 TTS engines - 30 languages - 262 tests -

+**Select any text, press Alt+V, and hear it.**
+Text-to-speech for the Linux desktop, with local voices, a global shortcut and real playback controls. + +License: GPL-3.0-or-later +Version 4.1.0 +Platform: Arch, Manjaro, BigLinux +GTK4 and libadwaita 1.5+ +Rust 1.85+ +Python 3.10+ +30 languages + +

+ + + + BigLinux TTS main window, ready to speak + + +
--- -## Table of Contents +**BigLinux TTS** is the text-to-speech tool of [BigLinux](https://www.biglinux.com.br/), a Brazilian distribution based on Manjaro and Arch Linux. Select text in any application, press the shortcut, and it is read aloud; press it again to stop. The package installs a Brazilian Portuguese voice that works without extra setup. All voices run on your computer. + +The app uses Python, GTK4 and libadwaita for the interface and a native Rust engine (PyO3) for espeak-ng and Piper. You can choose between four speech engines: **RHVoice**, **espeak-ng**, **Piper** and **Kokoro**. -- [About](#about) +## Table of contents + +- [Highlights](#highlights) - [Screenshots](#screenshots) -- [History](#history) -- [Features](#features) -- [TTS Engines](#tts-engines) -- [Architecture](#architecture) +- [Quick start](#quick-start) - [Installation](#installation) - [Usage](#usage) -- [Configuration](#configuration) +- [Speech engines](#speech-engines) +- [Text processing](#text-processing) +- [Configuration and files](#configuration-and-files) +- [Troubleshooting](#troubleshooting) +- [Architecture](#architecture) +- [Technical details](#technical-details) - [Internationalization](#internationalization) -- [Technical Details](#technical-details) -- [Testing](#testing) -- [Building from Source](#building-from-source) +- [Development and testing](#development-and-testing) +- [Packaging](#packaging) +- [Project structure](#project-structure) +- [Project history](#project-history) +- [Contributing](#contributing) - [License](#license) - [Authors](#authors) --- -## About - -**BigLinux TTS** is a native Linux desktop application that reads text aloud. Built with GTK4, libadwaita and a native Rust engine, it is the built-in text-to-speech tool of [BigLinux](https://www.biglinux.com.br/) — a Brazilian Linux distribution based on Manjaro/Arch Linux. - -Select any text on screen, press **Alt+V**, and hear it read aloud. Press again to stop. No complicated setup: installing the package brings a ready Brazilian Portuguese voice. - -### Use Cases +## Highlights + +| | | +| --- | --- | +| **One shortcut** | Alt+V by default, and you can change it. Press it to read the selected text and press it again to stop, also while the voice is still loading. | +| **Four engines** | RHVoice (the default, with the Brazilian Portuguese voice *Letícia*), espeak-ng (100+ languages), Piper (neural, native ONNX) and Kokoro (neural, voice blending). | +| **Clear status** | The *Ready to speak* card shows the real state: ready, loading the voice, speaking, stopped, or an error with its reason and a way to fix it, such as *Open Voice Manager*. Errors are never silent. | +| **Checked shortcut** | The shortcut is registered through the desktop's own API and the app reports it as active only after the desktop confirms it. On KDE, conflicts with other actions are shown by name. | +| **Playback controls** | Pause and resume where the audio stopped, a tray icon that pulses while reading, and an MPRIS mini-player for desktop media controls and media keys. | +| **History** | Every reading you heard is kept with its text and the exact audio that played, whatever the engine. You can search it, play it again or export it, and deleted entries go to the Trash. | +| **Text cleanup** | Abbreviations, numbers, dates and money are read as words, special characters are read by name, and HTML and Markdown are removed. The rules follow the language of the selected voice. | +| **Speech Dispatcher never started** | The app talks to every engine directly. It never starts or queries `speech-dispatcher`, because starting it can make other installed modules speak. | +| **Modern interface** | GTK4 + libadwaita (GNOME HIG): adaptive down to 360 px wide, light and dark styles, RTL support, reduced motion, accessible names. | +| **30 languages** | Every string is translated. Portuguese (Brazil) and Portuguese (Portugal) are separate catalogs. | +| **Official repositories only** | Every required dependency comes from the official Arch/Manjaro repositories. BigLinux extras are optional. | -- **Accessibility** — screen reading for users with visual impairments or reading difficulties -- **Multitasking** — listen to articles, documents, and emails while doing other things -- **Language learning** — hear correct pronunciation in 100+ languages -- **Proofreading** — catch writing errors by listening to what was written -- **Productivity** — convert passive reading into active listening +--- -### What Sets It Apart +## Screenshots -1. **4 TTS engines** — RHVoice, espeak-ng (native FFI), Piper (native ONNX) and Kokoro neural voices -2. **Native Rust engine** — espeak-ng via direct FFI and Piper ONNX inference via `ort`; the GIL is released during synthesis, so the window never freezes -3. **Honest feedback** — the window shows the real state (loading the voice, speaking, stopped, error with the reason and how to fix it); nothing fails silently -4. **Reliable global shortcut** — registered through the desktop's own API and confirmed before the app says it works; conflicts are named -5. **Smart text processing** — expands abbreviations, normalizes numbers, pronounces special characters, strips HTML/Markdown -6. **Desktop integration** — system tray with playback controls, MPRIS media controls, notifications -7. **Modern UI** — GTK4 + libadwaita (GNOME HIG), adaptive down to 360 px wide, light/dark, RTL, reduced motion -8. **30 languages** — every string translated; Portuguese of Brazil and of Portugal kept separate -9. **Plain Arch packaging** — every dependency comes from the official Arch/Manjaro repositories +| Ready to speak (light) | Ready to speak (dark) | +| :---: | :---: | +| ![Main window, light style](screenshots/main-light.png) | ![Main window, dark style](screenshots/main-dark.png) | +| **Speaking** | **Error with a way out** | +| ![Reading in progress](screenshots/speaking.png) | ![Error state with an Open Voice Manager button](screenshots/error.png) | +| **Engine status** | **Voice Manager** | +| ![Status of each engine](screenshots/engine-status.png) | ![Voice Manager listing Kokoro voices](screenshots/voice-manager.png) | +| **History** (main menu → History) | **Brazilian Portuguese interface** | +| ![History page with saved readings](screenshots/history.png) | ![Main window in pt-BR](screenshots/main-pt-BR.png) | +| **First run** | **Advanced options** | +| ![Welcome dialog](screenshots/welcome.png) | ![Advanced options with the active shortcut](screenshots/advanced-options.png) | + + + + + + +
Narrow window layout
Narrow window (360–600 px)
+ Screenshots of version 4.1.0 with the BigLinux icon theme, rendered headless in a clean profile. The history entries are sample data. +
--- -## Screenshots +## Quick start -| Ready to speak (dark) | Ready to speak (light) | -|---|---| -| ![Main window, dark theme](screenshots/main-dark.png) | ![Main window, light theme](screenshots/main-light.png) | +```bash +sudo pacman -S tts-biglinux # BigLinux (other distributions: see Installation) +``` -| Speaking | Error with a way out | -|---|---| -| ![Speaking state](screenshots/speaking.png) | ![Error state with Open Voice Manager](screenshots/error.png) | +1. Open **BigLinux Text to Speech** from the application menu, or run `biglinux-tts`. +2. On the first run, a welcome dialog explains the basics. RHVoice with *Letícia* is already selected. +3. Select text in any application and press Alt+V. Press it again to stop. -| Engine status | Voice Manager | -|---|---| -| ![Engine status for each engine](screenshots/engine-status.png) | ![Voice Manager with Kokoro voices](screenshots/voice-manager.png) | +The window is only for settings. The shortcut works with the window closed. -| History (main menu → History) | Advanced options | -|---|---| -| ![History view](screenshots/history.png) | ![Advanced options with the shortcut](screenshots/advanced-options.png) | +--- -| Narrow window (360–600 px) | First run | Brazilian Portuguese | -|---|---|---| -| ![Narrow layout](screenshots/narrow.png) | ![Welcome dialog](screenshots/welcome.png) | ![Main window in pt-BR](screenshots/main-pt-BR.png) | +## Installation -Screenshots of the real application, version 4.1.0 (BigLinux icon theme, rendered headless in a clean profile; sample history entries). +### From the BigLinux repositories ---- +```bash +sudo pacman -S tts-biglinux +``` -## History +### Manjaro and Arch Linux -BigLinux TTS was born from a practical need: making text-to-speech accessible and easy on the Linux desktop. +The package builds from its PKGBUILD. All of its dependencies are in the official repositories (`core`, `extra`), so you do not need the BigLinux or Big Community repositories: -| Date | Version | Milestone | -|------|---------|-----------| -| Sep 2021 | — | First commit by Bruno Gonçalves: initial web-based interface | -| Mar 2022 | — | Rafael Ruscher joins: icon design, CSS refinements, translations | -| Aug 2022 | — | PKGBUILD packaging, i18n with 29 locales, CI/CD workflow | -| Dec 2023 | — | Volume/pitch/rate range inputs, UI polish | -| Feb 2026 | 3.0 | **Full rewrite**: web UI → GTK4 + libadwaita + Python. speech-dispatcher integration, Piper Neural TTS, tray icon (PySide6 subprocess), text processor with abbreviation expansion | -| Mar 2026 | 3.1 | Native RHVoice backend, parallel voice discovery, Python DBus launcher | -| Mar 2026 | 3.2 | Voice Manager dialog with install/remove, theme support, khotkeys sync | -| Jun 2026 | **4.0** | **Native Rust engine** (PyO3): espeak-ng FFI, Piper ONNX inference via `ort` with model caching (7× faster short text). Kokoro Neural TTS integration. Complete i18n audit. Full codebase cleanup | -| Sep 2026 | 4.0 | History in SQLite with retention, Piper streaming, systray playback controls (pause/resume), MPRIS mini-player, pt-BR number/date normalization, diagnostics | -| Oct 2026 | 4.0 | Kokoro fixed for the global shortcut, silent startup, real loading/error states, *Ready to speak* card, engine status, KGlobalAccel shortcut registration with conflict detection, GIL released in Rust, adaptive layout, tray icon breathing on HiDPI, 100 automated tests | -| Oct 2026 | **4.1** | **Release**: history for every engine, long texts streamed by espeak-ng too (bounded memory), text rules follow the voice's language, complete translations in 30 languages, dependencies from the official repositories only, release versioning, CI | +```bash +git clone https://github.com/biglinux/tts-biglinux.git +cd tts-biglinux/pkgbuild +makepkg -si +``` ---- +The package contains the application, the native engine, RHVoice with the Brazilian Portuguese voice *Letícia*, espeak-ng, the tray icon (PySide6), the History player (GStreamer) and the clipboard tools. That is everything you need to select text and press Alt+V. -## Features +> [!NOTE] +> The PKGBUILD's `source=` is the upstream Git repository, so `makepkg` builds `main` from GitHub and not your local files. To build another repository or branch, such as a checkout you have not published yet, set `TTS_BIGLINUX_SOURCE`: +> +> ```bash +> cd tts-biglinux/pkgbuild +> TTS_BIGLINUX_SOURCE="file://$PWD/..#branch=my-branch" makepkg -si +> ``` +> +> A `file://` Git source only sees **committed** work on that branch. -### Text Reading +### Optional voices and extras -- **Configurable global hotkey** (default Alt+V) — select text anywhere, press to speak, press again to stop (also while the voice is still loading) -- **"Ready to speak" card** — shows the real state (ready, loading voice, speaking, stopped, error with the reason and a fix such as *Open Voice Manager*) and the shortcut as keyboard keys, with the desktop's confirmation that it is active -- **Engine status** — RHVoice, espeak-ng, Piper and Kokoro each show *Not installed*, *No voices*, *Ready*, and for the selected engine *Loading*, *Speaking* or *Error* -- **System tray icon** — breathes (opacity only) while reading; left-click speaks or stops, right-click offers Pause/Play and Stop while reading, plus Read text, Settings and Quit; the tooltip shows the current shortcut -- **Notifications** — what is being read, and a clear message when something fails or nothing is selected -- **Built-in voice test** — text field to type and hear with the current voice settings -- **History** — every reading that was heard, with its audio, whatever the engine (main menu → History, Ctrl+H; see [History](#reading-history)) -- **Silent start** — opening the app never speaks and never starts speech-dispatcher -- **Long texts** — read to the end with any character limit, including *Unlimited*: espeak-ng and Piper stream sentence chunks, Kokoro reads line by line +```bash +# English RHVoice voice (official repositories) +sudo pacman -S rhvoice-voice-evgeniy-eng -### Voice Control +# BigLinux repositories +sudo pacman -S biglinux-kokoro-tts # Kokoro neural engine: koko, model, base voices +sudo pacman -S piper-voices-pt-br # Brazilian Portuguese Piper voices (stable) +sudo pacman -S piper-voices-pt-BR # the same voices, as spelled in biglinux-testing +``` -- **Speed** — scale from -100 (slow) to +100 (fast) -- **Pitch** — scale from -100 (low) to +100 (high); for Piper it controls *expressiveness* -- **Volume** — scale from 0 (mute) to 100 (max) -- **Voice selection** — list filtered by engine: "Name — Language · Neural / High quality" -- **Kokoro** — expression presets (neutral, happy, calm, urgent, narrative) and blending with a second voice +You can also install voices from the **Voice Manager** in the app: RHVoice, Piper and espeak-ng voices as pacman packages, and Kokoro voices as downloads. -### Reading History +
+About the two Piper package spellings and the AUR -Main menu → **History** (Ctrl+H) is always available, also with saving turned off (old readings stay). Saving is on for new installations; settings from earlier versions keep their choice. Advanced options: *Save history*, *Keep the audio*, *Keep the text*, *Keep at most* (100 … 5000 or unlimited). +BigLinux stable spells the Piper voice package `piper-voices-pt-br` and testing spells it `piper-voices-pt-BR`. Both ship the same files, so you can install only one. When Piper is selected, the app looks for both spellings: first an installed one, then one in your repositories. It never offers the second spelling when one is already installed. -- **What is kept** for each reading that was actually heard: the text as given and as processed, engine, voice, date and time, duration, outcome (*completed*, *stopped* by the user, or *error* after sound started), and the audio -- **The audio is the audio that was played**, not a second synthesis: +If neither spelling is in your repositories, the app falls back to the AUR and says so before installing. It builds the AUR package as you, never as root, using `pamac build` when pamac's AUR support is on, or else `paru` or `yay`. These helpers are optional dependencies. -| Engine | How the audio is kept | -|--------|-----------------------| -| RHVoice | `RHVoice-test` writes WAV to stdout; the app passes it to `aplay` and writes the same bytes to the history file | -| Kokoro | `koko stream` writes one WAV stream to stdout; same relay as RHVoice (koko's own `pipe` mode plays by itself and could not be kept) | -| espeak-ng, Piper | each WAV chunk is appended to the history file when it starts playing (long texts: one file for the whole reading) | -| Speech Dispatcher | not a selectable engine since 4.0: speech-dispatcher plays on its own, so its audio could only be captured from the sound server — which this app never does | +
-- A stopped reading keeps the audio up to the stop (at most a fraction of a second not heard, still in the player's buffer) -- **The page**: cards with the text (expandable), engine, voice, date, duration and outcome; player; copy, *Read again* with the current voice, *Save audio as…*, open the folder, delete. Search (text, voice, engine, date), filters (all dates, today, yesterday, last 7 days; engine), newest or oldest first, list or grid, multiple selection (save to a folder, delete). New readings appear by themselves; pages of 50 load as you scroll -- **Safe deleting**: one entry can be undone for 5 seconds; a selection asks first; files go to the **Trash** (restored files come back as entries on the next start) -- **Recovery**, at every start, off the main thread: entries are rebuilt from the files when the index is damaged (the damaged file is kept as `history.db.damaged-…`), files without an entry come back, a recording interrupted by a crash is finished and kept, durations of older entries are filled in, a legacy `history.json` is migrated once. Nothing saved is ever deleted by the recovery -- Saving runs in a worker: it never delays the start of speech (first audio measured equal with history on and off) and a failure (full disk, no permission) never stops a reading +### Running from a source checkout -### Voice Manager +```bash +git clone https://github.com/biglinux/tts-biglinux.git +cd tts-biglinux -- Install and remove voices for RHVoice, Piper, espeak-ng (pacman packages via `pkexec`) and Kokoro (downloaded voice files) -- Play/stop a sample with the same button, search by name or language, size shown before installing -- Kokoro downloads: real progress, **Cancel**, verification (incomplete or damaged files are rejected), free-space check, atomic update of `voices.bin` -- Removal asks for confirmation +# 1. Build the native engine against the SYSTEM ONNX Runtime +# (linking a pip-provided onnxruntime breaks Piper at runtime) +cd tts-engine +ORT_LIB_LOCATION=/usr/lib ORT_PREFER_DYNAMIC_LINK=1 cargo build --release --locked +cd .. -### Text Processing +# 2. Link it into the application directory (a checkout's own build wins +# over an installed /usr/lib/tts-biglinux/tts_engine.so) +ln -sf ../../../../tts-engine/target/release/libtts_engine.so \ + usr/share/biglinux/tts-biglinux/tts_engine.so -The rules follow the **language of the selected voice** (an English voice on a Portuguese desktop gets English rules). Each option applies to every engine. +# 3. Run. The launchers detect the checkout and use its code and catalogs. +./usr/bin/biglinux-tts --debug +``` -| Option | What it does | Example | -|--------|--------------|---------| -| Expand abbreviations | Expands slang and titles per language; never inside e-mails, URLs or hyphenated words | `vc` → "você", `Dr. Silva` → "doutor Silva", `w/o` → "without" | -| Normalize numbers (pt) | Numbers, money, percentages, dates and times as words | `R$ 1.234,56`, `08/10/2026`, `21:00` → "vinte e uma horas" | -| Read special characters | Symbols by name (pt, en, es); off: symbols the engines would read are removed | `#` → "cerquilha"/"hash" | -| Remove formatting | HTML tags and Markdown markers; headings keep a pause, numbered lists keep their numbers | `**bold**` → "bold" | -| Read URLs | On: the domain (`https://www.example.com/a?b=1` → "example.com"); off: removed | | -| Character limit | Unlimited, 1K, 5K, 10K, 50K, 100K — cut at a sentence or word boundary | | +Without the native engine, espeak-ng and Piper fall back to their command-line programs (`espeak-ng`, and `piper-tts` from `piper-tts-bin`). -### Keyboard Shortcuts +While the checkout's own app is the one running, the global shortcut it registers runs the checkout's `usr/bin/biglinux-tts-speak`. -| Shortcut | Action | -|----------|--------| -| Alt+V (default) | Speak/stop selected text (toggle) | -| Ctrl+H | Open History | -| Escape / Alt+Left | Back from History | -| F10 | Open the main menu | -| Ctrl+Q | Quit application | +### Uninstalling -Changing the shortcut (Advanced options → Keyboard shortcut) registers it with the desktop and only reports success once the desktop confirms it. On KDE Plasma this uses KGlobalAccel over D-Bus: a key already used by another action is reported by name and the previous shortcut is kept. A shortcut needs Ctrl, Alt or Super (or a function key) so normal typing is never captured. GNOME, XFCE and Cinnamon use their own settings. +```bash +sudo pacman -R tts-biglinux +``` ---- +Removing the package leaves your personal data in your home folder: settings, history and downloaded Kokoro voices. To remove it as well, delete those folders (see [Configuration and files](#configuration-and-files)). -## TTS Engines +### Dependencies -### 1. RHVoice +Every required package comes from the official Arch/Manjaro repositories. This list matches `pkgbuild/PKGBUILD` (`epoch=1`, `pkgver=4.1.0`, `pkgrel=5`). -High-quality multilingual TTS — **the default engine**, installed with the package together with the Brazilian Portuguese voice *Letícia*. Played as `RHVoice-test | aplay` (no speech-dispatcher), both for reading and for the Voice Manager preview; the text goes through stdin, so its size is not limited. +
+Required (depends) -| Voice | Language | Quality | -|-------|----------|---------| -| Letícia F123 | pt-BR | ★★★★ | -| Evgeniy | English | ★★★★ | -| + others | Multiple | ★★★–★★★★ | +| Package | Why | +| --- | --- | +| `python`, `python-gobject` | Python 3 and PyGObject | +| `gtk4`, `libadwaita>=1:1.5` | Interface (`Adw.AlertDialog`, `Adw.OverlaySplitView`, `Adw.Breakpoint`) | +| `python-numpy` | Kokoro voice downloads (`.pt` → `.npy` conversion) | +| `polkit` | `pkexec` to install or remove voice packages | +| `onnxruntime` | Piper ONNX inference in the native engine (provided by `onnxruntime-cpu` and variants) | +| `espeak-ng` | espeak voices and the Piper phonemizer (linked by `tts_engine.so`) | +| `alsa-utils` | `aplay`: every engine plays through it | +| `alsa-lib` | Linked by `tts_engine.so` (rodio/cpal) | +| `sox` | Volume on the subprocess fallback paths | +| `gstreamer`, `gst-plugins-base`, `gst-plugins-good` | History player (`playbin`, `wavparse`, audio sinks) | +| `rhvoice`, `rhvoice-voice-leticia-f123` | Default engine and voice | +| `wl-clipboard` | Selected text on Wayland (`wl-paste`, also provided by `wl-clipboard-rs`) | +| `xsel` | Selected text on X11 | +| `xdg-utils` | `xdg-open`, the History *Open file location* fallback | +| `pyside6`, `qt6-svg` | System tray icon (the tray icons are SVG) | + +
+ +
+Build and check (makedepends, checkdepends) -Settings from versions that used the old speech-dispatcher backend are moved to RHVoice. +| Package | Why | +| --- | --- | +| `git` | Fetch the sources | +| `rust>=1.85`, `cargo` | Build `tts_engine.so` | +| `pkgconf` | `alsa-sys` finds `alsa-lib` through pkg-config | +| `python-pytest` | `check()` runs the Python test suite | -### 2. espeak-ng (Native FFI) ⚡ +
-Direct C FFI to `libespeak-ng.so` from the Rust engine — no subprocess for synthesis. +
+Optional (optdepends) -- `AUDIO_OUTPUT_SYNCHRONOUS` mode: espeak-ng renders PCM and never opens the audio device itself (it is also Piper's phonemizer) -- One-time initialization via `OnceLock`, all calls serialized -- Texts longer than 600 characters are streamed in sentence chunks: sound starts at once, memory stays bounded and Stop takes effect between chunks (the command-line fallback reads the text from stdin) -- 100+ languages with basic quality +| Package | Repository | Why | +| --- | --- | --- | +| `rhvoice-voice-evgeniy-eng` | official | RHVoice English male voice | +| `xclip` | official | Selection capture on X11 when `xsel` is not available | +| `biglinux-kokoro-tts` | BigLinux | Kokoro neural voices (`koko`, model, base voices) | +| `piper-voices-pt-br` | BigLinux | Brazilian Portuguese Piper voices, preinstalled (`piper-voices-pt-BR` in testing) | +| `piper-tts-bin` | BigLinux | Piper command-line fallback (the native engine needs only the voices) | +| `rhvoice-brazilian-portuguese-complementary-dict-biglinux` | BigLinux | Extra pronunciation dictionary for RHVoice | +| `paru` (or `yay`, or `pamac` with AUR enabled) | AUR / BigLinux | Build Piper packages from the AUR when your repositories do not have them | -### 3. Piper (Native ONNX Inference) ★★★★★ +
-Neural TTS with near-human quality. ONNX models run locally through the `ort` crate — the `piper-tts` binary is only a fallback. +`speech-dispatcher` is not a dependency of this package. The `rhvoice` package may still pull it in, but this app never starts it. -**Pipeline**: text → espeak-ng IPA phonemes (FFI) → phoneme IDs → ONNX model → WAV → `aplay` +--- -| Feature | Detail | -|---------|--------| -| Runtime | `ort` 2.0 (system ONNX Runtime, dynamic link) | -| Model cache | load once, reuse; prewarmed in the background after startup | -| Long text | sentence chunks synthesized while the previous one plays | -| Performance | 7× faster than the subprocess for short text | +## Usage -### 4. Kokoro (Neural TTS) ★★★★★ +### The window -Neural voices in Portuguese, English, Spanish, French, Italian, Japanese, Hindi and Chinese, with voice blending and expression presets. It runs through the `koko` binary from the `biglinux-kokoro-tts` package (model + base voices). +| Area | What it does | +| --- | --- | +| **Ready to speak** card | Current state (ready / loading voice / speaking / stopped / error with the fix) and the shortcut drawn as keys, plus whether the desktop confirmed it | +| **Voice settings** (sidebar) | Engine (*TTS Backend*), **Engine status** of all four engines, voice, speed, pitch, volume; for Kokoro also *Expression style* and *Voice blend* | +| **Test voice** | Type a short text and hear it with the current settings | +| Text options | Expand abbreviations, read special characters, remove formatting, read URLs, character limit | +| **Advanced options** | Keyboard shortcut, tray icon, history (save, keep audio, keep text, keep at most), audio playback mode, media player | +| Main menu (F10) | History, Show tray icon, Restore Defaults…, Welcome, About BigLinux TTS, Quit | -- One command builder (`kokoro_voice_service.build_koko_command`) serves both reading and the Voice Manager preview: explicit model, voices file, language from the voice prefix, and a private writable work directory (`$XDG_RUNTIME_DIR/biglinux-tts/koko`) -- `koko pipe` streams sentence by sentence (sentences longer than 220 characters are split, punctuation runs collapsed); the card shows *Loading voice* until koko reports that audio started, and a crash of koko is reported as an error -- The selected voice is checked against `voices.bin` before speaking (koko would silently use another voice) -- Voice blending: mix two voices (`voice.W+voice.W`); expression presets adjust the speed -- More voices can be downloaded in the Voice Manager +Engine status shows each engine as *Not installed*, *No voices* or *Ready*. For the selected engine it can also show *Loading*, *Speaking* or *Error*. -### Automatic Voice Discovery +The slider ranges are speed −100…+100, pitch −100…+100 and volume 0…100. The defaults are −25, −25 and 75. Piper has no real pitch control, so the pitch row is relabeled *Expressiveness* for Piper. Kokoro does not follow pitch or volume levels; see [Speech engines](#speech-engines). -All engines are scanned in parallel in a background thread: +### Global shortcut -1. **RHVoice**: scans `/usr/share/RHVoice/voices/` (speech-dispatcher is not queried: starting it can make other installed modules speak) -2. **espeak-ng**: `espeak-ng --voices` → language code, gender -3. **Piper**: scans `/usr/share/piper-voices/` and `~/.local/share/piper-voices/` for `.onnx` + `.onnx.json` -4. **Kokoro**: `koko voices` on the active `voices.bin` (system base voices + user downloads) +The default is Alt+V. You can change it in **Advanced options → Keyboard shortcut**. The shortcut must include Ctrl, Alt or Super (or be a function key), so that normal typing is never captured. -Result: a `VoiceCatalog` with every voice plus, per engine, whether it is installed and has voices (shown as *Engine status*). +When pressed, it runs `biglinux-tts-speak`, which hands the request to the running app or starts one: ---- +1. While a reading is loading or playing, it **stops** it (in the default *Interrupt previous* playback mode). +2. If text is selected, it **reads** it with the configured engine and voice. +3. If nothing is selected, a short notification says so. -## Architecture +| Desktop | How the shortcut is registered | +| --- | --- | +| KDE Plasma | KGlobalAccel over D-Bus (`doRegister`, `setForeignShortcut`), then read back with `allShortcutInfos`. Other actions using the key are found with `getGlobalShortcutsByKey`, reported by name, and the previous key is kept. If KGlobalAccel cannot be reached, the app writes `kglobalshortcutsrc` instead. | +| GNOME (and Unity) | `gsettings`: a custom keybinding in `org.gnome.settings-daemon.plugins.media-keys` | +| Cinnamon | `gsettings`: `org.cinnamon.desktop.keybindings` custom list | +| Xfce | `xfconf-query`, channel `xfce4-keyboard-shortcuts` | +| Any other desktop | The app does not claim a shortcut. Add one in your desktop's keyboard settings with the command **`biglinux-tts-speak`**. | -``` -┌──────────────────────────────────────────────────────────────────────────┐ -│ main.py — CLI args (--speak, --debug, --version), logging, App.run() │ -├──────────────────────────────────────────────────────────────────────────┤ -│ application.py — TTSApplication (Adw.Application, single instance) │ -│ startup → shortcut registration (worker) → tray → MPRIS → prewarm │ -│ --speak: capture selection (worker) → speak / stop (toggle) │ -├────────────────────────┬────────────────────────┬────────────────────────┤ -│ UI layer (GTK thread) │ Service layer │ Data layer │ -├────────────────────────┼────────────────────────┼────────────────────────┤ -│ window.py │ tts_service.py │ config.py │ -│ ├ OverlaySplitView │ ├ speak / stop │ ├ AppSettings │ -│ ├ header + main menu │ ├ LOADING/SPEAKING/ │ ├ TTSBackend/TTSState │ -│ └ status bar │ │ ERROR + reasons │ └ JSON load/save │ -│ main_view.py │ └ per-engine paths │ settings_service.py │ -│ ├ Ready to speak card │ shortcut_service.py │ └ debounced save │ -│ ├ engine status │ └ KGlobalAccel D-Bus │ history_db.py │ -│ └ voice settings │ voice_manager.py │ └ SQLite + retention │ -│ components.py │ └ discovery + status │ │ -│ └ ShortcutKeys, pills │ kokoro_voice_service.py│ │ -│ voice_manager_dialog.py│ └ koko argv, downloads│ │ -│ history_view.py │ clipboard_service.py │ │ -│ audio_player.py (Gst) │ tray_service.py ──────────▶ PySide6 subprocess │ -│ welcome_dialog.py │ tray_icon_frames.py │ (QSystemTrayIcon) │ -│ │ mpris_service.py │ │ -│ │ desktop_integration_* │ │ -├────────────────────────┴────────────────────────┴────────────────────────┤ -│ tts_engine.so (Rust / PyO3) — GIL released during synthesis │ -│ espeak-ng FFI (synchronous) · Piper ONNX via ort (model cache) │ -├──────────────────────────────────────────────────────────────────────────┤ -│ External: RHVoice-test · koko · aplay · wl-paste/xsel · pkexec pacman │ -└──────────────────────────────────────────────────────────────────────────┘ -``` +Registration runs in a background thread when the app starts. The card, Advanced options, the status bar and the tray tooltip update when the desktop answers. -### Request Lifecycle (TTS State Machine) +### Tray icon and media controls -``` - speak() audio starts - IDLE ─────────────────▶ LOADING ─────────────────────▶ SPEAKING - ▲ │ (synthesizing, │ - │ │ loading the model) │ playback ends - │ stop() / Alt+V │ │ - ├────────────────────────┴──────────────────────────────┤ - │ │ - │ engine failed │ - │ ┌──────────────── ERROR ◀───────────────────┘ - └────────────┘ (reason + recovery action kept until the next speak/stop) -``` +The tray icon is on by default. You can turn it off in Advanced options or with main menu → *Show tray icon*. -- `LOADING` is real: engines that synthesize first (Piper, Kokoro, espeak-ng) switch to `SPEAKING` when the player starts, or when `koko` reports *Streaming audio* -- `ERROR` carries a translated message and an action (`voice-manager` or `retry`); it is never reset to idle behind the user's back -- A monotonic request generation discards stale audio from an interrupted request -- State listeners always run on the GTK main thread; stale notifications from workers are dropped +| Action | Idle | While reading | +| --- | --- | --- | +| Left click | Read the selected text | Stop | +| Right-click menu | Read text · Settings · Quit | Pause / Play · Stop · Settings · Quit | +| Icon | Static | Pulses (opacity only). Dims while paused. Stays steady when reduced motion is on | +| Tooltip | *Select text and press Alt+V to hear it* | *Playing…* / *Paused* | ---- +With the tray icon on, closing the window hides it to the tray. **Pause** freezes the player in place and **Play** continues from the same point. -## Installation +The **MPRIS** player (`org.mpris.MediaPlayer2.biglinux-tts`) shows a system mini-player with Play/Pause, Stop and progress while a reading plays. It responds to media keys, and Play when idle reads the last text again. You can turn it off in Advanced options → *Media player*. -### BigLinux / Manjaro / Arch Linux +Notifications show what is being read, and give a clear message when something fails or nothing is selected. -On BigLinux the package is in the BigLinux repositories: +### Command line -```bash -sudo pacman -S tts-biglinux -``` +| Command | What it does | +| --- | --- | +| `biglinux-tts` | Open the window, or bring the running one forward | +| `biglinux-tts --speak` | Read or stop the selected text (the shortcut action) | +| `biglinux-tts-speak` | Same as `biglinux-tts --speak`; this is the command bound to the shortcut | +| `biglinux-tts --debug` | Detailed logging on the terminal | +| `biglinux-tts --version` | Print the version | -On Manjaro or Arch Linux, build it from the PKGBUILD — every dependency is in the official repositories (`core`, `extra`), no BigLinux or Big Community repository is needed: +The app runs as a single instance (GApplication `br.com.biglinux.tts`). A second command is passed to the instance that is already running. After the last reading, the app stays alive for a few seconds, so a quick second Alt+V reuses it. -```bash -git clone https://github.com/biglinux/tts-biglinux.git -cd tts-biglinux/pkgbuild -makepkg -si -``` +### Playback modes -This installs the application, the native engine, RHVoice with the Brazilian Portuguese voice *Letícia*, espeak-ng (voices for 100+ languages), the tray icon (PySide6), the History player (GStreamer) and the clipboard tools — everything needed to select text and press Alt+V. +You set this in **Advanced options → Audio playback mode**: -Optional voices: +| Mode | A new reading while one is playing… | +| --- | --- | +| **Interrupt previous** (default) | replaces it. The shortcut works as a toggle and stops a reading in progress | +| **Queue** | waits and plays when the current one ends. Stop also clears the queue | +| **Simultaneous** | plays alongside it. Stop and Pause apply to every reading | -```bash -# English RHVoice voice (official repositories) -sudo pacman -S rhvoice-voice-evgeniy-eng +### History -# BigLinux repositories only: -sudo pacman -S biglinux-kokoro-tts # Kokoro neural voices -sudo pacman -S piper-voices-pt-br # Brazilian Portuguese Piper voices (stable) -sudo pacman -S piper-voices-pt-BR # the same voices, as named in biglinux-testing -``` +Open it from the main menu → **History** (Ctrl+H). Saving is on for new installations. Settings from earlier versions keep their own choice, and History is never switched on silently for an existing user. -Piper voices can also be downloaded in the Voice Manager. +- **What is kept** for each reading that was actually heard: the original text, the processed text, engine, voice, date and time, duration, outcome (*completed*, *stopped* by you, or *error* after sound started), and the audio. +- **The audio is what was played**, not a second synthesis. A stopped reading keeps the audio up to the point where you stopped it. -The Piper voice packages are spelled differently in BigLinux stable -(`piper-voices-pt-br`) and testing (`piper-voices-pt-BR`); both ship the same -files, so only one can be installed. When Piper is selected, the app looks -for both spellings and never offers the second one if either is installed. -Only when neither is in the repositories does it fall back to the AUR. It -builds the AUR package as you, never as root, with `pamac build` when pamac's -AUR support is on, or with `paru`/`yay`, and it says so before installing. + | Engine | How the audio is captured | + |---|---| + | RHVoice | `RHVoice-test` writes WAV to stdout. The app sends it to `aplay` and writes the same bytes to the history file | + | espeak-ng, Piper | Each WAV chunk is appended to the history file when it starts playing. A long text gives one file for the whole reading | + | Kokoro | `koko pipe -o ` writes everything it plays straight into the history file | -### Uninstall +- **The page** shows cards with the text (expandable), engine, voice, date, duration and outcome, a player, and actions: copy, *Read again with the current voice*, *Save audio as…*, open the folder, delete. You can search (text, voice, engine, date), filter (all dates, today, yesterday, last 7 days, engine), sort newest or oldest first, switch between list and grid, and select several entries to save them to a folder or delete them. New readings appear on their own, and entries load in pages as you scroll. +- **Deleting is safe.** A single entry can be undone with *Undo*, a selection asks for confirmation first, and the files go to the **Trash**. Files you restore from the Trash come back as entries on the next start. +- **Recovery runs at every start**, outside the main thread. A damaged index is set aside as `history.db.damaged-…` and rebuilt from the files. Files without an entry come back, a recording interrupted by a crash is finished and kept, and a legacy `history.json` is migrated once. Recovery never deletes anything that was saved. +- **Limits** are set in *Keep at most* (100, 500, 1000, 5000 or unlimited). Saving runs in a background thread: it never delays the start of speech, and a failure such as a full disk or a missing permission never stops a reading. -```bash -sudo pacman -R tts-biglinux -``` +### Voice Manager -Personal data stays in your home folder (settings, history, downloaded voices — see [File Locations](#file-locations)); delete those folders to remove it too. +- Install and remove voices for RHVoice, Piper and espeak-ng (pacman packages through `pkexec`) and Kokoro (downloaded voice files). +- Before installing, the app checks the package with `pacman -Si`. It only ever runs `pkexec pacman -S --needed`, never `-Sy`, because refreshing the databases without upgrading would be a partial upgrade. +- Each voice has one button to play and stop a sample. You can search by name or language, and the size is shown before you install. +- Kokoro downloads show real progress and have a **Cancel** button. Downloads are verified (incomplete or damaged files are rejected) and free space is checked first. `voices.bin` is updated atomically. +- Removing a voice asks for confirmation. -### Run without Installing (Development) +### Keyboard shortcuts in the window -```bash -git clone https://github.com/biglinux/tts-biglinux.git -cd tts-biglinux +| Shortcut | Action | +| --- | --- | +| Alt+V (global, configurable) | Read / stop the selected text | +| Ctrl+H | Open History | +| Esc / Alt+← | Back from History | +| F10 | Open the main menu | +| Ctrl+Q | Quit | -# Build the native Rust engine against the SYSTEM onnxruntime -cd tts-engine -ORT_LIB_LOCATION=/usr/lib ORT_PREFER_DYNAMIC_LINK=1 cargo build --release -cd .. +--- -# Link the .so into the application directory -ln -sf ../../../../tts-engine/target/release/libtts_engine.so \ - usr/share/biglinux/tts-biglinux/tts_engine.so +## Speech engines -# Run -cd usr/share/biglinux/tts-biglinux -python main.py --debug -``` +| | RHVoice | espeak-ng | Piper | Kokoro | +| --- | --- | --- | --- | --- | +| **Kind** | Parametric, multilingual | Formant, very light | Neural (ONNX) | Neural (ONNX) | +| **Default** | **Yes**, voice *Letícia F123* (pt-BR) | | | | +| **Comes with the package** | Yes (`rhvoice`, `rhvoice-voice-leticia-f123`) | Yes (`espeak-ng`) | Engine yes, voices optional | No (`biglinux-kokoro-tts`) | +| **How it runs** | `RHVoice-test` → WAV on stdout → `aplay` | Native FFI to `libespeak-ng`. Fallback: `espeak-ng` command | Native ONNX via the Rust engine. Fallback: `piper-tts` command | `koko pipe` (sentence by sentence) | +| **Languages** | Voices installed (pt-BR, English, others) | 100+ | Voices installed | pt-BR, en-US, en-GB, es, fr, it, hi, ja, zh | +| **Speed** | Yes | Yes | Yes | Yes (speed × expression preset) | +| **Pitch** | Yes | Yes | Shown as *Expressiveness* (noise scale) | No (slider disabled) | +| **Volume** | Yes | Yes | Yes | Only mute (0) | +| **Long texts** | One stream, text through stdin | 600-character chunks, streamed | 600-character chunks, streamed | One sentence per line, long sentences split | -### Dependencies +
+RHVoice (default) -#### Required (installed with the package) +- Installed with the package together with the Brazilian Portuguese voice *Letícia*. It plays as `RHVoice-test | aplay` for reading and for the Voice Manager preview. +- The text goes through stdin, so there is no size limit. +- Voices are found by scanning `/usr/share/RHVoice/voices/` and `/usr/local/share/RHVoice/voices/`. Speech Dispatcher is not queried. +- Settings that still name the removed `speech-dispatcher` engine (dropped in 4.0) are switched to RHVoice. -All from the official Arch/Manjaro repositories. +
-| Package | Why | -|---------|-----| -| `python`, `python-gobject` | Python 3 and PyGObject | -| `gtk4`, `libadwaita>=1:1.5` | Interface (AlertDialog, OverlaySplitView, Breakpoint) | -| `python-numpy` | Kokoro voice download conversion (`.pt` → `.npy`) | -| `polkit` | `pkexec` to install/remove voice packages | -| `onnxruntime` | Piper neural inference (system library, dynamic link; provided by `onnxruntime-cpu` and variants) | -| `espeak-ng` | espeak voices and Piper phonemizer (linked by `tts_engine.so`) | -| `alsa-utils`, `alsa-lib` | `aplay` — every engine plays through it; `libasound` for the native engine | -| `sox` | Volume control on the subprocess fallback paths | -| `gstreamer`, `gst-plugins-base`, `gst-plugins-good` | History player (`playbin`, `wavparse`, audio sink) | -| `rhvoice`, `rhvoice-voice-leticia-f123` | Default engine and voice, ready out of the box | -| `speech-dispatcher` | Required by RHVoice's package (the app itself never starts it) | -| `wl-clipboard`, `xsel` | Selected text on Wayland (`wl-paste`; `wl-clipboard-rs` also provides it) and X11 | -| `pyside6`, `qt6-svg` | System tray icon (SVG icons) | - -#### Build and check +
+espeak-ng -| Package | Why | -|---------|-----| -| `git`, `rust>=1.85`, `cargo` | Fetch sources, build `tts_engine.so` (also needs `pkgconf` from `base-devel`) | -| `python-pytest` | `check()` runs the test suite | +- Calls `libespeak-ng.so` directly from the Rust engine, with no synthesis subprocess. It runs in `AUDIO_OUTPUT_SYNCHRONOUS` mode, so espeak-ng renders PCM and never opens the audio device itself. espeak-ng is also Piper's phonemizer. +- It is initialized once (`OnceLock`) and all calls are serialized. +- Texts longer than 600 characters are streamed in sentence chunks. Sound starts right away, memory stays bounded, and Stop takes effect between chunks. +- Without the native engine, the `espeak-ng` command is used. A hung process is stopped after 120 s of work (time spent paused does not count). +- Voices come from `espeak-ng --voices`. -#### Optional +
-| Package | Repository | Why | -|---------|------------|-----| -| `rhvoice-voice-evgeniy-eng` | official | RHVoice English voice | -| `xclip` | official | X11 selection capture when `xsel` is missing | -| `biglinux-kokoro-tts` | BigLinux | Kokoro neural voices (`koko`, model, base voices) | -| `piper-voices-pt-br` | BigLinux | Brazilian Portuguese Piper voices, preinstalled (`piper-voices-pt-BR` in testing) | -| `piper-tts-bin` | BigLinux | Piper command-line fallback (the native engine needs only voices) | -| `rhvoice-brazilian-portuguese-complementary-dict-biglinux` | BigLinux | Extra pronunciation dictionary for RHVoice | +
+Piper ---- +- **Pipeline:** text → espeak-ng IPA phonemes (FFI) → phoneme IDs → ONNX model (`ort` crate, system ONNX Runtime, dynamic link) → WAV → `aplay`. +- The model is loaded once and cached. The selected voice is prewarmed in the background after startup, with no audio, so the first Alt+V starts faster. +- On long texts, the next chunk is synthesized while the current one plays. +- If the native engine is missing or fails, the `piper-tts` command (`piper-tts-bin`) is used, and its output is played through sox `play` so the volume still applies. +- Voices (`.onnx` + `.onnx.json`) are found in `/usr/share/piper-voices/`, `/usr/local/share/piper-voices/` and `~/.local/share/piper-voices/`. -## Usage +
-### GUI +
+Kokoro -```bash -biglinux-tts # Open the window (or bring the running one forward) -biglinux-tts --debug # Detailed logging -biglinux-tts --version # Print the version -``` +- Runs the `koko` binary from `biglinux-kokoro-tts` (model in `/usr/share/biglinux-kokoro-tts/model/`, base voices in `/usr/share/biglinux-kokoro-tts/voices/voices.bin`). +- One builder (`kokoro_voice_service.build_koko_command`) creates the command for both reading and the Voice Manager preview. It passes an explicit model, the voices file, the language from the voice prefix (`pf_`/`pm_` → pt-br, `af_` → en-us, …), and a private writable work directory (`$XDG_RUNTIME_DIR/biglinux-tts/koko`). koko fails with *Permission denied* when it runs in the read-only install folder. +- It uses `koko pipe -o `: one sentence per line, played as soon as it is synthesized, with everything written to a WAV file (the history recording). `koko stream` is not used because it mixes log lines into the audio. +- The card shows *Loading voice* until koko reports *Streaming audio*. A crash of koko is reported as an error. +- The selected voice is checked against `voices.bin` before speaking, because koko would otherwise use another voice without saying so. +- **Voice blending** mixes a second voice (`voice.W+voice.W`). **Expression presets** (neutral, happy, calm, urgent, narrative) adjust the speed. +- **Pitch** is disabled, because koko has no such option. **Volume** only honors mute (0); use the system volume for the level. +- You can download more voices in the Voice Manager. They are stored in `~/.local/share/biglinux-tts/kokoro-voices/voices.bin`, together with the base voices. -### Keyboard Shortcut (CLI) +
-```bash -biglinux-tts-speak # What Alt+V runs: biglinux-tts --speak -``` +All engines are scanned in parallel in a background thread. The result is a voice catalog, plus each engine's status (installed, has voices) as shown under *Engine status*. -`--speak` is forwarded to the running instance (GApplication) or starts one: +--- -1. Reading or loading a voice → stop (in the default *interrupt* playback mode) -2. Text selected → read it with the configured engine and voice -3. Nothing selected → a short notification says so +## Text processing -### Typical Workflow +`services/text_processor.py` prepares the text before synthesis. The rules follow the **language of the selected voice**, so an English voice on a Portuguese desktop gets English rules. The options apply to every engine. -1. **First launch**: the welcome dialog explains features and setup -2. **Configure**: choose the engine and voice, adjust speed/pitch/volume -3. **Test**: type text in the test field and press *Test voice* (or Enter) -4. **Daily use**: select text anywhere → Alt+V → listen +| Option | What it does | Example | +| --- | --- | --- | +| Expand abbreviations | Expands slang and titles (pt, en, es). Never applied inside e-mail addresses, URLs or hyphenated words | `vc` → "você", `Dr. Silva` → "doutor Silva", `w/o` → "without" | +| Read special characters | Reads symbols by name (pt, en, es). When off, the symbols that engines would read aloud are removed | `#` → "cerquilha" / "hash" | +| Remove formatting | Removes HTML tags and Markdown markers. Headings keep a pause and numbered lists keep their numbers | `**bold**` → "bold" | +| Read URLs | On: reads only the domain. Off: removes the URL | `https://www.example.com/a?b=1` → "example.com" | +| Character limit | Unlimited, 1K, 5K, 10K, 50K or 100K, cut at a sentence or word boundary | | +| Number normalization | Portuguese voices: numbers, money, percentages, dates and times as words. On by default, with no switch in the window (settings key `text.normalize_numbers`) | `R$ 1.234,56`, `08/10/2026`, `21:00` → "vinte e uma horas" | + +
+Pipeline order + +1. **Character limit:** cut at a sentence or word boundary. +2. **Strip formatting:** only real HTML tags are removed (`x < 5 e y > 3` stays), plus Markdown bold, italic, code, links, headings and bullets. HTML entities are always decoded. +3. **URLs:** replaced by the domain, or removed together with the space before them. +4. **Numbers** (pt): numbers, money, percentages, dates and times (feminine hours, seconds). This step runs before special characters, so `%` and `R$` become words. +5. **Abbreviations:** one precompiled pattern per language, and titles drop their period. With the option off, slang is spelled out so RHVoice does not expand it on its own (real words are left alone). +6. **Special characters:** read by name, or the symbols espeak-ng-based engines would read are removed. +7. **Cleanup:** spaces are collapsed and paragraph breaks are kept as pauses. + +
--- -## Configuration - -### File Locations +## Configuration and files | Path | Content | -|------|---------| -| `~/.config/biglinux-tts/settings.json` | All app settings (JSON) | -| `~/Music/tts-biglinux/` (XDG Music folder) | Reading history: `