How to Select a TTS Engine in VoiceStudio: A Complete Guide
Select a TTS engine in VoiceStudio by opening Settings → Voice, choosing from the dropdown of available engines, and letting the app save your selection to user_env.tts_engine for all subsequent voice operations.
VoiceStudio simplifies text-to-speech engine management through a registry-based discovery system. The Python worker exposes available backends via a REST API, while the React frontend renders them as a user-friendly dropdown. This article explains exactly how engine selection works under the hood, with file paths and code references from the debpalash/VoiceStudio repository.
Where TTS Engines Are Discovered
VoiceStudio maintains a central registry of TTS backends in services/tts_backend.py. The _REGISTRY dictionary holds all engine classes, keyed by their unique identifiers.
Engine registration happens through a decorator pattern:
# services/tts_backend.py
_REGISTRY = {}
def register_engine(id_: str):
def decorator(cls):
_REGISTRY[id_] = cls
return cls
return decorator
@register_engine("indextts")
class IndexttsBackend(OmniVoiceBackend):
@staticmethod
def is_available() -> Tuple[bool, Optional[str]]:
# Verify binary, model files, GPU availability
return (True, None)
Each engine implements is_available() to report whether it can run on the current worker. This method returns a tuple: (available: bool, reason: Optional[str]). The reason string explains why an engine is unavailable—missing binaries, insufficient VRAM, or missing model weights.
How the Frontend Lists Available Engines
The VoicePanel component in frontend/src/components/settings/VoicePanel.jsx fetches the engine list when mounted. It calls GET /api/settings/tts-engines and transforms the response into a dropdown.
// frontend/src/components/settings/VoicePanel.jsx
const [engines, setEngines] = useState([]);
const [engineAvailable, setEngineAvailable] = useState(true);
const [engineReason, setEngineReason] = useState(null);
useEffect(() => {
apiJson('/api/settings/tts-engines')
.then(data => {
setEngineAvailable(data?.engine_available !== false);
setEngineReason(data?.engine_reason || null);
setEngines(data?.engines ?? []);
});
}, []);
The API returns an array of engine objects with these fields:
id— the registry key (e.g.,"indextts","voxcpm2")label— human-readable display nameavailable— boolean fromis_available()reason— explanation whenavailableis false
The UI filters to show only available engines by default, with tooltips for unavailable ones.
Selecting and Persisting Your TTS Engine
When you pick an engine from the dropdown, the frontend POSTs the choice to the backend:
// frontend/src/components/settings/VoicePanel.jsx (lines 252-260)
<Select
value={selectedEngine?.id}
onChange={e => apiPost('/api/settings/tts-engine', {engine: e.target.value})}
options={engines.map(e => ({value: e.id, label: e.label}))}
/>
The FastAPI route validates the engine ID against _REGISTRY, then stores it:
@app.post("/api/settings/tts-engine")
def set_tts_engine(payload: dict, user: User = Depends(get_current_user)):
engine_id = payload.get("engine")
if engine_id not in services.tts_backend._REGISTRY:
raise HTTPException(400, "Unknown TTS engine")
user.settings["tts_engine"] = engine_id
return {"ok": True}
This writes to the user's environment file (.voice_studio_env). The setting takes effect immediately—no restart required.
How the Worker Uses Your Selected Engine
Every TTS operation calls get_active_tts_backend() to resolve the concrete backend instance. This function:
- Reads
user.settings["tts_engine"] - Looks up the class in
_REGISTRY - Instantiates it with worker-appropriate configuration
The resolution logic is exercised in tests/test_worker_executor_residency.py around line 200. The function guarantees that voice-over, dubbing, and audiobook generation tasks all use the same user-selected engine.
Troubleshooting Engine Availability
If your preferred engine doesn't appear in the dropdown, check these common causes:
- Missing binaries — The engine's executable isn't in
PATHor the expected directory - Model weights missing — Required checkpoint files aren't downloaded
- GPU requirements — The engine needs CUDA but the worker has no compatible GPU
The UI surfaces this information through engine_reason. In VoicePanel.jsx, unavailable engines trigger a banner:
{engineAvailable ? (
<Select /* ... */ />
) : (
<span>{engineReason || t('voicePanel.engine_unavailable')}</span>
)}
Adding a Custom TTS Engine
To register a new engine:
- Create a class extending
OmniVoiceBackend(or the appropriate base) - Implement
is_available()with your detection logic - Apply the
@register_engine("your_id")decorator - Ensure the worker module containing your class is imported at startup
The test file tests/test_worker_tts_parity.py demonstrates mock engine registration for validation.
Summary
- Engine discovery happens through
services.tts_backend._REGISTRY, populated by decorators on backend classes - UI population flows from
GET /api/settings/tts-enginesto theVoicePaneldropdown infrontend/src/components/settings/VoicePanel.jsx - Selection persistence stores
tts_enginein user settings viaPOST /api/settings/tts-engine - Runtime resolution uses
get_active_tts_backend()to instantiate the chosen engine for every TTS task - Availability checks run automatically; unavailable engines show explanatory tooltips instead of blocking the UI
Frequently Asked Questions
Why doesn't my installed engine appear in the dropdown?
The engine's is_available() method returned False. Check engine_reason in the UI or run the worker with debug logging to see which requirement failed—missing binary, model file, or GPU capability.
Do I need to restart VoiceStudio after changing engines?
No. The new tts_engine value is read at the start of each TTS task. Active jobs continue with their original engine; new jobs use your selection immediately.
Can I use different engines for different projects?
Not through the UI. VoiceStudio stores one global tts_engine per user environment. For per-project engines, you would need to modify get_active_tts_backend() to accept an override parameter or manage environment switching externally.
Where is the engine selection saved?
In the user's VoiceStudio environment file, typically .voice_studio_env in the working directory or a platform-specific config location. The exact path depends on how the User model resolves user_env in your deployment.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →