mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-19 06:40:38 -07:00
docs
This commit is contained in:
@@ -1,195 +0,0 @@
|
||||
---
|
||||
title: "Auto-Updater Documentation"
|
||||
description: "How Voicebox automatic updates work for users and developers"
|
||||
---
|
||||
|
||||
Voicebox includes automatic updates powered by Tauri's updater plugin. This document explains how it works for both users and developers.
|
||||
|
||||
## 1. Generate Signing Keys
|
||||
|
||||
Run this command to generate your signing keypair:
|
||||
|
||||
```bash
|
||||
cd tauri && bun tauri signer generate -w ~/.tauri/voicebox.key
|
||||
```
|
||||
|
||||
This creates:
|
||||
- **Private key**: `~/.tauri/voicebox.key` (keep this secret!)
|
||||
- **Public key**: `~/.tauri/voicebox.key.pub`
|
||||
|
||||
## 2. Update Configuration
|
||||
|
||||
Copy the content from `~/.tauri/voicebox.key.pub` and replace the placeholder in `tauri/src-tauri/tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "PASTE_PUBLIC_KEY_CONTENT_HERE",
|
||||
"endpoints": [
|
||||
"https://github.com/YOUR_USERNAME/voicebox/releases/latest/download/latest.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Update the endpoint URL with your actual GitHub username/organization.
|
||||
|
||||
## 3. Building with Signatures
|
||||
|
||||
When building releases, set these environment variables:
|
||||
|
||||
**macOS/Linux:**
|
||||
```bash
|
||||
export TAURI_SIGNING_PRIVATE_KEY="$(cat ~/.tauri/voicebox.key)"
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
|
||||
bun run build
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
$env:TAURI_SIGNING_PRIVATE_KEY = Get-Content ~/.tauri/voicebox.key -Raw
|
||||
$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD = ""
|
||||
bun run build
|
||||
```
|
||||
|
||||
## 4. GitHub Release Setup
|
||||
|
||||
When you create a GitHub release, the build process will generate:
|
||||
- Installers for each platform
|
||||
- `.sig` signature files
|
||||
- `latest.json` update manifest
|
||||
|
||||
### Manual Release Process
|
||||
|
||||
1. Build the app with signing keys set
|
||||
2. Create a new GitHub release
|
||||
3. Upload all files from `tauri/src-tauri/target/release/bundle/`
|
||||
4. Create `latest.json` in your release assets:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"notes": "Bug fixes and improvements",
|
||||
"pub_date": "2026-01-25T12:00:00Z",
|
||||
"platforms": {
|
||||
"darwin-aarch64": {
|
||||
"signature": "CONTENT_FROM_.app.tar.gz.sig",
|
||||
"url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_aarch64.dmg"
|
||||
},
|
||||
"darwin-x86_64": {
|
||||
"signature": "CONTENT_FROM_.app.tar.gz.sig",
|
||||
"url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64.dmg"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"signature": "CONTENT_FROM_.AppImage.sig",
|
||||
"url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_amd64.AppImage"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"signature": "CONTENT_FROM_.msi.sig",
|
||||
"url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64_en-US.msi"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Automated GitHub Actions (Recommended)
|
||||
|
||||
Create `.github/workflows/release.yml`:
|
||||
|
||||
```yaml
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [macos-latest, ubuntu-22.04, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install dependencies (Ubuntu)
|
||||
if: matrix.platform == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: bun run build
|
||||
|
||||
- name: Upload Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: tauri/src-tauri/target/release/bundle/**/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
Add your private key to GitHub secrets:
|
||||
- Go to Settings → Secrets and variables → Actions
|
||||
- Add `TAURI_SIGNING_PRIVATE_KEY` with the content of `~/.tauri/voicebox.key`
|
||||
- Add `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (empty string if no password)
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
The frontend integration is complete with automatic update notifications and manual update checks:
|
||||
|
||||
- **Update Notification Banner** - Appears automatically when updates are available
|
||||
- **Settings Panel** - Manual "Check for Updates" button in Settings tab
|
||||
- **Update Hook** - React hook handles all update operations
|
||||
|
||||
See `docs/AUTOUPDATER_QUICKSTART.md` for a quick setup guide.
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Never commit your private key to version control
|
||||
- Store private keys securely (use GitHub secrets for CI/CD)
|
||||
- The public key in `tauri.conf.json` is safe to commit
|
||||
- Updates are cryptographically verified before installation
|
||||
- HTTP endpoints are blocked by default (HTTPS only)
|
||||
|
||||
## Testing Updates
|
||||
|
||||
1. Build version 0.1.0 and install it
|
||||
2. Update version in `tauri.conf.json` to 0.2.0
|
||||
3. Build version 0.2.0 with signatures
|
||||
4. Create a local server or GitHub release with `latest.json`
|
||||
5. Run version 0.1.0 and trigger update check
|
||||
6. Verify update downloads and installs correctly
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Invalid signature" error:**
|
||||
- Verify public key matches the private key used to sign
|
||||
- Ensure signature files (.sig) are uploaded correctly
|
||||
|
||||
**"No update available" when one exists:**
|
||||
- Check endpoint URL is correct
|
||||
- Verify `latest.json` format matches specification
|
||||
- Ensure version in latest.json is higher than current version
|
||||
|
||||
**Build fails with signing:**
|
||||
- Confirm environment variables are set correctly
|
||||
- Check private key file exists and is readable
|
||||
- Verify private key format (should start with `dW50cnVzdGVkIGNvbW1lbnQ6`)
|
||||
@@ -1,119 +0,0 @@
|
||||
---
|
||||
title: "Autoupdater Quick Start"
|
||||
description: "Quick guide to activate the Tauri v2 autoupdater"
|
||||
---
|
||||
|
||||
The Tauri v2 autoupdater has been fully configured and integrated. Follow these steps to activate it.
|
||||
|
||||
## What's Already Done
|
||||
|
||||
✅ Rust plugin installed and initialized
|
||||
✅ Tauri configuration set up with updater settings
|
||||
✅ Permissions granted for update operations
|
||||
✅ GitHub Actions workflow updated with signing support
|
||||
✅ Frontend components created and integrated
|
||||
✅ Update notifications on app startup
|
||||
✅ Manual update check in Settings tab
|
||||
|
||||
## Required Steps (5 minutes)
|
||||
|
||||
### 1. Generate Signing Keys
|
||||
|
||||
```bash
|
||||
bun run generate:keys
|
||||
```
|
||||
|
||||
This creates:
|
||||
- Private key: `~/.tauri/voicebox.key` (keep secret!)
|
||||
- Public key: `~/.tauri/voicebox.key.pub` (safe to share)
|
||||
|
||||
### 2. Update Tauri Config
|
||||
|
||||
Open `tauri/src-tauri/tauri.conf.json` and:
|
||||
|
||||
1. Replace `"REPLACE_WITH_YOUR_PUBLIC_KEY"` with the content from `~/.tauri/voicebox.key.pub`
|
||||
2. Update the endpoint URL with your GitHub username:
|
||||
```json
|
||||
"endpoints": [
|
||||
"https://github.com/YOUR_USERNAME/voicebox/releases/latest/download/latest.json"
|
||||
]
|
||||
```
|
||||
|
||||
### 3. Add GitHub Secrets
|
||||
|
||||
Go to your repo Settings → Secrets and variables → Actions:
|
||||
|
||||
1. Add `TAURI_SIGNING_PRIVATE_KEY`:
|
||||
```bash
|
||||
cat ~/.tauri/voicebox.key
|
||||
```
|
||||
Copy the entire output and paste as the secret value
|
||||
|
||||
2. Add `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`:
|
||||
Leave empty (or add your password if you set one)
|
||||
|
||||
### 4. Test the Setup
|
||||
|
||||
To test locally before creating a release:
|
||||
|
||||
```bash
|
||||
bun run build:release
|
||||
```
|
||||
|
||||
This will verify your keys are set up correctly.
|
||||
|
||||
## How It Works
|
||||
|
||||
### For Users
|
||||
1. App checks for updates on startup (only in Tauri builds)
|
||||
2. If an update is available, a banner appears at the top
|
||||
3. Users can click "Install Now" to download and install
|
||||
4. App restarts automatically after installation
|
||||
|
||||
### For Developers
|
||||
1. Create a new git tag: `git tag v0.2.0 && git push --tags`
|
||||
2. GitHub Actions builds signed releases for all platforms
|
||||
3. Uploads installers and generates `latest.json` manifest
|
||||
4. Users running older versions will be notified automatically
|
||||
|
||||
## UI Components
|
||||
|
||||
### Update Notification Banner
|
||||
- Shows at top of app when update is available
|
||||
- Appears automatically on startup
|
||||
- Displays download/install progress
|
||||
|
||||
### Settings Panel
|
||||
- Located in Settings tab
|
||||
- Shows current version
|
||||
- Manual "Check for Updates" button
|
||||
- Update status and progress
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Public key not configured"**
|
||||
- Make sure you copied the entire content from `voicebox.key.pub`
|
||||
- The key should start with `dW50cnVzdGVkIGNvbW1lbnQ6`
|
||||
|
||||
**"Failed to check for updates"**
|
||||
- Endpoint URL might be incorrect
|
||||
- No releases published yet (expected for first setup)
|
||||
|
||||
**Build fails with signing error**
|
||||
- Check that GitHub secrets are set correctly
|
||||
- Verify private key file exists at `~/.tauri/voicebox.key`
|
||||
|
||||
## Next Release Workflow
|
||||
|
||||
1. Update version in `tauri/src-tauri/tauri.conf.json`
|
||||
2. Commit changes
|
||||
3. Create and push tag: `git tag v0.2.0 && git push --tags`
|
||||
4. GitHub Actions will automatically build and create a draft release
|
||||
5. Review the release and publish it
|
||||
6. Users will be notified of the update
|
||||
|
||||
## See Also
|
||||
|
||||
- Full documentation: `docs/AUTOUPDATER.md`
|
||||
- Build script: `scripts/prepare-release.sh`
|
||||
- GitHub workflow: `.github/workflows/release.yml`
|
||||
@@ -26,19 +26,22 @@ These two layers communicate via HTTP, with the frontend making API requests to
|
||||
|
||||
### Component Structure
|
||||
|
||||
```
|
||||
app/src/
|
||||
├── components/ # React components
|
||||
│ ├── profiles/ # Voice profile UI
|
||||
│ ├── generation/ # Speech generation UI
|
||||
│ ├── stories/ # Timeline editor
|
||||
│ └── shared/ # Reusable components
|
||||
├── lib/ # Utilities
|
||||
│ ├── api/ # Generated API client
|
||||
│ └── utils/ # Helper functions
|
||||
├── hooks/ # React hooks
|
||||
└── stores/ # Zustand state stores
|
||||
```
|
||||
<Files>
|
||||
<Folder name="app/src" defaultOpen>
|
||||
<Folder name="components">
|
||||
<File name="profiles/" />
|
||||
<File name="generation/" />
|
||||
<File name="stories/" />
|
||||
<File name="shared/" />
|
||||
</Folder>
|
||||
<Folder name="lib">
|
||||
<File name="api/" />
|
||||
<File name="utils/" />
|
||||
</Folder>
|
||||
<Folder name="hooks" />
|
||||
<Folder name="stores" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### State Management
|
||||
|
||||
@@ -64,16 +67,69 @@ const useProfileStore = create((set) => ({
|
||||
|
||||
### API Structure
|
||||
|
||||
```python
|
||||
# main.py - API routes
|
||||
@app.post("/generate")
|
||||
async def generate_speech(request: GenerateRequest):
|
||||
# 1. Validate request
|
||||
# 2. Load voice profile
|
||||
# 3. Generate audio with TTS
|
||||
# 4. Save to database
|
||||
# 5. Return response
|
||||
```
|
||||
<Files>
|
||||
<Folder name="backend" defaultOpen>
|
||||
<File name="app.py" />
|
||||
<File name="main.py" />
|
||||
<File name="config.py" />
|
||||
<File name="models.py" />
|
||||
<File name="server.py" />
|
||||
<Folder name="routes">
|
||||
<File name="profiles.py" />
|
||||
<File name="generate.py" />
|
||||
<File name="history.py" />
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="services">
|
||||
<File name="generation.py" />
|
||||
<File name="task_queue.py" />
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="backends">
|
||||
<File name="__init__.py" />
|
||||
<File name="base.py" />
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="database">
|
||||
<File name="models.py" />
|
||||
<File name="session.py" />
|
||||
</Folder>
|
||||
<Folder name="utils">
|
||||
<File name="audio.py" />
|
||||
<File name="effects.py" />
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### Request Flow
|
||||
|
||||
HTTP request → **routes/** (validate input, parse params) → **services/** (business logic, orchestration) → **backends/** (TTS/STT inference) → **utils/** (audio processing)
|
||||
|
||||
Route handlers are intentionally thin. They validate input, delegate to a service function, and format the response. All business logic lives in `services/`.
|
||||
|
||||
### Key Modules
|
||||
|
||||
- **app.py** — FastAPI app factory, CORS, lifecycle events
|
||||
- **main.py** — Entry point (imports app, runs uvicorn)
|
||||
- **server.py** — Tauri sidecar launcher, parent-pid watchdog
|
||||
- **services/generation.py** — Single function handling all generation modes (generate, retry, regenerate)
|
||||
- **services/task_queue.py** — Serial generation queue for GPU inference
|
||||
- **backends/__init__.py** — Protocol definitions and backend factory
|
||||
- **backends/base.py** — Shared utilities across all engine implementations
|
||||
|
||||
### Backend Selection
|
||||
|
||||
The server detects the best inference backend at startup:
|
||||
|
||||
| Platform | Backend | Acceleration |
|
||||
|----------|---------|-------------|
|
||||
| macOS (Apple Silicon) | MLX | Metal / Neural Engine |
|
||||
| Windows / Linux (NVIDIA) | PyTorch | CUDA |
|
||||
| Linux (AMD) | PyTorch | ROCm |
|
||||
| Intel Arc | PyTorch | IPEX / XPU |
|
||||
| Windows (any GPU) | PyTorch | DirectML |
|
||||
| Any | PyTorch | CPU fallback |
|
||||
|
||||
### Data Model
|
||||
|
||||
@@ -89,11 +145,13 @@ The database uses three main tables:
|
||||
|
||||
### Rust Backend
|
||||
|
||||
```rust
|
||||
// Sidecar process management
|
||||
// File system access
|
||||
// Native integrations
|
||||
```
|
||||
<Files>
|
||||
<Folder name="tauri/src-tauri" defaultOpen>
|
||||
<File name="Cargo.toml" />
|
||||
<File name="src/" />
|
||||
<Folder name="binaries" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### Responsibilities
|
||||
|
||||
@@ -196,11 +254,11 @@ When a user generates speech, the data flows through the following stages:
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Development Setup" icon="code" href="/development/setup">
|
||||
<Cards>
|
||||
<Card title="Development Setup" href="/development/setup">
|
||||
Set up your dev environment
|
||||
</Card>
|
||||
<Card title="Contributing" icon="code-pull-request" href="/development/contributing">
|
||||
<Card title="Contributing" href="/development/contributing">
|
||||
Contribute to Voicebox
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
@@ -1,84 +1,226 @@
|
||||
---
|
||||
title: "Auto-Updater"
|
||||
description: "Configure and use the Tauri auto-updater"
|
||||
description: "How Voicebox automatic updates work"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Voicebox uses Tauri's built-in auto-updater to deliver updates to users automatically.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
For detailed setup instructions, see the existing documentation:
|
||||
|
||||
- [AUTOUPDATER_QUICKSTART.md](https://github.com/jamiepine/voicebox/blob/main/docs/AUTOUPDATER_QUICKSTART.md)
|
||||
- [AUTOUPDATER.md](https://github.com/jamiepine/voicebox/blob/main/docs/AUTOUPDATER.md)
|
||||
Voicebox uses Tauri's built-in auto-updater to deliver signed updates to users. The system verifies updates cryptographically before installation.
|
||||
|
||||
## How It Works
|
||||
|
||||
The auto-updater follows a secure update process:
|
||||
When Voicebox launches (in production Tauri builds only), it checks GitHub Releases for a `latest.json` manifest. If a newer version is available:
|
||||
|
||||
1. **Check for Updates** - The Voicebox app periodically checks GitHub Releases for new versions
|
||||
2. **Download Update** - If a new version is found, the update package is downloaded
|
||||
3. **Verify Signature** - The downloaded package is cryptographically verified using the public key
|
||||
4. **Install** - After verification, the update is installed
|
||||
5. **Restart** - The app restarts with the new version
|
||||
1. **Notification** - An update banner appears at the top of the app
|
||||
2. **Download** - User clicks "Install Now" to download the update package
|
||||
3. **Verification** - The downloaded package is cryptographically verified using the public key embedded in `tauri.conf.json`
|
||||
4. **Installation** - After verification, the update is installed
|
||||
5. **Restart** - The app restarts automatically with the new version
|
||||
|
||||
Users can also check for updates manually via **Settings → Check for Updates**.
|
||||
|
||||
## Configuration
|
||||
|
||||
Updates are configured in `tauri/src-tauri/tauri.conf.json`:
|
||||
The updater is configured in `tauri/src-tauri/tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": [
|
||||
"https://github.com/jamiepine/voicebox/releases/latest/download/latest.json"
|
||||
],
|
||||
"dialog": true,
|
||||
"pubkey": "YOUR_PUBLIC_KEY"
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"active": true,
|
||||
"dialog": false,
|
||||
"endpoints": [
|
||||
"https://github.com/jamiepine/voicebox/releases/latest/download/latest.json"
|
||||
],
|
||||
"pubkey": "PASTE_PUBLIC_KEY_CONTENT_HERE"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Generating Keys
|
||||
**Key settings:**
|
||||
- `endpoints` - URL to the `latest.json` manifest (checked on app startup)
|
||||
- `pubkey` - Public key for verifying update signatures
|
||||
- `dialog` - Set to `false` (we use custom UI instead of Tauri's built-in dialog)
|
||||
|
||||
```bash
|
||||
# Generate signing keys
|
||||
bun run generate:keys
|
||||
## Release Manifest
|
||||
|
||||
# Keys saved to ~/.tauri/voicebox.key
|
||||
The `latest.json` file defines available updates per platform:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"notes": "Bug fixes and improvements",
|
||||
"pub_date": "2026-01-25T12:00:00Z",
|
||||
"platforms": {
|
||||
"darwin-aarch64": {
|
||||
"signature": "base64_encoded_signature",
|
||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_aarch64.app.tar.gz"
|
||||
},
|
||||
"darwin-x86_64": {
|
||||
"signature": "base64_encoded_signature",
|
||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64.app.tar.gz"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"signature": "base64_encoded_signature",
|
||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_amd64.AppImage"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"signature": "base64_encoded_signature",
|
||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64_en-US.msi"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Keep your private key secure! Never commit it to the repository.
|
||||
</Warning>
|
||||
## Signing
|
||||
|
||||
## Release Process
|
||||
Updates must be cryptographically signed to be accepted. The signing process:
|
||||
|
||||
1. **Bump version** using bumpversion
|
||||
2. **Push tag** to trigger CI/CD
|
||||
3. **GitHub Actions** builds and signs releases
|
||||
4. **Users** receive update notification
|
||||
1. **Generate keys** (one-time setup):
|
||||
```bash
|
||||
bun tauri signer generate -w ~/.tauri/voicebox.key
|
||||
```
|
||||
This creates:
|
||||
- Private key: `~/.tauri/voicebox.key` (stored in GitHub Secrets, never committed)
|
||||
- Public key: `~/.tauri/voicebox.key.pub` (pasted into `tauri.conf.json`)
|
||||
|
||||
## User Experience
|
||||
2. **Build with signing** (GitHub Actions handles this):
|
||||
- Set `TAURI_SIGNING_PRIVATE_KEY` environment variable
|
||||
- Tauri signs the update package during build
|
||||
- Generates `.sig` signature file alongside the installer
|
||||
|
||||
When an update is available:
|
||||
3. **Verification** - The updater compares the signature against the public key before installing
|
||||
|
||||
1. User sees a notification dialog
|
||||
2. User clicks "Update"
|
||||
3. Update downloads in background
|
||||
4. App restarts with new version
|
||||
## GitHub Actions Workflow
|
||||
|
||||
## For Developers
|
||||
The release workflow (`.github/workflows/release.yml`) automatically:
|
||||
|
||||
See the full documentation files for:
|
||||
- Builds signed releases for macOS, Windows, and Linux
|
||||
- Creates the `latest.json` manifest with signatures
|
||||
- Uploads everything to the GitHub Release
|
||||
|
||||
- Setting up signing keys
|
||||
- Configuring GitHub releases
|
||||
- Testing updates locally
|
||||
- Troubleshooting update failures
|
||||
Triggered by pushing a git tag:
|
||||
|
||||
<Card title="View Full Docs" href="https://github.com/jamiepine/voicebox/tree/main/docs">
|
||||
Access AUTOUPDATER.md and AUTOUPDATER_QUICKSTART.md in the repository
|
||||
</Card>
|
||||
```bash
|
||||
git tag v0.2.0 && git push --tags
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
GitHub Actions needs these secrets set:
|
||||
|
||||
- `TAURI_SIGNING_PRIVATE_KEY` - Content of `~/.tauri/voicebox.key`
|
||||
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` - Password for the key (if set)
|
||||
|
||||
## Security
|
||||
|
||||
<Callout type="warn">
|
||||
**Critical:** Never commit the private key. Store it only in GitHub Secrets. The public key in `tauri.conf.json` is safe to commit and distribute.
|
||||
</Callout>
|
||||
|
||||
- Updates are cryptographically signed using Ed25519
|
||||
- HTTP endpoints are blocked (HTTPS only)
|
||||
- Signature verification happens before installation
|
||||
- Failed verification aborts the update
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Invalid signature" error
|
||||
- Public key in `tauri.conf.json` doesn't match the private key used to sign
|
||||
- Signature file wasn't uploaded to the release
|
||||
|
||||
### "No update available" when one exists
|
||||
- `latest.json` version isn't higher than current version
|
||||
- Wrong endpoint URL in configuration
|
||||
- Manifest hasn't propagated to GitHub's CDN yet
|
||||
|
||||
### Update check fails in dev mode
|
||||
The updater only works in production Tauri builds. It doesn't run during `just dev` or web mode.
|
||||
|
||||
### Build fails with signing error
|
||||
- GitHub Secrets aren't set correctly
|
||||
- Private key file is missing or corrupted
|
||||
- Key format is wrong (should start with `dW50cnVzdGVkIGNvbW1lbnQ6`)
|
||||
|
||||
## CUDA Backend Updates
|
||||
|
||||
The CUDA-enabled backend is distributed separately from the main app due to its large size (~2.43 GB). Unlike the Tauri auto-updater, this uses a custom download system built into the Python backend.
|
||||
|
||||
**Size comparison:**
|
||||
- Standard app bundle: ~410 MB
|
||||
- CUDA backend binary: ~2.43 GB (6× larger)
|
||||
|
||||
### Why Split?
|
||||
|
||||
GitHub Releases has file size limits, and the CUDA-enabled `voicebox-server` binary is too large to include in the main Tauri bundle. Instead:
|
||||
|
||||
- **Standard release**: Includes CPU-only backend (~50MB)
|
||||
- **CUDA release**: Split into multiple parts and downloaded on-demand by users who need GPU acceleration
|
||||
|
||||
### Download Process
|
||||
|
||||
When a user clicks "Enable CUDA" in the settings:
|
||||
|
||||
1. **Manifest Fetch** - Backend fetches `{version}/voicebox-server-cuda.manifest` from GitHub Releases
|
||||
2. **Part Download** - Downloads each split part sequentially (e.g., `voicebox-server-cuda.part1`, `.part2`, etc.)
|
||||
3. **Assembly** - Concatenates parts into a single binary
|
||||
4. **Verification** - SHA-256 checksum verification (optional, if `.sha256` file exists)
|
||||
5. **Placement** - Binary moved to `{data_dir}/backends/voicebox-server-cuda.exe`
|
||||
6. **Restart** - Backend must restart to use the CUDA binary
|
||||
|
||||
### Auto-Update on Startup
|
||||
|
||||
On server startup, `check_and_update_cuda_binary()` compares the installed CUDA binary version with the app version:
|
||||
|
||||
```python
|
||||
# backend/services/cuda.py
|
||||
cuda_version = get_cuda_binary_version() # runs `voicebox-server-cuda --version`
|
||||
current_version = __version__
|
||||
|
||||
if cuda_version != current_version:
|
||||
await download_cuda_binary() # Auto-download in background
|
||||
```
|
||||
|
||||
If versions mismatch, the backend automatically downloads the matching CUDA binary version without user intervention.
|
||||
|
||||
### Storage Location
|
||||
|
||||
Downloaded CUDA binaries are stored in the app's data directory:
|
||||
|
||||
```
|
||||
{data_dir}/
|
||||
backends/
|
||||
voicebox-server-cuda.exe # Windows
|
||||
voicebox-server-cuda # macOS/Linux
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/backend/cuda-status` | GET | Check if CUDA binary available/active |
|
||||
| `/backend/download-cuda` | POST | Start download |
|
||||
| `/backend/cuda-progress` | GET | SSE stream of download progress |
|
||||
| `/backend/cuda` | DELETE | Remove downloaded binary |
|
||||
|
||||
### Progress Tracking
|
||||
|
||||
Downloads report progress via Server-Sent Events (SSE):
|
||||
|
||||
```
|
||||
GET /backend/cuda-progress
|
||||
|
||||
event: progress
|
||||
data: {"current": 52428800, "total": 104857600, "filename": "Downloading CUDA backend (2/4)", "status": "downloading"}
|
||||
```
|
||||
|
||||
The frontend subscribes to this endpoint to show real-time download progress in the UI.
|
||||
|
||||
### Release Artifacts
|
||||
|
||||
For each release, these CUDA-related files are uploaded to GitHub:
|
||||
|
||||
- `voicebox-server-cuda.manifest` - List of split part filenames
|
||||
- `voicebox-server-cuda.part1` through `voicebox-server-cuda.partN` - Binary chunks
|
||||
- `voicebox-server-cuda.sha256` - SHA-256 checksum for integrity verification
|
||||
|
||||
@@ -1,270 +1,190 @@
|
||||
---
|
||||
title: "Building"
|
||||
description: "Build Voicebox for production"
|
||||
description: "How Voicebox is built for production"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Voicebox uses a multi-step build process to create platform-specific installers.
|
||||
Voicebox uses a two-stage build process:
|
||||
|
||||
## Quick Build
|
||||
1. **Python Server Binary** — PyInstaller bundles the FastAPI backend into a standalone executable
|
||||
2. **Tauri Desktop App** — Bundles the React frontend, Rust wrapper, and Python server as a sidecar
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# Build for your current platform (automatically builds server binary first)
|
||||
make build
|
||||
|
||||
# Or manually
|
||||
bun run build
|
||||
just build # Build everything (server + Tauri)
|
||||
just build-server # Build Python server binary only
|
||||
just build-tauri # Build Tauri app only
|
||||
```
|
||||
|
||||
This automatically:
|
||||
1. Builds the Python server binary (`bun run build:server`)
|
||||
2. Builds the Tauri app (`cd tauri && bun run tauri build`)
|
||||
## Server Binary Build
|
||||
|
||||
## Build Process
|
||||
### Build Script
|
||||
|
||||
The build process consists of two steps, but `bun run build` handles both automatically:
|
||||
|
||||
### 1. Server Binary Build (Automatic)
|
||||
|
||||
The Python backend is compiled into a standalone executable using PyInstaller. This happens automatically when you run `bun run build`.
|
||||
|
||||
**Platform-specific binaries:**
|
||||
- macOS (Apple Silicon): `voicebox-server-aarch64-apple-darwin` (includes MLX backend)
|
||||
- macOS (Intel): `voicebox-server-x86_64-apple-darwin` (PyTorch backend)
|
||||
- Windows: `voicebox-server-x86_64-pc-windows-msvc.exe` (PyTorch backend)
|
||||
- Linux: `voicebox-server-x86_64-unknown-linux-gnu` (PyTorch backend)
|
||||
|
||||
<Note>
|
||||
The build script automatically detects your platform and includes the appropriate backend (MLX for Apple Silicon, PyTorch for others).
|
||||
</Note>
|
||||
|
||||
**Manual build (if needed):**
|
||||
```bash
|
||||
bun run build:server
|
||||
```
|
||||
|
||||
### 2. Tauri App Build (Automatic)
|
||||
|
||||
The Tauri app build is also handled automatically, which:
|
||||
1. Builds the React frontend (Vite)
|
||||
2. Compiles the Rust backend
|
||||
3. Bundles the server binary as a sidecar
|
||||
4. Creates platform-specific installers
|
||||
|
||||
**Manual build (if needed):**
|
||||
```bash
|
||||
cd tauri && bun run tauri build
|
||||
```
|
||||
|
||||
### 3. Output
|
||||
|
||||
Installers are created in `tauri/src-tauri/target/release/bundle/`:
|
||||
|
||||
**macOS:**
|
||||
- `dmg/` - Disk image installer
|
||||
- `macos/` - App bundle
|
||||
|
||||
**Windows:**
|
||||
- `msi/` - MSI installer
|
||||
- `nsis/` - NSIS installer
|
||||
|
||||
**Linux:**
|
||||
- `deb/` - Debian package
|
||||
- `appimage/` - AppImage
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### Building for Specific Platform
|
||||
`scripts/build-server.sh` orchestrates the build:
|
||||
|
||||
```bash
|
||||
# Build for macOS (Apple Silicon)
|
||||
bun run tauri build -- --target aarch64-apple-darwin
|
||||
# Determine platform (e.g., x86_64-apple-darwin)
|
||||
PLATFORM=$(rustc --print host-tuple)
|
||||
|
||||
# Build for macOS (Intel)
|
||||
bun run tauri build -- --target x86_64-apple-darwin
|
||||
# Run PyInstaller via build_binary.py
|
||||
cd backend
|
||||
python build_binary.py
|
||||
|
||||
# Build for Windows
|
||||
bun run tauri build -- --target x86_64-pc-windows-msvc
|
||||
|
||||
# Build for Linux
|
||||
bun run tauri build -- --target x86_64-unknown-linux-gnu
|
||||
# Copy to Tauri's binaries directory
|
||||
cp dist/voicebox-server ../tauri/src-tauri/binaries/voicebox-server-${PLATFORM}
|
||||
```
|
||||
|
||||
### Using Local Qwen3-TTS
|
||||
### PyInstaller Configuration
|
||||
|
||||
If you're developing Qwen3-TTS locally:
|
||||
`backend/build_binary.py` contains the PyInstaller configuration:
|
||||
|
||||
**Entry Point:** Uses `server.py` (not `main.py`) for Tauri sidecar support
|
||||
|
||||
**Key Options:**
|
||||
- `--onefile` — Single executable
|
||||
- `--hidden-import` — Explicitly import modules PyInstaller can't detect
|
||||
- `--collect-all` — Bundle data files and native libraries for packages like `mlx`, `zipvoice`
|
||||
- `--exclude-module` — Strip NVIDIA packages from CPU builds
|
||||
|
||||
**Platform-Specific Logic:**
|
||||
|
||||
```python
|
||||
# Apple Silicon — include MLX backend
|
||||
if is_apple_silicon() and not cuda:
|
||||
args.extend([
|
||||
"--hidden-import", "mlx",
|
||||
"--collect-all", "mlx", # Bundles .dylib and .metallib files
|
||||
])
|
||||
|
||||
# CUDA builds — include torch.cuda
|
||||
if cuda:
|
||||
args.extend(["--hidden-import", "torch.cuda"])
|
||||
|
||||
# CPU builds — exclude NVIDIA packages to save ~3GB
|
||||
else:
|
||||
for pkg in ["nvidia", "nvidia.cublas", "nvidia.cudnn", ...]:
|
||||
args.extend(["--exclude-module", pkg])
|
||||
```
|
||||
|
||||
**Environment Variable:**
|
||||
|
||||
```bash
|
||||
export QWEN_TTS_PATH=~/path/to/Qwen3-TTS
|
||||
bun run build:server # Build server binary only
|
||||
# or
|
||||
bun run build # Build everything
|
||||
export QWEN_TTS_PATH=~/path/to/Qwen3-TTS # Use local Qwen3-TTS source
|
||||
```
|
||||
|
||||
This makes PyInstaller use your local version instead of the pip package.
|
||||
### CUDA Binary
|
||||
|
||||
### Debug Build
|
||||
The CUDA-enabled server is built separately due to size (~2.43 GB vs ~410 MB CPU version):
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
python build_binary.py --cuda
|
||||
```
|
||||
|
||||
The resulting binary is too large for GitHub Releases, so it's split into parts for distribution (see Auto-Updater docs for the download mechanism).
|
||||
|
||||
## Tauri App Build
|
||||
|
||||
Tauri bundles everything together:
|
||||
|
||||
```bash
|
||||
cd tauri
|
||||
bun run tauri build --debug
|
||||
bun run tauri build
|
||||
```
|
||||
|
||||
Creates a debug build with symbols and logging.
|
||||
**What happens:**
|
||||
1. Vite builds the React frontend
|
||||
2. Rust compiles the Tauri wrapper
|
||||
3. Sidecar binary is copied from `src-tauri/binaries/`
|
||||
4. Platform-specific installer created (DMG, MSI, AppImage)
|
||||
|
||||
## Build Configuration
|
||||
**Output locations:**
|
||||
|
||||
### Tauri Config
|
||||
|
||||
Edit `tauri/src-tauri/tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"bundle": {
|
||||
"identifier": "com.voicebox.app",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
<Files>
|
||||
<Folder name="tauri/src-tauri/target/release/bundle" defaultOpen>
|
||||
<File name="dmg/" />
|
||||
<File name="msi/" />
|
||||
<File name="nsis/" />
|
||||
<File name="appimage/" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### Sidecar Configuration
|
||||
|
||||
The Python server is bundled as a sidecar:
|
||||
The server binary is declared as an external binary in `tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"externalBin": [
|
||||
"binaries/voicebox-server"
|
||||
]
|
||||
"externalBin": ["binaries/voicebox-server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Code Signing
|
||||
Tauri looks for `voicebox-server-${PLATFORM}` in `src-tauri/binaries/` and bundles it.
|
||||
|
||||
### macOS
|
||||
## GitHub Actions Release
|
||||
|
||||
To sign the app for distribution:
|
||||
`.github/workflows/release.yml` automates the full build:
|
||||
|
||||
```bash
|
||||
# Set signing identity
|
||||
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name"
|
||||
### Matrix Strategy
|
||||
|
||||
# Build with signing
|
||||
bun run tauri build
|
||||
```
|
||||
| Platform | Target | Backend | Notes |
|
||||
|----------|--------|---------|-------|
|
||||
| macos-latest | aarch64-apple-darwin | MLX | Apple Silicon native |
|
||||
| macos-15-intel | x86_64-apple-darwin | PyTorch | Intel Macs |
|
||||
| windows-latest | x86_64-pc-windows-msvc | PyTorch | Windows with CUDA optional |
|
||||
|
||||
For notarization:
|
||||
### Build Steps
|
||||
|
||||
```bash
|
||||
# Set credentials
|
||||
export APPLE_ID="[email protected]"
|
||||
export APPLE_PASSWORD="app-specific-password"
|
||||
1. **Setup** — Python, Rust, Bun, dependencies
|
||||
2. **Build Server** — `build-server.sh` (Unix) or `build_binary.py` (Windows)
|
||||
3. **Build Tauri** — `tauri-action` with signing keys
|
||||
4. **Upload** — Release artifacts and `latest.json`
|
||||
|
||||
# Build and notarize
|
||||
bun run tauri build
|
||||
```
|
||||
### Code Signing
|
||||
|
||||
### Windows
|
||||
**macOS:**
|
||||
- Apple Developer certificate imported from secrets
|
||||
- Notarization via App Store Connect API
|
||||
|
||||
For Windows code signing:
|
||||
**Windows:**
|
||||
- Tauri handles signing via `TAURI_SIGNING_PRIVATE_KEY`
|
||||
|
||||
```bash
|
||||
# Set certificate
|
||||
export WINDOWS_CERTIFICATE_PATH="/path/to/cert.pfx"
|
||||
export WINDOWS_CERTIFICATE_PASSWORD="password"
|
||||
### CUDA Binary (Separate Job)
|
||||
|
||||
# Build with signing
|
||||
bun run tauri build
|
||||
```
|
||||
The `build-cuda-windows` job runs separately:
|
||||
|
||||
## Release Process
|
||||
1. Install PyTorch with CUDA 12.1
|
||||
2. Build with `build_binary.py --cuda`
|
||||
3. Split binary with `scripts/split_binary.py`
|
||||
4. Upload parts as release artifacts
|
||||
|
||||
The full release process is automated:
|
||||
|
||||
```bash
|
||||
# 1. Bump version
|
||||
bumpversion patch # or minor/major
|
||||
|
||||
# 2. Build all platforms (CI/CD handles this)
|
||||
git push --tags
|
||||
|
||||
# 3. GitHub Actions creates releases
|
||||
```
|
||||
|
||||
See [CONTRIBUTING.md](/development/contributing) for the full release workflow.
|
||||
This binary is downloaded on-demand by users who enable CUDA in settings.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Server Binary Build Fails">
|
||||
**Common issues:**
|
||||
- Missing Python dependencies: `pip install -r requirements.txt`
|
||||
- PyInstaller not found: `pip install pyinstaller`
|
||||
- Qwen3-TTS not installed: `pip install git+https://github.com/QwenLM/Qwen3-TTS.git`
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
cd backend
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
pip install pyinstaller
|
||||
```
|
||||
<Accordion title="Binary not found in dist/">
|
||||
PyInstaller failed to create the output. Check:
|
||||
- Python venv is activated
|
||||
- All dependencies installed: `pip install -r requirements.txt`
|
||||
- PyInstaller installed: `pip install pyinstaller`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Tauri Build Fails">
|
||||
**Common issues:**
|
||||
- Rust not installed: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
|
||||
- Server binary missing: Usually auto-built, but can run manually: `./scripts/build-server.sh`
|
||||
- Node modules outdated: `bun install`
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Clean and rebuild
|
||||
cd tauri/src-tauri
|
||||
cargo clean
|
||||
cd ../..
|
||||
bun run build # Automatically builds server binary first
|
||||
```
|
||||
<Accordion title="MLX/Metal libraries missing in bundle">
|
||||
macOS Apple Silicon builds need `--collect-all mlx` to include `.dylib` and `.metallib` files, not just `--collect-data`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="App Won't Launch After Build">
|
||||
**Check:**
|
||||
- Server binary has execute permissions
|
||||
- All dependencies are bundled
|
||||
- Check logs in the app's data directory
|
||||
<Accordion title="CUDA DLLs bloating CPU build">
|
||||
If building CPU version but CUDA torch is installed locally, the script auto-detects and swaps to CPU torch temporarily, then restores CUDA torch after.
|
||||
</Accordion>
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
tail -f ~/Library/Application\ Support/com.voicebox.app/logs/server.log
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
type %APPDATA%\com.voicebox.app\logs\server.log
|
||||
```
|
||||
<Accordion title="Tauri can't find sidecar">
|
||||
Ensure binary exists at `tauri/src-tauri/binaries/voicebox-server-${PLATFORM}` before running Tauri build.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## CI/CD
|
||||
|
||||
GitHub Actions automatically builds releases when tags are pushed:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/release.yml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
```
|
||||
|
||||
See the [repository](https://github.com/jamiepine/voicebox) for the full CI/CD configuration.
|
||||
|
||||
@@ -23,20 +23,20 @@ Before you start contributing, make sure you have:
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Report Bugs" icon="bug">
|
||||
<Cards>
|
||||
<Card title="Report Bugs">
|
||||
Found a bug? Open an issue with reproduction steps
|
||||
</Card>
|
||||
<Card title="Request Features" icon="lightbulb">
|
||||
<Card title="Request Features">
|
||||
Have an idea? Start a discussion or open an issue
|
||||
</Card>
|
||||
<Card title="Improve Docs" icon="book">
|
||||
<Card title="Improve Docs">
|
||||
Fix typos, add examples, or clarify instructions
|
||||
</Card>
|
||||
<Card title="Write Code" icon="code">
|
||||
<Card title="Write Code">
|
||||
Fix bugs, add features, or optimize performance
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
## Development Workflow
|
||||
|
||||
@@ -164,27 +164,28 @@ When creating a pull request:
|
||||
|
||||
## Project Structure
|
||||
|
||||
Understanding the codebase:
|
||||
|
||||
```
|
||||
voicebox/
|
||||
├── app/ # Shared React frontend
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # UI components
|
||||
│ │ ├── lib/ # Utilities and API client
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ └── stores/ # Zustand state stores
|
||||
├── backend/ # Python FastAPI server
|
||||
│ ├── main.py # API routes
|
||||
│ ├── tts.py # Voice synthesis logic
|
||||
│ ├── database.py # SQLite operations
|
||||
│ └── models.py # Pydantic models
|
||||
├── tauri/ # Desktop app wrapper
|
||||
│ └── src-tauri/ # Rust backend
|
||||
├── web/ # Web deployment
|
||||
├── landing/ # Marketing website
|
||||
└── scripts/ # Build & release scripts
|
||||
```
|
||||
<Files>
|
||||
<Folder name="voicebox" defaultOpen>
|
||||
<Folder name="app/src">
|
||||
<File name="components/" />
|
||||
<File name="lib/" />
|
||||
<File name="hooks/" />
|
||||
<File name="stores/" />
|
||||
</Folder>
|
||||
<Folder name="backend">
|
||||
<File name="main.py" />
|
||||
<File name="tts.py" />
|
||||
<File name="database.py" />
|
||||
<File name="models.py" />
|
||||
</Folder>
|
||||
<Folder name="tauri">
|
||||
<File name="src-tauri/" />
|
||||
</Folder>
|
||||
<File name="web/" />
|
||||
<File name="landing/" />
|
||||
<File name="scripts/" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
## Areas for Contribution
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
---
|
||||
title: "Effects Pipeline"
|
||||
description: "Audio post-processing effects and generation versioning"
|
||||
---
|
||||
|
||||
The effects pipeline provides professional-grade DSP audio processing using Spotify's Pedalboard library. Each generation can have multiple versions with different effect chains applied.
|
||||
|
||||
## Overview
|
||||
|
||||
**Key concepts:**
|
||||
|
||||
- **Effects Chain** — JSON-serializable list of effect configurations applied sequentially
|
||||
- **Generation Version** — A processed variant of a generation with its own audio file and effects chain
|
||||
- **Effect Preset** — Saved effects chain configuration (built-in or user-created)
|
||||
- **Clean Version** — The original unprocessed generation audio
|
||||
|
||||
**Flow:**
|
||||
|
||||
1. TTS Generation creates clean audio
|
||||
2. Effects Chain processes the audio
|
||||
3. Processed Version is saved as a new generation version
|
||||
|
||||
Each generation maintains a clean version (original) plus any number of processed versions with different effect chains applied.
|
||||
|
||||
## Effect Types
|
||||
|
||||
The following effect types are available, each with configurable parameters:
|
||||
|
||||
### Chorus / Flanger
|
||||
|
||||
Modulated delay effect. Short centre_delay_ms gives flanger; longer gives chorus.
|
||||
|
||||
**Parameters:**
|
||||
- rate_hz: LFO speed in Hz (range: 0.01 to 20, default: 1.0)
|
||||
- depth: Modulation depth (range: 0.0 to 1.0, default: 0.5)
|
||||
- feedback: Feedback amount (range: 0.0 to 0.95, default: 0.0)
|
||||
- centre_delay_ms: Centre delay in milliseconds (range: 0.5 to 50, default: 7.0)
|
||||
- mix: Wet/dry mix (range: 0.0 to 1.0, default: 0.5)
|
||||
|
||||
### Reverb
|
||||
|
||||
Room reverb effect.
|
||||
|
||||
**Parameters:**
|
||||
- room_size: Room size (range: 0.0 to 1.0, default: 0.5)
|
||||
- damping: High frequency damping (range: 0.0 to 1.0, default: 0.5)
|
||||
- wet_level: Wet level (range: 0.0 to 1.0, default: 0.33)
|
||||
- dry_level: Dry level (range: 0.0 to 1.0, default: 0.4)
|
||||
- width: Stereo width (range: 0.0 to 1.0, default: 1.0)
|
||||
|
||||
### Delay
|
||||
|
||||
Echo / delay line.
|
||||
|
||||
**Parameters:**
|
||||
- delay_seconds: Delay time in seconds (range: 0.01 to 2.0, default: 0.3)
|
||||
- feedback: Feedback amount (range: 0.0 to 0.95, default: 0.3)
|
||||
- mix: Wet/dry mix (range: 0.0 to 1.0, default: 0.3)
|
||||
|
||||
### Compressor
|
||||
|
||||
Dynamic range compression for consistent loudness.
|
||||
|
||||
**Parameters:**
|
||||
- threshold_db: Threshold in dB (range: -60 to 0, default: -20.0)
|
||||
- ratio: Compression ratio (range: 1.0 to 20.0, default: 4.0)
|
||||
- attack_ms: Attack time in ms (range: 0.1 to 100, default: 10.0)
|
||||
- release_ms: Release time in ms (range: 10 to 1000, default: 100.0)
|
||||
|
||||
### Gain
|
||||
|
||||
Volume adjustment in decibels.
|
||||
|
||||
**Parameters:**
|
||||
- gain_db: Gain in dB (range: -40 to 40, default: 0.0)
|
||||
|
||||
### High-Pass Filter
|
||||
|
||||
Removes frequencies below the cutoff.
|
||||
|
||||
**Parameters:**
|
||||
- cutoff_frequency_hz: Cutoff frequency in Hz (range: 20 to 8000, default: 80.0)
|
||||
|
||||
### Low-Pass Filter
|
||||
|
||||
Removes frequencies above the cutoff.
|
||||
|
||||
**Parameters:**
|
||||
- cutoff_frequency_hz: Cutoff frequency in Hz (range: 200 to 20000, default: 8000.0)
|
||||
|
||||
### Pitch Shift
|
||||
|
||||
Shift pitch up or down by semitones.
|
||||
|
||||
**Parameters:**
|
||||
- semitones: Semitones to shift (range: -12 to 12, default: 0.0)
|
||||
|
||||
## Generation Versions
|
||||
|
||||
Each generation starts with a clean version (no effects). Users can create processed versions by applying effect chains.
|
||||
|
||||
**Version properties:**
|
||||
- id — Unique version identifier
|
||||
- label — User-defined name (e.g., "robotic", "with reverb")
|
||||
- audio_path — Path to the processed audio file
|
||||
- effects_chain — JSON array of effect configurations
|
||||
- source_version_id — Which version this was derived from
|
||||
- is_default — Whether this is the default audio for the generation
|
||||
|
||||
**File storage:**
|
||||
|
||||
<Files>
|
||||
<Folder name="data/generations" defaultOpen>
|
||||
<File name="{generation_id}.wav" />
|
||||
<File name="{generation_id}_{version_id}.wav" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
**Default version behavior:**
|
||||
- One version per generation is marked as default
|
||||
- The generation's audio_path always points to the default version's audio
|
||||
- Deleting the default version automatically promotes another version
|
||||
|
||||
## Effect Presets
|
||||
|
||||
Presets are saved effects chains that can be reused across generations.
|
||||
|
||||
**Built-in presets:**
|
||||
|
||||
- **Robotic**: Metallic robotic voice using chorus (flanger-style)
|
||||
- **Radio**: Thin AM-radio voice with band-pass filtering and light compression
|
||||
- **Echo Chamber**: Spacious reverb with trailing echo
|
||||
- **Deep Voice**: Lower pitch with added warmth using pitch shift and compression
|
||||
|
||||
**User presets:**
|
||||
- Created via the effects UI
|
||||
- Stored in the database (SQLite)
|
||||
- Cannot modify/delete built-in presets
|
||||
- Used to quickly apply favorite effect combinations
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Effects Management
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| /effects/available | GET | List all effect types with parameter definitions |
|
||||
| /effects/presets | GET | List all presets (built-in + user) |
|
||||
| /effects/presets | POST | Create a new user preset |
|
||||
| /effects/presets/:id | GET | Get a specific preset |
|
||||
| /effects/presets/:id | PUT | Update a user preset |
|
||||
| /effects/presets/:id | DELETE | Delete a user preset |
|
||||
| /effects/preview/:generation_id | POST | Preview effects on a generation (returns audio stream) |
|
||||
|
||||
### Generation Versions
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| /generations/:id/versions | GET | List all versions for a generation |
|
||||
| /generations/:id/versions/apply-effects | POST | Apply effects chain, create new version |
|
||||
| /generations/:id/versions/:version_id/set-default | PUT | Set a version as default |
|
||||
| /generations/:id/versions/:version_id | DELETE | Delete a version |
|
||||
|
||||
### Request Body: Apply Effects
|
||||
|
||||
Request body for applying effects:
|
||||
|
||||
- effects_chain: Array of effect objects
|
||||
- label: Version label (e.g., "with reverb")
|
||||
- set_as_default: Whether to set as default
|
||||
- source_version_id: Source version ID (optional)
|
||||
|
||||
## Implementation
|
||||
|
||||
### Backend Architecture
|
||||
|
||||
**Files:**
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| backend/utils/effects.py | Effect registry, validation, and audio processing |
|
||||
| backend/services/versions.py | Generation version CRUD operations |
|
||||
| backend/services/effects.py | Effect preset CRUD operations |
|
||||
| backend/routes/effects.py | API endpoints for effects and versions |
|
||||
|
||||
**Effect Registry:**
|
||||
|
||||
The EFFECT_REGISTRY dict in utils/effects.py defines all available effects with their parameters, defaults, and ranges.
|
||||
|
||||
**Validation:**
|
||||
|
||||
Effects chains are validated before application:
|
||||
- Each effect type must exist in the registry
|
||||
- Parameters must be numbers within min/max bounds
|
||||
- Unknown parameters are rejected
|
||||
|
||||
**Audio Processing:**
|
||||
|
||||
Uses Spotify's Pedalboard library:
|
||||
|
||||
```python
|
||||
from pedalboard import Pedalboard
|
||||
|
||||
# Build pedalboard from chain
|
||||
board = build_pedalboard(effects_chain)
|
||||
|
||||
# Apply to audio (async via thread)
|
||||
processed = await asyncio.to_thread(lambda: board(audio, sample_rate))
|
||||
```
|
||||
|
||||
### Frontend Integration
|
||||
|
||||
**Key components:**
|
||||
|
||||
| Component | Location |
|
||||
|-----------|----------|
|
||||
| Effects chain editor | app/src/components/Effects/ |
|
||||
| Version selector | Generation detail view |
|
||||
| Preset manager | Effects panel |
|
||||
| Live preview | Preview button (streams processed audio) |
|
||||
|
||||
**State management:**
|
||||
- Effects chains are stored as JSON arrays
|
||||
- Live preview fetches processed audio without saving
|
||||
- Applied effects create new versions via POST endpoint
|
||||
|
||||
## Adding New Effects
|
||||
|
||||
To add a new effect type:
|
||||
|
||||
1. **Add to registry** (backend/utils/effects.py):
|
||||
- Add entry to EFFECT_REGISTRY with cls, label, description, and params
|
||||
- Import the effect class from Pedalboard
|
||||
|
||||
2. **Update frontend types** if needed
|
||||
|
||||
The new effect automatically appears in /effects/available and the chain editor UI.
|
||||
|
||||
## Best Practices
|
||||
|
||||
**Effect ordering matters.** Process effects in this order for best results:
|
||||
1. Pitch shift (if needed)
|
||||
2. High/low-pass filters
|
||||
3. Chorus/flanger (time-based)
|
||||
4. Reverb/delay (spatial)
|
||||
5. Compressor
|
||||
6. Gain (final level adjustment)
|
||||
|
||||
**CPU usage:**
|
||||
- Effects are applied in real-time during generation
|
||||
- Pitch shift and reverb are the most CPU-intensive
|
||||
- Consider previewing complex chains before applying
|
||||
|
||||
**Storage:**
|
||||
- Each version creates a new audio file
|
||||
- Clean version always exists (can be reverted to)
|
||||
- Processed versions can be deleted to save space
|
||||
@@ -30,11 +30,13 @@ class Generation(Base):
|
||||
|
||||
Generated audio is stored in:
|
||||
|
||||
```
|
||||
data/
|
||||
└── generations/
|
||||
└── {generation_id}.wav
|
||||
```
|
||||
<Files>
|
||||
<Folder name="data" defaultOpen>
|
||||
<Folder name="generations">
|
||||
<File name="{generation_id}.wav" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
## Core Functions
|
||||
|
||||
@@ -171,11 +173,12 @@ async def delete_generations_by_profile(profile_id: str, db: Session) -> int:
|
||||
|
||||
Generations can be exported as ZIP archives:
|
||||
|
||||
```
|
||||
generation_export.zip
|
||||
├── generation.json # Metadata
|
||||
└── audio.wav # Audio file
|
||||
```
|
||||
<Files>
|
||||
<Folder name="generation_export.zip" defaultOpen>
|
||||
<File name="generation.json" />
|
||||
<File name="audio.wav" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### Importing a Generation
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
"autoupdater",
|
||||
"voice-profiles",
|
||||
"tts-generation",
|
||||
"tts-engines",
|
||||
"effects-pipeline",
|
||||
"history",
|
||||
"stories",
|
||||
"transcription",
|
||||
|
||||
@@ -36,13 +36,13 @@ Models are downloaded from HuggingFace Hub on first use and cached locally.
|
||||
|
||||
Models are cached in the HuggingFace cache directory:
|
||||
|
||||
```
|
||||
~/.cache/huggingface/hub/
|
||||
├── models--Qwen--Qwen3-TTS-12Hz-1.7B-Base/
|
||||
├── models--Qwen--Qwen3-TTS-12Hz-0.6B-Base/
|
||||
├── models--openai--whisper-base/
|
||||
└── ...
|
||||
```
|
||||
<Files>
|
||||
<Folder name="~/.cache/huggingface/hub" defaultOpen>
|
||||
<File name="models--Qwen--Qwen3-TTS-12Hz-1.7B-Base/" />
|
||||
<File name="models--Qwen--Qwen3-TTS-12Hz-0.6B-Base/" />
|
||||
<File name="models--openai--whisper-base/" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
|
||||
@@ -3,55 +3,158 @@ title: "Development Setup"
|
||||
description: "Set up your local development environment for Voicebox"
|
||||
---
|
||||
|
||||
## Quick Setup (Recommended)
|
||||
|
||||
Get started in two commands:
|
||||
|
||||
```bash
|
||||
# Clone and enter the repository
|
||||
git clone https://github.com/jamiepine/voicebox.git
|
||||
cd voicebox
|
||||
|
||||
# Setup everything (Python venv, JS deps, dev sidecar)
|
||||
just setup
|
||||
|
||||
# Start development (backend + desktop app)
|
||||
just dev
|
||||
```
|
||||
|
||||
The `just dev` command automatically starts the Python backend (if not already running) and launches the Tauri desktop app.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following installed:
|
||||
Ensure you have these installed:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Bun" icon="package">
|
||||
<Cards>
|
||||
<Card title="Bun" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>}>
|
||||
[Download Bun](https://bun.sh)
|
||||
```bash
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
```
|
||||
</Card>
|
||||
<Card title="Python 3.11+" icon="python">
|
||||
<Card title="Python 3.11+" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>}>
|
||||
[Download Python](https://python.org)
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
</Card>
|
||||
<Card title="Rust" icon="rust">
|
||||
<Card title="Rust" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>}>
|
||||
[Install Rust](https://rustup.rs)
|
||||
```bash
|
||||
rustc --version
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
<Card title="Just" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 7V4h3"/><path d="M7 4h14v6h-2V6H7V4Z"/><path d="M4 10v10h16V10H4Z"/></svg>}>
|
||||
[Install Just](https://github.com/casey/just)
|
||||
```bash
|
||||
brew install just # macOS
|
||||
cargo install just # Linux/Windows
|
||||
```
|
||||
</Card>
|
||||
</Cards>
|
||||
|
||||
## Clone the Repository
|
||||
<Callout type="info">
|
||||
Just works on macOS, Linux, and Windows.
|
||||
</Callout>
|
||||
|
||||
## Just Commands
|
||||
|
||||
Run `just --list` to see all available commands:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `just setup` | Full setup (Python venv + JS deps) |
|
||||
| `just dev` | Start backend + desktop app |
|
||||
| `just dev-web` | Start backend + web app (no Tauri) |
|
||||
| `just dev-backend` | Start backend only |
|
||||
| `just dev-frontend` | Start desktop app only (backend must be running) |
|
||||
| `just build` | Build desktop app for production |
|
||||
| `just build-web` | Build web app for production |
|
||||
| `just check` | Run all checks (JS + Python lint + format) |
|
||||
| `just fix` | Fix lint + format issues |
|
||||
| `just test` | Run Python tests |
|
||||
| `just db-init` | Initialize SQLite database |
|
||||
| `just db-reset` | Reset database (delete + reinit) |
|
||||
| `just clean` | Clean build artifacts |
|
||||
| `just clean-all` | Nuclear clean (includes node_modules) |
|
||||
|
||||
## Project Structure
|
||||
|
||||
<Files>
|
||||
<Folder name="voicebox" defaultOpen>
|
||||
<Folder name="app">
|
||||
<Folder name="src">
|
||||
<File name="components/" />
|
||||
<File name="lib/" />
|
||||
<File name="hooks/" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
<Folder name="backend">
|
||||
<File name="app.py" />
|
||||
<File name="main.py" />
|
||||
<File name="config.py" />
|
||||
<File name="models.py" />
|
||||
<File name="server.py" />
|
||||
<Folder name="routes">
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="services">
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="backends">
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="database">
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
<Folder name="utils">
|
||||
<File name="..." />
|
||||
</Folder>
|
||||
</Folder>
|
||||
<Folder name="tauri">
|
||||
<Folder name="src-tauri" />
|
||||
</Folder>
|
||||
<Folder name="web" />
|
||||
<Folder name="scripts" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
### Request Flow
|
||||
|
||||
HTTP request → **routes/** (validate input) → **services/** (business logic) → **backends/** (TTS/STT inference) → **utils/** (audio processing)
|
||||
|
||||
### Key Modules
|
||||
|
||||
- **app.py** — FastAPI app factory, CORS, lifecycle events
|
||||
- **main.py** — Entry point (imports app, runs uvicorn)
|
||||
- **server.py** — Tauri sidecar launcher, parent-pid watchdog
|
||||
- **services/generation.py** — Single function handling all generation modes
|
||||
- **backends/** — TTS/STT engine implementations (MLX, PyTorch, etc.)
|
||||
|
||||
## Model Downloads
|
||||
|
||||
Models are automatically downloaded from HuggingFace Hub on first use:
|
||||
|
||||
- **Whisper** (transcription): Auto-downloads on first transcription
|
||||
- **Qwen3-TTS** (voice cloning): Auto-downloads on first generation (~2-4GB)
|
||||
|
||||
<Callout type="warn">
|
||||
First-time usage will be slower due to model downloads, but subsequent runs will use cached models.
|
||||
</Callout>
|
||||
|
||||
## Generate OpenAPI Client
|
||||
|
||||
After starting the backend server, generate the TypeScript API client:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jamiepine/voicebox.git
|
||||
cd voicebox
|
||||
just generate-api
|
||||
```
|
||||
|
||||
## Quick Setup (Recommended)
|
||||
This downloads the OpenAPI schema and generates the TypeScript client in `app/src/lib/api/`
|
||||
|
||||
The easiest way to get started is using the Makefile:
|
||||
## Manual Setup (Advanced)
|
||||
|
||||
```bash
|
||||
# Setup everything
|
||||
make setup
|
||||
|
||||
# Start development
|
||||
make dev
|
||||
```
|
||||
|
||||
<Note>
|
||||
The Makefile is available on macOS and Linux. Windows users should follow the manual setup below.
|
||||
</Note>
|
||||
|
||||
## Manual Setup
|
||||
If you prefer not to use Just, follow these manual steps:
|
||||
|
||||
### 1. Install JavaScript Dependencies
|
||||
|
||||
@@ -80,145 +183,51 @@ venv\Scripts\activate # Windows
|
||||
# Install Python dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Install MLX dependencies (Apple Silicon only - for faster inference)
|
||||
# On Apple Silicon, this enables native Metal acceleration
|
||||
if [[ $(uname -m) == "arm64" ]]; then
|
||||
pip install -r requirements-mlx.txt
|
||||
fi
|
||||
# Apple Silicon: install MLX dependencies
|
||||
pip install -r requirements-mlx.txt
|
||||
|
||||
# Install Qwen3-TTS
|
||||
pip install git+https://github.com/QwenLM/Qwen3-TTS.git
|
||||
```
|
||||
|
||||
## Running in Development
|
||||
|
||||
Development requires **two terminals**: one for the Python backend, one for the Tauri app.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Terminal 1: Backend">
|
||||
Start the Python server first:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
source venv/bin/activate # Activate venv
|
||||
bun run dev:server
|
||||
```
|
||||
|
||||
Or manually:
|
||||
```bash
|
||||
uvicorn main:app --reload --port 17493
|
||||
```
|
||||
|
||||
Backend will be available at `http://localhost:17493`
|
||||
</Tab>
|
||||
|
||||
<Tab title="Terminal 2: Desktop App">
|
||||
Then start the Tauri app:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
This will:
|
||||
- Create a placeholder sidecar binary
|
||||
- Start Vite dev server on port 5173
|
||||
- Launch Tauri window
|
||||
- Enable hot reload
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Info>
|
||||
In dev mode, the app connects to your manually-started Python server. The bundled server binary is only used in production builds.
|
||||
</Info>
|
||||
|
||||
### Optional: Web App
|
||||
### 3. Start Development
|
||||
|
||||
Start the backend:
|
||||
```bash
|
||||
bun run dev:web
|
||||
cd backend
|
||||
source venv/bin/activate
|
||||
uvicorn main:app --reload --port 17493
|
||||
```
|
||||
|
||||
Web app will be available at `http://localhost:5174`
|
||||
|
||||
## Model Downloads
|
||||
|
||||
Models are automatically downloaded from HuggingFace Hub on first use:
|
||||
|
||||
- **Whisper** (transcription): Auto-downloads on first transcription
|
||||
- **Qwen3-TTS** (voice cloning): Auto-downloads on first generation (~2-4GB)
|
||||
|
||||
<Warning>
|
||||
First-time usage will be slower due to model downloads, but subsequent runs will use cached models.
|
||||
</Warning>
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
voicebox/
|
||||
├── app/ # Shared React frontend
|
||||
│ └── src/
|
||||
│ ├── components/ # UI components
|
||||
│ ├── lib/ # Utilities and API client
|
||||
│ └── hooks/ # React hooks
|
||||
├── backend/ # Python FastAPI server
|
||||
│ ├── main.py # API routes
|
||||
│ ├── tts.py # Voice synthesis
|
||||
│ └── database.py # SQLite operations
|
||||
├── tauri/ # Desktop app wrapper
|
||||
│ └── src-tauri/ # Rust backend
|
||||
├── web/ # Web deployment
|
||||
├── landing/ # Marketing website
|
||||
└── scripts/ # Build & release scripts
|
||||
```
|
||||
|
||||
## Available Make Commands
|
||||
|
||||
Run `make help` to see all available commands:
|
||||
|
||||
In a new terminal, start the desktop app:
|
||||
```bash
|
||||
make setup # Install all dependencies
|
||||
make dev # Start development servers
|
||||
make dev-web # Start web development server
|
||||
make build # Build desktop app
|
||||
make build-web # Build web app
|
||||
make clean # Clean build artifacts
|
||||
make test # Run tests
|
||||
cd tauri
|
||||
bun run tauri dev
|
||||
```
|
||||
|
||||
## Generate OpenAPI Client
|
||||
|
||||
After starting the backend server, generate the TypeScript API client:
|
||||
|
||||
```bash
|
||||
./scripts/generate-api.sh
|
||||
# or
|
||||
bun run generate:api
|
||||
```
|
||||
|
||||
This downloads the OpenAPI schema and generates the TypeScript client in `app/src/lib/api/`
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Architecture" icon="diagram-project" href="/development/architecture">
|
||||
<Cards>
|
||||
<Card title="Architecture" href="/development/architecture">
|
||||
Understand the system architecture
|
||||
</Card>
|
||||
<Card title="Contributing" icon="code-pull-request" href="/development/contributing">
|
||||
<Card title="Contributing" href="/development/contributing">
|
||||
Read the contribution guidelines
|
||||
</Card>
|
||||
<Card title="Building" icon="hammer" href="/development/building">
|
||||
<Card title="Building" href="/development/building">
|
||||
Learn how to build production releases
|
||||
</Card>
|
||||
<Card title="API Reference" icon="code" href="/api-reference">
|
||||
<Card title="API Reference" href="/api-reference">
|
||||
Explore the REST API
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Backend won't start">
|
||||
- Check Python version (must be 3.11+)
|
||||
- Ensure virtual environment is activated
|
||||
- Ensure virtual environment is activated: `source backend/venv/bin/activate`
|
||||
- Verify all dependencies are installed: `pip install -r requirements.txt`
|
||||
- Check if port 17493 is available
|
||||
</Accordion>
|
||||
@@ -226,7 +235,7 @@ This downloads the OpenAPI schema and generates the TypeScript client in `app/sr
|
||||
<Accordion title="Tauri build fails">
|
||||
- Ensure Rust is installed: `rustc --version`
|
||||
- Clean the build: `cd tauri/src-tauri && cargo clean`
|
||||
- Try rebuilding: `bun run dev`
|
||||
- Try rebuilding: `just dev`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="OpenAPI client generation fails">
|
||||
@@ -236,4 +245,4 @@ This downloads the OpenAPI schema and generates the TypeScript client in `app/sr
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
See the full [Troubleshooting Guide](/guides/troubleshooting) for more issues and solutions.
|
||||
See the full [Troubleshooting Guide](/overview/troubleshooting) for more issues and solutions.
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
---
|
||||
title: "TTS Engines"
|
||||
description: "How to add new text-to-speech engines to Voicebox"
|
||||
---
|
||||
|
||||
Adding an engine touches ~10 files across 4 layers. The backend protocol work is straightforward — the real time sink is dependency hell, upstream library bugs, and PyInstaller bundling.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The backend is split into layers:
|
||||
|
||||
| Layer | Purpose | Files Touched |
|
||||
|-------|---------|---------------|
|
||||
| `routes/` | Thin HTTP handlers | None (auto-dispatch) |
|
||||
| `services/` | Business logic | None (auto-dispatch) |
|
||||
| `backends/` | Engine implementations | `your_engine_backend.py` |
|
||||
| `utils/` | Shared utilities | As needed |
|
||||
|
||||
New engines only need to touch `backends/` and `models.py` on the backend side — the route and service layers use a model config registry that handles dispatch automatically.
|
||||
|
||||
## Phase 1: Backend Implementation
|
||||
|
||||
### 1.1 Create the Backend File
|
||||
|
||||
Create `backend/backends/<engine>_backend.py` (~200-300 lines) implementing the `TTSBackend` protocol:
|
||||
|
||||
```python
|
||||
class YourBackend:
|
||||
"""Must satisfy the TTSBackend protocol."""
|
||||
|
||||
async def load_model(self, model_size: str = "default") -> None: ...
|
||||
async def create_voice_prompt(self, audio_path: str, reference_text: str, use_cache: bool = True) -> tuple[dict, bool]: ...
|
||||
async def combine_voice_prompts(self, audio_paths: list[str], ref_texts: list[str]) -> tuple[np.ndarray, str]: ...
|
||||
async def generate(self, text: str, voice_prompt: dict, language: str = "en", seed: int | None = None, instruct: str | None = None) -> tuple[np.ndarray, int]: ...
|
||||
def unload_model(self) -> None: ...
|
||||
def is_loaded(self) -> bool: ...
|
||||
def _get_model_path(self, model_size: str) -> str: ...
|
||||
```
|
||||
|
||||
**Key decisions per engine:**
|
||||
|
||||
| Decision | Options | Examples |
|
||||
|----------|---------|---------|
|
||||
| **Voice prompt storage** | Pre-computed tensors vs deferred file paths | Qwen stores tensor dicts; Chatterbox stores paths |
|
||||
| **Caching** | Use voice prompt cache or skip it | LuxTTS caches with prefix; Chatterbox skips caching |
|
||||
| **Device selection** | CUDA / MPS / CPU | Chatterbox forces CPU on macOS (MPS bugs) |
|
||||
| **Model download** | Library handles it vs manual `snapshot_download` | Turbo uses manual download to bypass `token=True` bug |
|
||||
| **Sample rate** | Engine-specific | LuxTTS outputs 48kHz, everything else is 24kHz |
|
||||
|
||||
### 1.2 Voice Prompt Patterns
|
||||
|
||||
**Pattern A: Pre-computed tensors** (Qwen, LuxTTS)
|
||||
```python
|
||||
encoded = model.encode_prompt(audio_path)
|
||||
return encoded, False # (prompt_dict, was_cached)
|
||||
```
|
||||
|
||||
**Pattern B: Deferred file paths** (Chatterbox, MLX)
|
||||
```python
|
||||
return {"ref_audio": audio_path, "ref_text": reference_text}, False
|
||||
```
|
||||
|
||||
**Pattern C: Hybrid** (possible for new engines)
|
||||
```python
|
||||
embedding = model.extract_speaker(audio_path)
|
||||
return {"embedding": embedding, "ref_audio": audio_path}, False
|
||||
```
|
||||
|
||||
If caching, prefix your cache keys:
|
||||
```python
|
||||
cache_key = "yourengine_" + get_cache_key(audio_path, reference_text)
|
||||
```
|
||||
|
||||
### 1.3 Register the Engine
|
||||
|
||||
In `backend/backends/__init__.py`:
|
||||
|
||||
**Add a `ModelConfig` entry:**
|
||||
|
||||
```python
|
||||
ModelConfig(
|
||||
model_name="your-engine",
|
||||
display_name="Your Engine",
|
||||
engine="your_engine",
|
||||
hf_repo_id="org/model-repo",
|
||||
size_mb=3200,
|
||||
needs_trim=False, # set True if output needs trim_tts_output()
|
||||
languages=["en", "fr", "de"],
|
||||
),
|
||||
```
|
||||
|
||||
**Add to `TTS_ENGINES` dict:**
|
||||
|
||||
```python
|
||||
TTS_ENGINES = {
|
||||
...
|
||||
"your_engine": "Your Engine",
|
||||
}
|
||||
```
|
||||
|
||||
**Add factory branch:**
|
||||
|
||||
```python
|
||||
elif engine == "your_engine":
|
||||
from .your_backend import YourBackend
|
||||
backend = YourBackend()
|
||||
```
|
||||
|
||||
### 1.4 Update Request Models
|
||||
|
||||
In `backend/models.py`:
|
||||
- Add engine name to `GenerationRequest.engine` regex pattern
|
||||
- Add any new language codes to the language regex
|
||||
|
||||
## Phase 2: Route and Service Integration
|
||||
|
||||
With the model config registry, route and service layers have **zero per-engine dispatch points**. All endpoints use registry helpers like `get_model_config()`, `load_engine_model()`, `engine_needs_trim()`, `check_model_loaded()`, etc.
|
||||
|
||||
**You don't need to touch any route or service files** unless your engine needs custom behavior in the generate pipeline.
|
||||
|
||||
### Post-Processing
|
||||
|
||||
If your model produces trailing silence, set `needs_trim=True` on your `ModelConfig`. The generation service applies `trim_tts_output()` automatically.
|
||||
|
||||
## Phase 3: Frontend Integration
|
||||
|
||||
### 3.1 TypeScript Types
|
||||
|
||||
In `app/src/lib/api/types.ts`:
|
||||
- Add to the `engine` union type on `GenerationRequest`
|
||||
|
||||
### 3.2 Language Maps
|
||||
|
||||
In `app/src/lib/constants/languages.ts`:
|
||||
- Add entry to `ENGINE_LANGUAGES` record
|
||||
- Add any new language codes to `ALL_LANGUAGES` if needed
|
||||
|
||||
### 3.3 Engine/Model Selector
|
||||
|
||||
In `app/src/components/Generation/EngineModelSelector.tsx`:
|
||||
- Add entry to `ENGINE_OPTIONS` and `ENGINE_DESCRIPTIONS`
|
||||
- Add to `ENGLISH_ONLY_ENGINES` if applicable
|
||||
|
||||
### 3.4 Form Hook
|
||||
|
||||
In `app/src/lib/hooks/useGenerationForm.ts`:
|
||||
- Add to Zod schema enum for `engine`
|
||||
- Add engine-to-model-name mapping
|
||||
- Update payload construction for engine-specific fields
|
||||
|
||||
### 3.5 Model Management
|
||||
|
||||
In `app/src/components/ServerSettings/ModelManagement.tsx`:
|
||||
- Add description to `MODEL_DESCRIPTIONS` record
|
||||
|
||||
## Phase 4: Dependencies
|
||||
|
||||
### 4.1 Python Dependencies
|
||||
|
||||
Add to `backend/requirements.txt`. Watch for:
|
||||
|
||||
**Pinned dependency conflicts** — If the model package pins old versions, install with `--no-deps`:
|
||||
```bash
|
||||
pip install --no-deps chatterbox-tts
|
||||
```
|
||||
|
||||
Then list sub-dependencies manually in `requirements.txt`.
|
||||
|
||||
**Non-PyPI packages:**
|
||||
```
|
||||
linacodec @ git+https://github.com/user/repo.git
|
||||
```
|
||||
|
||||
**Custom package indexes:**
|
||||
```
|
||||
--find-links https://k2-fsa.github.io/icefall/piper_phonemize.html
|
||||
```
|
||||
|
||||
### 4.2 Identifying Hidden Sub-Dependencies
|
||||
|
||||
1. Install the package normally in a throwaway venv
|
||||
2. Run `pip show <package>` to get its `Requires:` list
|
||||
3. Cross-reference against existing requirements.txt
|
||||
4. Test that the engine loads and generates
|
||||
|
||||
## Phase 5: PyInstaller Bundling
|
||||
|
||||
This is where most of the pain lives. Common issues:
|
||||
|
||||
| Issue | Symptom | Fix |
|
||||
|-------|---------|-----|
|
||||
| `inspect.getsource()` at import | "could not get source code" | `--collect-all <package>` |
|
||||
| Data files (yaml, .pth.tar) | FileNotFoundError at runtime | `--collect-all <package>` |
|
||||
| Native data paths (espeak-ng) | Library looks at `/usr/share/...` | Set env var in frozen builds |
|
||||
| `importlib.metadata` lookups | "No package metadata found" | `--copy-metadata <package>` |
|
||||
| Dynamic imports | ModuleNotFoundError | `--hidden-import <module>` |
|
||||
|
||||
### Testing Frozen Builds
|
||||
|
||||
You can't skip this. Models that work in `python -m uvicorn` will break in the PyInstaller binary.
|
||||
|
||||
1. Build: `just build`
|
||||
2. Run and try download + load + generate
|
||||
3. Check stderr for the actual error
|
||||
4. Fix, rebuild, repeat
|
||||
|
||||
## Phase 6: Common Upstream Workarounds
|
||||
|
||||
### torch.load device mismatch
|
||||
```python
|
||||
_original_torch_load = torch.load
|
||||
def _patched_torch_load(*args, **kwargs):
|
||||
kwargs.setdefault("map_location", "cpu")
|
||||
return _original_torch_load(*args, **kwargs)
|
||||
torch.load = _patched_torch_load
|
||||
```
|
||||
|
||||
### Float64/Float32 dtype mismatch
|
||||
```python
|
||||
original_fn = SomeClass.some_method
|
||||
def patched_fn(self, *args, **kwargs):
|
||||
result = original_fn(self, *args, **kwargs)
|
||||
return result.float()
|
||||
SomeClass.some_method = patched_fn
|
||||
```
|
||||
|
||||
### HuggingFace token bug
|
||||
```python
|
||||
from huggingface_hub import snapshot_download
|
||||
local_path = snapshot_download(repo_id=REPO, token=None)
|
||||
model = ModelClass.from_local(local_path, device=device)
|
||||
```
|
||||
|
||||
### MPS tensor issues
|
||||
Skip MPS entirely if operators aren't supported:
|
||||
```python
|
||||
def _get_device(self):
|
||||
if torch.cuda.is_available():
|
||||
return "cuda"
|
||||
return "cpu" # Skip MPS
|
||||
```
|
||||
|
||||
## Upcoming Engines
|
||||
|
||||
Based on the current model landscape, these are candidates for future integration:
|
||||
|
||||
| Model | Languages | Size | Key Features | Status |
|
||||
|-------|-----------|------|--------------|--------|
|
||||
| **CosyVoice2-0.5B** | Multilingual | ~500MB | Instruct support (`inference_instruct2()`) | Ready |
|
||||
| **Fish Speech** | 50+ | Medium | Word-level control via inline text | Ready |
|
||||
| **Kokoro-82M** | English | 82M | CPU realtime, Apache 2.0 | Ready |
|
||||
| **XTTS-v2** | 17+ | Medium | Zero-shot cloning | Ready |
|
||||
| **HumeAI TADA** | EN (1B), Multi (3B) | Medium | 700s+ coherent audio, synced transcripts | Needs vetting |
|
||||
| **MOSS-TTS** | Multilingual | Medium | Text-to-voice design, multi-speaker dialogue | Needs vetting |
|
||||
| **Pocket TTS** | English | ~100M | CPU-first, >1× realtime | Needs vetting |
|
||||
|
||||
The multi-engine architecture is now in place, making new model integration straightforward (~1 day for a well-documented model with a PyPI package).
|
||||
@@ -49,14 +49,16 @@ class ProfileSample(Base):
|
||||
|
||||
Profiles are stored in the data directory:
|
||||
|
||||
```
|
||||
data/
|
||||
└── profiles/
|
||||
└── {profile_id}/
|
||||
├── {sample_id_1}.wav
|
||||
├── {sample_id_2}.wav
|
||||
└── ...
|
||||
```
|
||||
<Files>
|
||||
<Folder name="data" defaultOpen>
|
||||
<Folder name="profiles">
|
||||
<Folder name="{profile_id}">
|
||||
<File name="{sample_id_1}.wav" />
|
||||
<File name="{sample_id_2}.wav" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
## Core Functions
|
||||
|
||||
@@ -158,14 +160,15 @@ Reference audio is validated before being accepted:
|
||||
|
||||
Profiles can be exported as ZIP archives for sharing:
|
||||
|
||||
```
|
||||
profile_export.zip
|
||||
├── profile.json # Metadata
|
||||
├── samples/
|
||||
│ ├── sample_1.wav
|
||||
│ └── sample_1.json # Reference text
|
||||
└── ...
|
||||
```
|
||||
<Files>
|
||||
<Folder name="profile_export.zip" defaultOpen>
|
||||
<File name="profile.json" />
|
||||
<Folder name="samples">
|
||||
<File name="sample_1.wav" />
|
||||
<File name="sample_1.json" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
@@ -28,32 +28,32 @@ Voice profiles are the foundation of voice cloning in Voicebox. This guide cover
|
||||
|
||||
### Ideal Sample Characteristics
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Duration" icon="clock">
|
||||
<Cards>
|
||||
<Card title="Duration">
|
||||
**10-30 seconds**
|
||||
|
||||
Too short: Poor quality
|
||||
Too long: Unnecessary
|
||||
</Card>
|
||||
<Card title="Clarity" icon="volume">
|
||||
<Card title="Clarity">
|
||||
**Clear speech**
|
||||
|
||||
No background noise
|
||||
No music or overlapping voices
|
||||
</Card>
|
||||
<Card title="Quality" icon="sparkles">
|
||||
<Card title="Quality">
|
||||
**High fidelity**
|
||||
|
||||
44.1kHz or 48kHz sample rate
|
||||
Minimal compression
|
||||
</Card>
|
||||
<Card title="Content" icon="microphone">
|
||||
<Card title="Content">
|
||||
**Natural speech**
|
||||
|
||||
Conversational tone
|
||||
Complete sentences
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
### File Formats
|
||||
|
||||
@@ -63,9 +63,9 @@ Supported formats:
|
||||
- **M4A** - Acceptable
|
||||
- **FLAC** - Lossless alternative
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
Use WAV for best results. Avoid heavily compressed formats.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
## Recording Tips
|
||||
|
||||
@@ -108,20 +108,20 @@ Adding multiple samples can significantly improve quality:
|
||||
|
||||
### Why Multiple Samples?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Robustness" icon="shield">
|
||||
<Cards>
|
||||
<Card title="Robustness">
|
||||
Model learns a more complete representation
|
||||
</Card>
|
||||
<Card title="Versatility" icon="palette">
|
||||
<Card title="Versatility">
|
||||
Handles different speaking styles better
|
||||
</Card>
|
||||
<Card title="Quality" icon="star">
|
||||
<Card title="Quality">
|
||||
Reduces artifacts and improves naturalness
|
||||
</Card>
|
||||
<Card title="Consistency" icon="check">
|
||||
<Card title="Consistency">
|
||||
More reliable across different texts
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
### Sample Variety
|
||||
|
||||
@@ -144,9 +144,9 @@ Consider adding samples with:
|
||||
- Phone call quality (if needed)
|
||||
- Room acoustics
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
All samples should be from the **same speaker**. Mixing voices will produce poor results.
|
||||
</Warning>
|
||||
</Callout>
|
||||
|
||||
## Processing Existing Audio
|
||||
|
||||
@@ -286,11 +286,11 @@ The emotional tone of samples affects generation:
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generate Speech" icon="waveform" href="/overview/generating-speech">
|
||||
<Cards>
|
||||
<Card title="Generate Speech" href="/overview/generating-speech">
|
||||
Use your profile to generate speech
|
||||
</Card>
|
||||
<Card title="Build Stories" icon="film" href="/overview/building-stories">
|
||||
<Card title="Build Stories" href="/overview/building-stories">
|
||||
Create multi-voice narratives
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
@@ -43,9 +43,9 @@ Use formatting to suggest emphasis:
|
||||
- Bold for strong emphasis: "This is **very** important"
|
||||
```
|
||||
|
||||
<Note>
|
||||
<Callout type="info">
|
||||
The model interprets these hints but results may vary.
|
||||
</Note>
|
||||
</Callout>
|
||||
|
||||
## Advanced Features
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@ Voicebox keeps a complete history of all generated audio, making it easy to find
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Full History" icon="clock">
|
||||
<Cards>
|
||||
<Card title="Full History" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>}>
|
||||
Every generation is automatically saved
|
||||
</Card>
|
||||
<Card title="Search & Filter" icon="search">
|
||||
<Card title="Search & Filter" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>}>
|
||||
Find by text, voice, or date
|
||||
</Card>
|
||||
<Card title="Re-generate" icon="rotate">
|
||||
<Card title="Re-generate" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg>}>
|
||||
Regenerate any past generation with one click
|
||||
</Card>
|
||||
<Card title="Export" icon="download">
|
||||
<Card title="Export" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>}>
|
||||
Download individual or batch exports
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
## Viewing History
|
||||
|
||||
@@ -54,20 +54,20 @@ Drag generations to the Stories Editor timeline.
|
||||
|
||||
## Search & Filter
|
||||
|
||||
<Tabs>
|
||||
<Tab title="By Text">
|
||||
<Tabs items={["By Text", "By Voice", "By Date"]}>
|
||||
<Tab value="By Text">
|
||||
Search for specific text content
|
||||
```
|
||||
"Hello world"
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="By Voice">
|
||||
<Tab value="By Voice">
|
||||
Filter by voice profile
|
||||
```
|
||||
Select from dropdown
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="By Date">
|
||||
<Tab value="By Date">
|
||||
Filter by date range
|
||||
```
|
||||
Last 7 days, Last 30 days, Custom range
|
||||
@@ -83,6 +83,6 @@ History is stored locally:
|
||||
- **Windows**: `%APPDATA%/com.voicebox.app/data/`
|
||||
- **Linux**: `~/.config/com.voicebox.app/data/`
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
Deleting the data directory will remove all history. Export important files first.
|
||||
</Warning>
|
||||
</Callout>
|
||||
|
||||
@@ -7,19 +7,19 @@ description: "Download and install Voicebox on macOS, Windows, or Linux"
|
||||
|
||||
Voicebox is available for macOS and Windows, with Linux builds coming soon.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="macOS" icon="apple">
|
||||
<Cards>
|
||||
<Card title="macOS" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2c-1.5 0-2.8.4-3.9 1.1A5.5 5.5 0 0 0 4 2.5C2.5 2.5 1 4 1 6c0 3.5 2.5 6 5 7.5C5 16 4 18 4 20c0 1.5.5 2.5 1.5 3C6.5 23.5 8 24 9.5 24c2 0 3.5-.5 5-2 1.5 1.5 3 2 5 2 1.5 0 3-.5 4-1 1-.5 1.5-1.5 1.5-3 0-2-1-4-2-6.5 2.5-1.5 5-4 5-7.5 0-2-1.5-3.5-3-3.5-.9 0-2.1.4-3.1 1.1A6.5 6.5 0 0 0 12 2Z"/></svg>}>
|
||||
Download for Apple Silicon or Intel Macs
|
||||
</Card>
|
||||
<Card title="Windows" icon="windows">
|
||||
<Card title="Windows" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>}>
|
||||
Download MSI installer or Setup executable
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
### macOS
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Apple Silicon">
|
||||
<Tabs items={["Apple Silicon", "Intel"]}>
|
||||
<Tab value="Apple Silicon">
|
||||
Download: [voicebox_aarch64.app.tar.gz](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_aarch64.app.tar.gz)
|
||||
|
||||
```bash
|
||||
@@ -30,7 +30,7 @@ Voicebox is available for macOS and Windows, with Linux builds coming soon.
|
||||
mv Voicebox.app /Applications/
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Intel">
|
||||
<Tab value="Intel">
|
||||
Download: [voicebox_x64.app.tar.gz](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64.app.tar.gz)
|
||||
|
||||
```bash
|
||||
@@ -45,13 +45,13 @@ Voicebox is available for macOS and Windows, with Linux builds coming soon.
|
||||
|
||||
### Windows
|
||||
|
||||
<Tabs>
|
||||
<Tab title="MSI Installer">
|
||||
<Tabs items={["MSI Installer", "Setup Executable"]}>
|
||||
<Tab value="MSI Installer">
|
||||
Download: [voicebox_x64_en-US.msi](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64_en-US.msi)
|
||||
|
||||
Double-click the MSI file and follow the installation wizard.
|
||||
</Tab>
|
||||
<Tab title="Setup Executable">
|
||||
<Tab value="Setup Executable">
|
||||
Download: [voicebox_x64-setup.exe](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64-setup.exe)
|
||||
|
||||
Run the executable and follow the installation wizard.
|
||||
@@ -60,9 +60,9 @@ Voicebox is available for macOS and Windows, with Linux builds coming soon.
|
||||
|
||||
### Linux
|
||||
|
||||
<Note>
|
||||
<Callout type="info">
|
||||
Linux builds are coming soon. Currently blocked by GitHub runner disk space limitations.
|
||||
</Note>
|
||||
</Callout>
|
||||
|
||||
## First Launch
|
||||
|
||||
@@ -76,9 +76,9 @@ When you launch Voicebox for the first time:
|
||||
|
||||
3. **Backend Server** — The bundled Python server starts automatically
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
First generation will be slower due to model downloads. Subsequent runs use cached models.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -95,9 +95,9 @@ When you launch Voicebox for the first time:
|
||||
- **GPU:** CUDA-capable NVIDIA GPU (for faster generation)
|
||||
- **Storage:** 10GB+ free space
|
||||
|
||||
<Note>
|
||||
<Callout type="info">
|
||||
CPU inference is supported but significantly slower than GPU. A CUDA-capable GPU is highly recommended for real-time workflows.
|
||||
</Note>
|
||||
</Callout>
|
||||
|
||||
## Verification
|
||||
|
||||
@@ -108,12 +108,12 @@ After installation, verify everything works:
|
||||
3. Navigate to **Profiles** and create a test profile
|
||||
4. Generate a short audio clip to verify the TTS engine works
|
||||
|
||||
<Check>
|
||||
<Callout type="success">
|
||||
If you see a green status indicator and can generate audio, you're all set!
|
||||
</Check>
|
||||
</Callout>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<Card title="Quick Start Guide" icon="rocket" href="/overview/quick-start">
|
||||
<Card title="Quick Start Guide" href="/overview/quick-start">
|
||||
Create your first voice profile and generate speech
|
||||
</Card>
|
||||
|
||||
@@ -46,9 +46,9 @@ Voice profiles are the foundation of Voicebox. Each profile contains voice sampl
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
For best results, use clean audio with minimal background noise and consistent speaking tone.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
## Step 2: Generate Speech
|
||||
|
||||
@@ -74,9 +74,9 @@ Now let's use your new voice profile to generate speech.
|
||||
<Step title="Generate">
|
||||
Click **Generate** and wait a few seconds
|
||||
|
||||
<Note>
|
||||
<Callout type="info">
|
||||
First generation may take longer due to model initialization. Subsequent generations will be faster.
|
||||
</Note>
|
||||
</Callout>
|
||||
</Step>
|
||||
|
||||
<Step title="Play & Download">
|
||||
@@ -114,20 +114,20 @@ The Stories Editor lets you create multi-voice narratives with a timeline-based
|
||||
|
||||
## What's Next?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Voice Cloning Guide" icon="microphone" href="/overview/creating-voice-profiles">
|
||||
<Cards>
|
||||
<Card title="Voice Cloning Guide" href="/overview/creating-voice-profiles">
|
||||
Learn advanced techniques for high-quality voice cloning
|
||||
</Card>
|
||||
<Card title="API Integration" icon="code" href="/api-reference">
|
||||
<Card title="API Integration" href="/api-reference">
|
||||
Integrate Voicebox into your own applications
|
||||
</Card>
|
||||
<Card title="Stories Editor" icon="film" href="/overview/stories-editor">
|
||||
<Card title="Stories Editor" href="/overview/stories-editor">
|
||||
Master the multi-track timeline editor
|
||||
</Card>
|
||||
<Card title="Remote Mode" icon="server" href="/overview/remote-mode">
|
||||
<Card title="Remote Mode" href="/overview/remote-mode">
|
||||
Connect to a GPU server for faster generation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
## Tips for Success
|
||||
|
||||
|
||||
@@ -59,6 +59,6 @@ Automatic speech-to-text powered by OpenAI's Whisper model.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
Transcription is useful for creating voice samples from existing audio or generating subtitles.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
@@ -41,9 +41,9 @@ In Remote Mode, the Voicebox desktop app (running on your local machine) communi
|
||||
uvicorn main:app --host 0.0.0.0 --port 17493
|
||||
```
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
This exposes the server to your network. Use a firewall or VPN for security.
|
||||
</Warning>
|
||||
</Callout>
|
||||
</Step>
|
||||
|
||||
<Step title="Open Firewall">
|
||||
@@ -108,9 +108,9 @@ In Remote Mode, the Voicebox desktop app (running on your local machine) communi
|
||||
|
||||
## Security Considerations
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
The API currently has no authentication. Only use on trusted networks or with a VPN.
|
||||
</Warning>
|
||||
</Callout>
|
||||
|
||||
**Best Practices:**
|
||||
- Use a VPN (WireGuard, Tailscale) instead of exposing to the internet
|
||||
@@ -129,9 +129,9 @@ Expected performance on various GPUs:
|
||||
| RTX 3060 | ~5-7s per 10 words |
|
||||
| CPU (12-core) | ~20-30s per 10 words |
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
A GPU with 8GB+ VRAM is recommended for best performance.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@ The Stories Editor is a DAW-like timeline interface for creating multi-voice nar
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Multi-Track Timeline" icon="timeline">
|
||||
<Cards>
|
||||
<Card title="Multi-Track Timeline">
|
||||
Arrange multiple voice tracks in parallel
|
||||
</Card>
|
||||
<Card title="Inline Editing" icon="scissors">
|
||||
<Card title="Inline Editing">
|
||||
Trim and split clips directly in the timeline
|
||||
</Card>
|
||||
<Card title="Auto-Playback" icon="play">
|
||||
<Card title="Auto-Playback">
|
||||
Preview with synchronized playhead
|
||||
</Card>
|
||||
<Card title="Voice Mixing" icon="users">
|
||||
<Card title="Voice Mixing">
|
||||
Build conversations with multiple speakers
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
## Creating a Story
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ Windows SmartScreen may warn that the app is unrecognized.
|
||||
- Click "More info"
|
||||
- Click "Run anyway"
|
||||
|
||||
<Note>
|
||||
<Callout type="info">
|
||||
This is expected for unsigned applications. We're working on code signing for future releases.
|
||||
</Note>
|
||||
</Callout>
|
||||
|
||||
## Server Issues
|
||||
|
||||
@@ -163,9 +163,9 @@ This is expected behavior. The first generation downloads the Qwen3-TTS model (~
|
||||
|
||||
Settings → Generation → Use CPU instead of GPU
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
CPU generation is 5-10x slower but uses system RAM instead of VRAM.
|
||||
</Warning>
|
||||
</Callout>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Reduce Batch Size">
|
||||
@@ -322,9 +322,9 @@ bun run tauri build
|
||||
|
||||
**Solutions:**
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
This will delete all your voice profiles and generation history. Export important profiles first if possible.
|
||||
</Warning>
|
||||
</Callout>
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
|
||||
@@ -28,20 +28,20 @@ Voicebox uses **Qwen3-TTS** from Alibaba to achieve near-perfect voice cloning f
|
||||
|
||||
### Sample Quality
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Do" icon="check">
|
||||
<Cards>
|
||||
<Card title="Do">
|
||||
- Use 10-30 seconds of audio
|
||||
- Clear, consistent speaking
|
||||
- Minimal background noise
|
||||
- Natural speaking pace
|
||||
</Card>
|
||||
<Card title="Don't" icon="xmark">
|
||||
<Card title="Don't">
|
||||
- Very short clips (< 5 seconds)
|
||||
- Heavy background noise
|
||||
- Music or overlapping voices
|
||||
- Heavily processed audio
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</Cards>
|
||||
|
||||
### Multiple Samples
|
||||
|
||||
@@ -51,9 +51,9 @@ Adding multiple samples from the same speaker can improve quality:
|
||||
- Different emotions (happy, serious)
|
||||
- Different recording conditions
|
||||
|
||||
<Tip>
|
||||
<Callout type="info">
|
||||
The model will learn a more robust representation from diverse samples.
|
||||
</Tip>
|
||||
</Callout>
|
||||
|
||||
## Supported Languages
|
||||
|
||||
@@ -65,9 +65,9 @@ More languages coming soon.
|
||||
|
||||
## Limitations
|
||||
|
||||
<Warning>
|
||||
<Callout type="warn">
|
||||
Voice cloning should only be used with consent. Ensure you have permission to clone someone's voice.
|
||||
</Warning>
|
||||
</Callout>
|
||||
|
||||
- Quality depends on sample clarity
|
||||
- Works best with consistent speaking tone
|
||||
|
||||
Reference in New Issue
Block a user