Custom Voice
Record a short sample, point the settings at it, and N.O.V.A speaks in your own cloned voice.
How the voice is chosen
N.O.V.A speaks through one small module that picks an engine from config/.env:
| Engine | NOVA_TTS | Voice | Notes |
|---|---|---|---|
| XTTS-v2 | xtts | Your own recorded voice | Natural. Needs the GPU build of PyTorch. Loads in the background so it doesn't block startup (see Fast startup) |
| pyttsx3 | pyttsx3 | Windows voices (David, Hazel, Zira) | Instant but robotic. Also the automatic fallback if XTTS fails to load |
Adding a voice only means giving XTTS a new reference recording and pointing the settings at it. No code changes are needed. Set up the environment first: Installation & Environment.
XTTS-v2 is released under the Coqui Public Model License, which is non-commercial only. Read coqui.ai/cpml before using it in anything you sell or offer as a paid service.
Record the reference sample
The clone copies whatever is in the recording: pitch, pace, accent and tone. A clean sample matters more than anything else. Read this aloud once or twice and keep the cleanest take:
The morning train from Pune arrived a little late, so I grabbed a cup of strong, sweet tea and watched the rain drip off the station roof. By nine thirty, the platform was crowded with students, vendors and tired travellers, all hurrying in different directions. Have you ever noticed how a busy place can feel strangely peaceful once you stop rushing? I checked my phone: three new messages, two reminders and a weather alert for 28 degrees by noon. Quickly, quietly, and without any fuss, I put it away and smiled. Some days are loud, others are slow, but each one teaches you something. Today, I decided to take the long way home.
- Use a quiet room with soft furnishings (curtains, a bed, a sofa) to reduce echo, and switch off fans and the AC
- Keep the mic about a hand-span (15 to 20 cm) from your mouth and don't move between takes
- Speak at your normal conversational pace, not in a stiff reading voice
- One voice only, with no music or background noise
- Save as WAV, mono, 22.05 kHz or higher. If your recorder saves
.m4a, convert it with Audacity (File, then Export as WAV) - 10 to 30 seconds of the cleanest speech is enough. A longer clip isn't better if the extra audio is noisy
Save it as ai-models/voices/<name>.wav.
Keep a copy outside the project, and keep it out of git. The rest of ai-models/ is a cache that can be re-downloaded, but your recording can't be re-created.
Register the voice
NOVA_TTS=xtts
NOVA_VOICE_SAMPLE=ai-models/voices/my-voice.wav
NOVA_SPEED=1.2
NOVA_SEMITONES=0
NOVA_VOICE=HazelEvery setting is explained in Settings. To switch to a different recording, add a new WAV to ai-models/voices/ and change only NOVA_VOICE_SAMPLE.
Test it
python -m src.output.tts_engine- On the first run it asks you to accept the XTTS licence. Type
yto accept the non-commercial terms. It then downloads about 2 GB of model files - You should see
Loading XTTS on cuda...and then hear your cloned voice - If you see
XTTS failed to load, N.O.V.A fell back to the Windows voice. The error printed above that line says why. See Troubleshooting
Once that works, start N.O.V.A as usual with python main.py.
Tune the sound
- Pace: change
NOVA_SPEEDin steps of 0.1. Above about 1.3 it sounds rushed - Pitch: change
NOVA_SEMITONESby 0.5 to 1 at a time. Large shifts sound processed and drift away from your real voice, so stay within about 3 either way - Quality problems (mushy, echoey, wrong accent) almost always come from the recording. Re-record in a quieter room with a steady mic distance, or try a different 15 to 20 second chunk
- Change one thing at a time so you know what caused a difference
Fast startup
Loading XTTS-v2 can take up to about 45 seconds on a laptop GPU. Instead of making you wait for it, N.O.V.A loads the voice in the background and plays a saved greeting straight away:
- Background loading:
create_tts_engine()returns immediately and the model loads in a separate thread while the database, monitors and welcome screen start. One shared engine is used everywhere, so the model is never loaded twice - Saved greeting: the startup greeting is rendered once in your cloned voice and stored as
assets/greeting.wav. It plays instantly at launch - First reply: if you speak or type a command before XTTS has finished loading, the first spoken reply waits for it. After that, replies are instant
- Fallback: if the WAV is missing, N.O.V.A speaks the greeting live, which waits for XTTS as before
Create the greeting once. It loads XTTS, so it takes about as long as a normal startup:
python scripts/make_greeting.pyRun it again whenever you change the greeting text (GREETING_TEXT in src/output/greeting.py), the voice sample, or NOVA_SPEED or NOVA_SEMITONES. If it reports No module named src, run python -m scripts.make_greeting instead.
The console prints [TTS] timing lines for the imports, model load, voice analysis and warm-up, ending with engine ready in. If building the shared engine appears more than once, see Troubleshooting.
Use a Windows voice instead
List the voices installed on your machine:
import pyttsx3
engine = pyttsx3.init()
for v in engine.getProperty("voices"):
print(v.name, "|", v.id)The default Windows voices are David (US male), Hazel (UK female) and Zira (US female). To add more, open Settings, then Time & language, then Language & region, and add a language with the Text-to-speech option ticked. Restart the terminal and list the voices again. Newer Natural voices don't appear, because pyttsx3 only reads the older SAPI5 voices.
NOVA_TTS=pyttsx3
NOVA_VOICE=HazelNOVA_VOICE matches any part of the voice name. If it is blank or doesn't match, Windows uses its default voice, which is David.