Why AI dubbing matters
If you’ve ever tried to reach a global audience, you know the pain of translating and recording voice‑overs for every language you support. Traditional dubbing requires hiring talent, booking studio time, and then painstakingly syncing the new audio to the original video.
Enter AI dubbing. With modern text‑to‑speech (TTS) and voice‑cloning tech, you can generate natural‑sounding narration in seconds, keep the original timing, and iterate instantly. This opens the door for rapid localization, A/B testing of scripts, and even creating personalized video experiences for individual users.
In this article we’ll walk through a practical, end‑to‑end workflow for building an AI dubbing tool that takes a video file, a transcript, and a target voice, then spits out a fully dubbed video ready for upload. The core of the speech synthesis will be powered by ElevenLabs – a service that delivers high‑quality neural voices and an easy‑to‑use API.
Core components of an AI dubbing pipeline
- Speech synthesis / voice cloning – Turn plain text into a natural‑sounding audio track.
- Timing extraction – Figure out where each line of dialogue belongs in the video.
- Audio‑video merging – Replace the original soundtrack with the newly generated voice while preserving background music, sound effects, and lip‑sync as best as possible.
We’ll cover each piece below, focusing on the parts you can implement yourself and where a third‑party service like ElevenLabs saves you hours of work.
Getting started with ElevenLabs
ElevenLabs provides a REST API that lets you generate speech from text, optionally using a custom cloned voice. Sign up through the affiliate link below to get an API key and a generous free tier:
https://try.elevenlabs.io/kr07zfuqn1bp
1️⃣ Install the Python client
pip install elevenlabs
2️⃣ Generate speech from a script
from elevenlabs import generate, set_api_key, voices
# Replace with your own key from the ElevenLabs dashboard
set_api_key("YOUR_ELEVENLABS_API_KEY")
# Choose a voice – you can list available voices with `voices()`
voice_id = "EXAVITQu4vr4xnSDxMaL" # example voice ID
def synthesize(text: str, output_path: str):
audio = generate(
text=text,
voice=voice_id,
model="eleven_monolingual_v1", # high‑quality model
latency=0.5, # faster response for dev loops
)
with open(output_path, "wb") as f:
f.write(audio)
# Example usage
synthesize(
text="Welcome to the future of AI dubbing.",
output_path="output.wav"
)
If you have a custom voice you cloned earlier, simply replace voice_id with the ID returned by the cloning endpoint. The API returns a WAV byte string, which you can write to disk or pipe directly into a video processing step.
Extracting timing information from the source video
For a clean dub you need to know when each line of dialogue starts and ends. If you already have a subtitle file (.srt or .vtt), you can parse it with the pysrt library:
pip install pysrt
import pysrt
def load_subtitles(srt_path):
subs = pysrt.open(srt_path)
# Return a list of (start_seconds, end_seconds, text)
return [(sub.start.ordinal/1000, sub.end.ordinal/1000, sub.text) for sub in subs]
# Example
segments = load_subtitles("movie_en.srt")
print(segments[:3])
If you don’t have subtitles, tools like autosub or Whisper can generate them for you automatically. The important thing is to end up with a list of timed text segments.
Syncing audio with video
Once you have a series of audio files that match the subtitle timestamps, you can stitch them together and overlay them on the original video. ffmpeg is perfect for this job, but you can also use Python’s moviepy for a more scriptable approach.
Using ffmpeg (quick and dirty)
# Concatenate all generated wav files into one track
ffmpeg -f concat -safe 0 -i <(for f in output_*.wav; do echo "file '$PWD/$f'"; done) -c copy merged.wav
# Replace the original audio while preserving background music (if any)
ffmpeg -i input.mp4 -i merged.wav -c:v copy -map 0:v:0 -map 1:a:0 -shortest dubbed.mp4
Using moviepy (more control)
pip install moviepy
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_audioclips
def build_dubbed_video(video_path, audio_segments, output_path):
# video_path: original video
# audio_segments: list of (start, end, wav_path) tuples
video = VideoFileClip(video_path)
# Load each generated clip and set its start time
clips = []
for start, end, wav in audio_segments:
clip = AudioFileClip(wav).set_start(start)
clips.append(clip)
# Combine all voice clips into a single audio track
voice_track = concatenate_audioclips(clips)
# Preserve the original audio (music, SFX) and overlay the voice
final_audio = video.audio.volumex(0.5).fx(
lambda a: a.set_fps(voice_track.fps)
).overlay(voice_track)
# Write the final video
video.set_audio(final_audio).write_videofile(output_path, codec="libx264", audio_codec="aac")
# Example usage
audio_segments = [(s, e, f"output_{i}.wav") for i, (s, e, _) in enumerate(segments)]
build_dubbed_video("input.mp4", audio_segments, "dubbed.mp4")
The overlay call mixes the new voice with any background audio that was present in the original video, giving you a natural‑sounding dub without completely muting the soundtrack.
Putting it all together
Below is a minimal “one‑stop” script that:
- Loads subtitles.
- Calls ElevenLabs for each line.
- Saves each audio snippet.
- Merges everything back into the video.
import os
import pysrt
from elevenlabs import generate, set_api_key
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_audioclips
set_api_key("YOUR_ELEVENLABS_API_KEY")
VOICE_ID = "EXAVITQu4vr4xnSDxMaL"
def synthesize_line(text, out_path):
audio = generate(text=text, voice=VOICE_ID, model="eleven_monolingual_v1")
with open(out_path, "wb") as f:
f.write(audio)
def main(video_path, srt_path, out_path):
subs = pysrt.open(srt_path)
audio_segments = []
# Step 1 – generate voice for each subtitle line
for i, sub in enumerate(subs):
wav_path = f"tmp_{i}.wav"
synthesize_line(sub.text, wav_path)
audio_segments.append((sub.start.ordinal/1000, sub.end.ordinal/1000, wav_path))
# Step 2 – combine with video
build_dubbed_video(video_path, audio_segments, out_path)
# Optional cleanup
for _, _, wav in audio_segments:
os.remove(wav)
def build_dubbed_video(video_path, audio_segments, output_path):
video = VideoFileClip(video_path)
clips = [AudioFileClip(wav).set_start(start) for start, _, wav in audio_segments]
voice_track = concatenate_audioclips(clips)
final_audio = video.audio.volumex(0.5).overlay(voice_track)
video.set_audio(final_audio).write_videofile(output_path, codec="libx264", audio_codec="aac")
if __name__ == "__main__":
main("input.mp4", "movie_en.srt", "dubbed_output.mp4")
Pro tip: Cache the generated WAV files locally. If you need to tweak the script, you won’t be hitting the ElevenLabs API again and can iterate instantly.
Tips & best practices
- Voice consistency – Use a single cloned voice for an entire project to keep the dub sounding cohesive.
- Chunk size – ElevenLabs works best with sentences or short paragraphs (< 200 characters). Split longer subtitles accordingly.
-
Latency vs quality – The
model="eleven_monolingual_v1"offers the best naturalness, but you can switch to a faster model (eleven_fast) for rapid prototyping. -
Audio levels – When overlaying voice on top of existing music, lower the background track’s volume (
volumex(0.5)) so speech stays clear. -
Error handling – Wrap the API call in a
try/exceptblock and implement exponential back‑off; network hiccups are common when processing many lines. - Legal – Always respect the licensing terms of the source video and ensure you have the right to dub it, especially if you plan to redistribute.
Wrap‑up
Building an AI dubbing tool is surprisingly approachable thanks to modern TTS services and open‑source media libraries. By leveraging ElevenLabs for high‑fidelity voice synthesis, you can replace manual voice‑over pipelines with a fully automated script that runs in minutes, not days.
Ready to give it a spin? Grab your free ElevenLabs API key through the affiliate link below, fire up the example script, and start dubbing your own videos today.
Try ElevenLabs now: https://try.elevenlabs.io/kr07zfuqn1bp 🚀
Top comments (0)