11 KiB
Voiceover Generation Guide
Generate professional AI voiceover using ElevenLabs, precisely timed and content-matched to video scenes.
Requirements
ELEVEN_LABS_API_KEYenvironment variableffmpeginstalled for audio processingwhisperCLI or Python package for timing verification- Python 3.x for the generation script
Critical Workflow: Scene-Voiceover Alignment
The #1 problem with promo video voiceovers is misalignment between what's being said and what's on screen. Follow this precise workflow:
Step 1: Extract Scene Timings from Remotion
First, read the main composition file to extract exact scene timings:
// Example from AIVoicePromo.jsx - extract these values:
const OPENING_DURATION = 150; // 5 seconds (frames / 30fps)
const PAIN_POINT_DURATION = 120; // 4 seconds each
const SOLUTION_INTRO_DURATION = 120;
const FEATURE_DURATION = 150; // 5 seconds each
const RESULTS_DURATION = 180; // 6 seconds
const CLOSING_DURATION = 240; // 8 seconds
Calculate cumulative timestamps:
Scene | Start | End | Duration
-------------------|-------|------|----------
Opening | 0s | 5s | 5s
Pain Point 1 | 5s | 9s | 4s
Pain Point 2 | 9s | 13s | 4s
Pain Point 3 | 13s | 17s | 4s
Solution Intro | 17s | 21s | 4s
Feature 1 | 21s | 26s | 5s
Feature 2 | 26s | 31s | 5s
Feature 3 | 31s | 36s | 5s
Feature 4 | 36s | 41s | 5s
Feature 5 | 41s | 46s | 5s
Results | 46s | 52s | 6s
Closing | 52s | 60s | 8s
Step 2: Extract Scene Content for Script Matching
Read each scene component to understand what's visually displayed:
# For each scene, extract:
# - Title text
# - Subtitle text
# - Stats displayed
# - Key visual elements
The voiceover MUST reference what's on screen. Example mapping:
| Scene | Visual Content | Voiceover Should Say |
|---|---|---|
| Opening | "Every Missed Call Costs You Money", stats: $650K, 38%, 4hrs | "Every missed call costs you money. With six hundred and fifty thousand dollars lost annually..." |
| Pain Point 1 | "Missed Calls = Lost Revenue", stat: 500+ | "Your dealership handles over five hundred service calls every month..." |
| Feature 1: Smart Voicemail Queue | Shows queue UI mockup | "The Smart Voicemail Queue transcribes every message instantly..." |
Step 3: Write Time-Aligned Script
Structure your script with EXACT scene boundaries:
[0-5s - Opening - Visual: "Every Missed Call Costs You Money"]
Every missed call costs you money. With six hundred and fifty thousand dollars lost annually...
[5-9s - Pain Point 1 - Visual: "Missed Calls = Lost Revenue", 500+ stat]
Your dealership handles over five hundred service calls every month...
[17-21s - Solution Intro - Visual: ShopLoader logo reveal]
Introducing ShopLoader AI Voice. Turn voicemails into appointments in minutes.
Content Rules:
- Reference the title/stat shown on screen
- Don't describe something not visible
- Time the "reveal" moment (e.g., "Introducing ShopLoader" exactly when logo appears)
Step 4: Generate Voiceover Sections
Use the generation script with precise start times that leave ~1s buffer before scene transitions:
sections = [
(1, "Every missed call costs you money."), # Scene starts at 0s, speak at 1s
(6, "Your dealership handles..."), # Scene starts at 5s, speak at 6s
(18, "Introducing ShopLoader AI Voice."), # Scene starts at 17s, speak at 18s
# ...
]
Step 5: Verify Timing with Whisper Transcription
CRITICAL STEP - Do not skip!
After generating the voiceover, transcribe it to verify actual timing:
# Using Whisper CLI
whisper voiceover.mp3 --model tiny --output_format srt
# Or using Python
python3 -c "
import whisper
model = whisper.load_model('tiny')
result = model.transcribe('voiceover.mp3')
for s in result['segments']:
print(f\"{s['start']:.1f}s - {s['end']:.1f}s: {s['text']}\")
"
Step 6: Compare and Validate
Compare Whisper output against scene timings:
Expected vs Actual Timing Analysis:
-----------------------------------
Scene: Solution Intro (17-21s)
Expected: "Introducing ShopLoader" starts at 18s
Actual: "Introducing ShopLoader" starts at 17.2s ✓ (within scene)
Scene: Feature 1 (21-26s)
Expected: "Smart Voicemail Queue" at 22s
Actual: "Smart Voicemail Queue" at 20.8s ✗ (OVERLAPS with previous scene!)
If overlap detected: Increase delay for that section and regenerate.
Step 7: FIX ALL OVERLAPS (Mandatory)
If ANY overlap is detected, you MUST fix it before proceeding. Do not ask the user.
Overlap fixes (in order of preference):
- Shorten the text - Make it punchier. Cut filler words. "The Smart Voicemail Queue transcribes every message instantly" → "Voicemails transcribed instantly"
- Add a beat - Insert "..." in the text to create a natural pause
- Increase gap - Push the next section's start time 1-2s later
Other timing fixes:
- Speech ends after scene: Shorten the text, remove unnecessary words
- Too much silence: Decrease start time or add more content
- Wrong content timing: Adjust start time to match visual reveal
After ANY fix: Regenerate and verify with Whisper again. Repeat until ZERO overlaps.
Script Writing Guidelines
Content-Scene Matching Rules
- Reference what's visible: If the screen shows "500+ calls", say "five hundred"
- Match reveal timing: Product name first spoken when logo appears
- Describe features when shown: Talk about "SMS Quick Response" during SMS mockup scene
- Complete the thought with benefit: Don't just name features, explain why they matter
DO:
- Write complete thoughts, not fragments
- Allow 1s buffer at scene start before speaking
- Allow 0.5s buffer before scene end
- Match spoken stats to visual stats
- Use conversational, natural language
DON'T:
- Don't reference visuals not yet shown
- Don't continue speaking into the next scene
- Don't make it too dense (audio needs breathing room)
- Don't skip the emotional hook in the opening
Script Template (60 seconds)
[0-5s - Opening - Visual: Hook headline + 3 stats]
[Start speaking at 1s]
Every missed call costs you money. With [stat from screen] and [stat from screen], your [consequence].
[5-9s - Pain Point 1 - Visual: Title + stat]
[Start speaking at 6s]
[Reference the title shown]. [Expand with the stat displayed].
[9-13s - Pain Point 2 - Visual: Title + stat]
[Start speaking at 10s]
[Reference the title shown]. [Emotional consequence].
[13-17s - Pain Point 3 - Visual: Title + stat]
[Start speaking at 14s]
[Reference the title shown]. [What happens as a result].
[17-21s - Solution Intro - Visual: Product logo reveal]
[Start speaking at 18s - TIME THIS TO LOGO APPEARANCE]
Introducing [Product Name]. [One-line value prop].
[21-26s - Feature 1 - Visual: Feature title + UI mockup]
[Start speaking at 22s]
[Feature name from screen] [what it does], so [benefit to user].
[26-31s - Feature 2]
[Start speaking at 27s]
[Feature name from screen] [what it does], so [benefit to user].
[31-36s - Feature 3]
[Start speaking at 32s]
[Feature name from screen] [what it does], so [benefit to user].
[36-41s - Feature 4]
[Start speaking at 37s]
[Feature name from screen] [what it does], so [benefit to user].
[41-46s - Feature 5]
[Start speaking at 42s]
[Feature name from screen] [what it does], so [benefit to user].
[46-52s - Results - Visual: Outcome stats]
[Start speaking at 47s]
[Stat from screen]. [Stat from screen]. [Emotional benefit].
[52-60s - Closing - Visual: CTA + branding]
[Start speaking at 53s]
[Product Name]. [Tagline from screen]. [CTA].
ElevenLabs Voice Settings
Recommended Voice: Matilda
- Voice ID:
XrExE9yKIg1WjnnlVkGX - Characteristics: American, Professional, Knowledgeable, Clear
Voice Settings for Professional Delivery:
{
"model_id": "eleven_multilingual_v2",
"voice_settings": {
"stability": 0.65,
"similarity_boost": 0.85,
"style": 0.2,
"use_speaker_boost": true
}
}
Adjustments:
- If too breathy: Increase
stabilityto 0.70-0.75 - If too robotic: Decrease
stabilityto 0.55-0.60 - If speech too fast: Decrease
style, or add "..." pauses in text
Generation & Verification Workflow
Full Pipeline:
# 1. Generate voiceover sections
python ${SKILL_DIR}/scripts/generate_voiceover.py
# 2. Transcribe to verify timing
whisper voiceover.mp3 --model tiny --output_format srt
# 3. Review SRT file against scene timings
cat voiceover.srt
# 4. If timing issues found, adjust sections[] start times and regenerate
# 5. Normalize audio
ffmpeg -y -i voiceover.mp3 -af "loudnorm=I=-16:TP=-1.5:LRA=11" voiceover-normalized.mp3
# 6. Add background music
ffmpeg -y -i voiceover-normalized.mp3 -i music.mp3 \
-filter_complex "[1:a]volume=0.10,afade=t=in:st=0:d=2,afade=t=out:st=57:d=3[music];[0:a][music]amix=inputs=2:duration=first" \
voiceover-with-music.mp3
# 7. Combine with video
ffmpeg -y -i video.mp4 -i voiceover-with-music.mp3 -c:v copy -map 0:v:0 -map 1:a:0 final.mp4
# 8. Watch final video to confirm alignment
Troubleshooting
CRITICAL: Never accept overlaps. Fix them immediately and regenerate.
| Issue | Cause | Solution |
|---|---|---|
| Voiceover overlaps itself | Sections too close | FIX NOW: Shorten text OR increase gap, regenerate, verify again |
| Even 0.4s overlap | Text too long for scene | FIX NOW: Make text punchier, cut words, regenerate |
| Speech doesn't match screen | Script not aligned | Re-read scene components, match text to visuals |
| "Introducing X" before logo | Start time too early | Delay to 1s after scene start |
| Feature description during wrong scene | Timing drift | Use Whisper to find actual timestamps, adjust |
| Voice too fast | Too much text | Shorten text or add "..." for pauses |
| Awkward cuts between scenes | No buffer | Leave 0.5-1s silence before scene transitions |
The loop: Generate → Whisper verify → Fix overlaps → Regenerate → Verify again → Repeat until clean
Background Music
Auto-Download Royalty-Free Tracks (Verified Working)
# Bensound - "Inspire" (corporate/uplifting) - RECOMMENDED
curl -sL "https://www.bensound.com/bensound-music/bensound-inspire.mp3" -o background-music.mp3
# Bensound - "Creative Minds" (light/positive)
curl -sL "https://www.bensound.com/bensound-music/bensound-creativeminds.mp3" -o background-music.mp3
# Pixabay - Corporate background (256kbps, high quality)
curl -sL "https://cdn.pixabay.com/download/audio/2022/03/15/audio_8cb749d484.mp3" -o background-music.mp3
# Verify download worked
ls -lah background-music.mp3 && file background-music.mp3
Mixing Settings
- Volume: 10% of voice level (
volume=0.10) - Fade in: 2 seconds at start (
afade=t=in:st=0:d=2) - Fade out: 3 seconds before video end (
afade=t=out:st=57:d=3for 60s video) - Style: Subtle corporate/tech underscore, no lyrics
Mix Command
ffmpeg -y -i voiceover-normalized.mp3 -i background-music.mp3 \
-filter_complex "[1:a]volume=0.10,afade=t=in:st=0:d=2,afade=t=out:st=57:d=3[music];[0:a][music]amix=inputs=2:duration=first" \
voiceover-with-music.mp3
Other Sources (if auto-download fails)
- Bensound.com - Free with attribution
- Mixkit.co - Free, no attribution needed
- Pixabay.com/music - Free
- Epidemic Sound (subscription)
- Artlist (subscription)