Building a pack, start to finish.
0. Building more than one voice?
Make a pack per voice and pass --pack to everything:
python scripts\wvs.py pack new my-voice --label "My voice"
python scripts\wvs.py run --pack my-voice
Each pack keeps its own source list, clips, and export under packs/<name>/, so
two voices never mix. Everything below happens inside a pack. Building only one
voice? Ignore packs and use the shared audio/ tree.
1. Pick a voice you can actually use
Start with audio you have the right to use, and to redistribute if you plan to publish anything. Your own voice, someone who gave explicit permission, public-domain or permissively licensed material, or synthetic voices with clear rights.
Do not use copyrighted characters, actors, or celebrity voices without permission. See LEGAL.md.
2. Decide what the pack says
config/phrases.json is the inventory. Each entry:
| Field | Meaning |
|---|---|
id |
Stable machine-readable prompt ID. Used in filenames and routes. |
label |
Human-readable phrase, shown in checklists. |
required |
Whether the pack is incomplete without it. |
filename |
Final filename in audio/master. |
status |
missing, sourced, extracted, cleaned, synthesized, or final. Maintained by the pipeline. |
waze_filename |
The exact name Waze expects, e.g. TurnLeft.mp3. Validated against Waze's list. |
units |
any, metric, or imperial. Distance callouts are two separate file sets. |
weight |
This prompt's share of the pack size budget. Higher means more bitrate. |
group |
start, distance, maneuver, lane, roundabout, arrival, alert, or misc. |
order |
Position within the group. |
tts_text |
What synthesis should say, when it differs from the label. |
notes |
Production notes. |
aliases |
Alternative wordings, for your own reference. |
The shipped list covers all 43 prompts Waze recognises. You do not need every one: Waze falls back to its default voice for anything absent, and dropping prompts you will never hear is the easiest way to free up size budget.
3. Find your clips
Listen through your source media and note where each phrase occurs. Copy the sample CSV and fill it in:
copy data\sources.sample.csv data\my-sources.csv
phrase_id,source_path,start,end,take,preferred,gain_db,notes
turn_left,C:\media\episode-one.m4a,00:12:03.100,00:12:04.250,1,,,first attempt
turn_left,C:\media\episode-one.m4a,00:41:55.000,00:41:56.100,2,1,,cleaner delivery
Using a pack? Its packs/<name>/sources.csv already exists with the right
header and is picked up automatically, so there is nothing to copy.
Practical advice:
- Grab several takes. Cheap to note, and you will not know which one works until you
hear it in a route. Mark the winner with
preferred=1later. - Be generous with the boundaries. Include a little air on each side; silence trimming tidies it up, and a syllable cut short cannot be recovered.
- Prefer lines said in isolation. A phrase over music can be rescued by Demucs, but a clean one never needs rescuing.
- Watch the tone. A line delivered as a question sounds wrong as a turn instruction, however clean the audio is.
- Rows are validated before any ffmpeg runs, so a typo fails immediately.
4. Run the pipeline
python scripts\wvs.py run --sources data\my-sources.csv
Or step by step, which is what you will want while iterating:
python scripts\wvs.py extract --sources data\my-sources.csv
python scripts\wvs.py clean --mode demucs
python scripts\wvs.py synth --accept-voice-terms
python scripts\wvs.py normalize
python scripts\wvs.py validate
Steps skip work that already exists; add --force to redo it.
5. Fill the gaps
Some phrases will not exist in your source. Three options:
- Synthesize them in your own voice:
python scripts\wvs.py synth. See docs/tts.md. - Record them yourself and drop the file into
audio/extracted/as<phrase_id>__take1.wav. - Leave them. Waze falls back to its default voice for anything absent, and the export checklist lists what is missing.
6. Listen to it as a route
The step most people skip, and the one that catches the problems.
python scripts\wvs.py qa
python scripts\wvs.py qa --route highway_merge
python scripts\wvs.py qa --list-routes
Playback chains phrases the way Waze does, so you hear "In a quarter mile, turn right" as
one instruction, and AndThen joining two maneuvers. Mark each one pass or fail; verdicts
are saved to audio/qa-report.json.
What to listen for:
- A prompt noticeably louder or quieter than its neighbours. Normalization should prevent this; if one stands out, check whether validation flagged it as an outlier.
- Clipped first or last syllables.
- Two chained phrases that do not flow, usually a distance clip with too much trailing air.
- Synthesized lines that are subtly too slow. Genuinely irritating at speed.
For the real test, render the route and play it in the car:
python scripts\wvs.py qa --render route.wav --bed road-noise.wav
7. Export and upload
python scripts\wvs.py export
This builds audio/export/pack/: MP3s named exactly as Waze expects, covering both metric
and imperial distance callouts, with bitrates allocated so the whole pack fits Waze's
0.8 MB aggregate limit. The step prints the total against that limit:
Pack total: 793.5 kB of 795.0 kB (99.8%) - within budget
If it says over budget, fix that before uploading. Waze rejects oversized packs silently:
the share button greys out, or the pack downloads and plays nothing. HOW-TO-UPLOAD.md in
the export folder lists what to cut, cheapest first. Quick wins: drop TickerPoints.mp3,
drop roundabout ordinals you will never hear, or export a single unit system:
python scripts\wvs.py export --units metric
Then upload audio/export/pack/ with the community tool at
https://github.com/pipeeeeees/waze-voicepack-links, keep the UUID it returns, and open
https://waze.com/ul?acvp=<UUID> on your phone.
Prefer not to use third-party tooling? The in-app recorder still works, at the cost of
audio quality, and python scripts\record_assist.py walks the prompt list for you.
Full detail in docs/waze-import-workflow.md.
8. Iterate
Nothing here is one-shot. Fix a phrase and re-run just that phrase:
python scripts\wvs.py extract --only turn_left --force --sources data\my-sources.csv
python scripts\wvs.py clean --only turn_left --force
python scripts\wvs.py normalize --only turn_left --force
python scripts\wvs.py qa
Keeping the repo clean
Your media, clips, manifests, datasets, and model weights are all Git-ignored. Keep it
that way. git status before pushing.