pydub is the most popular Python library for audio manipulation. It simplifies tasks that would require complex FFmpeg commands: format conversion, trimming, joining, normalization, and fade effects.
Installation
pip install pydub
# pydub requires FFmpeg installed on the system
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows — download from ffmpeg.org and add to PATH
Format conversion
from pydub import AudioSegment
# Load audio (pydub auto-detects format)
audio = AudioSegment.from_file('song.mp3')
# Export to different formats
audio.export('song.wav', format='wav')
audio.export('song.flac', format='flac')
audio.export('song.ogg', format='ogg')
audio.export('song.aac', format='aac')
# With specific bitrate (MP3)
audio.export('song_128k.mp3', format='mp3', bitrate='128k')
audio.export('song_320k.mp3', format='mp3', bitrate='320k')
# With codec parameters
audio.export('song.ogg', format='ogg', codec='libvorbis', bitrate='192k')
# Audio info
print(f"Duration: {len(audio) / 1000:.2f} seconds")
print(f"Channels: {audio.channels}")
print(f"Sample rate: {audio.frame_rate} Hz")
print(f"Bit depth: {audio.sample_width * 8} bits")
Change sample rate and channels
from pydub import AudioSegment
audio = AudioSegment.from_file('audio.wav')
# Resample
audio_48k = audio.set_frame_rate(48000)
audio_44k = audio.set_frame_rate(44100)
audio_22k = audio.set_frame_rate(22050)
audio_44k.export('audio_44100.wav', format='wav')
# Convert to mono
audio_mono = audio.set_channels(1)
audio_mono.export('audio_mono.mp3', format='mp3', bitrate='128k')
# Convert to stereo
audio_stereo = audio_mono.set_channels(2)
# Change bit depth
audio_16bit = audio.set_sample_width(2) # 16-bit
audio_24bit = audio.set_sample_width(3) # 24-bit
Trim and join segments
from pydub import AudioSegment
audio = AudioSegment.from_file('podcast.mp3')
# Trim (times in milliseconds)
intro = audio[:5000] # first 5 seconds
ending = audio[-10000:] # last 10 seconds
segment = audio[30000:90000] # seconds 30 to 90
segment.export('segment.mp3', format='mp3', bitrate='192k')
# Join multiple files
part1 = AudioSegment.from_file('part1.mp3')
part2 = AudioSegment.from_file('part2.mp3')
part3 = AudioSegment.from_file('part3.mp3')
combined = part1 + part2 + part3
combined.export('combined.mp3', format='mp3', bitrate='192k')
# Insert silence between parts
silence_1s = AudioSegment.silent(duration=1000) # 1 second
with_pauses = part1 + silence_1s + part2 + silence_1s + part3
with_pauses.export('with_pauses.mp3', format='mp3')
Normalization and volume control
from pydub import AudioSegment
from pydub.effects import normalize
audio = AudioSegment.from_file('recording.wav')
# Normalize (adjust to maximum without clipping)
audio_norm = normalize(audio)
audio_norm.export('recording_norm.wav', format='wav')
# Raise/lower volume (in dB)
louder = audio + 6 # +6 dB (double amplitude)
quieter = audio - 10 # -10 dB
# Target a specific dBFS level
def adjust_to_dbfs(audio, target_dbfs=-14.0):
"""Adjust volume to target dBFS level."""
diff = target_dbfs - audio.dBFS
return audio + diff
adjusted = adjust_to_dbfs(audio, -14.0) # Streaming standard (Spotify, YouTube)
adjusted.export('normalized.mp3', format='mp3', bitrate='256k')
print(f"Original level: {audio.dBFS:.1f} dBFS")
print(f"Adjusted level: {adjusted.dBFS:.1f} dBFS")
Fade in and fade out
from pydub import AudioSegment
audio = AudioSegment.from_file('song.mp3')
# Fade in at the start (2 seconds)
with_fade_in = audio.fade_in(2000)
# Fade out at the end (3 seconds)
with_fade_out = audio.fade_out(3000)
# Both
with_fades = audio.fade_in(2000).fade_out(3000)
with_fades.export('song_with_fades.mp3', format='mp3', bitrate='320k')
# Crossfade between two songs (1 second overlap)
song1 = AudioSegment.from_file('song1.mp3')
song2 = AudioSegment.from_file('song2.mp3')
mixed = song1.append(song2, crossfade=1000)
mixed.export('mixed.mp3', format='mp3')
Batch conversion
from pydub import AudioSegment
from pathlib import Path
import os
def convert_directory(directory, output_format='mp3', bitrate='192k'):
"""Convert all audio files in a folder to the specified format."""
source = Path(directory)
audio_exts = {'.mp3', '.wav', '.flac', '.ogg', '.aac', '.m4a', '.wma', '.opus'}
results = {'ok': 0, 'error': 0}
for file in sorted(source.iterdir()):
if file.suffix.lower() not in audio_exts:
continue
dest = file.with_suffix(f'.{output_format}')
if dest == file:
continue # already the target format
try:
audio = AudioSegment.from_file(file)
kwargs = {'format': output_format}
if output_format in ('mp3', 'ogg', 'aac'):
kwargs['bitrate'] = bitrate
audio.export(dest, **kwargs)
orig_kb = os.path.getsize(file) // 1024
dest_kb = os.path.getsize(dest) // 1024
print(f"OK: {file.name} → {dest.name} ({orig_kb}KB → {dest_kb}KB)")
results['ok'] += 1
except Exception as e:
print(f"ERROR: {file.name}: {e}")
results['error'] += 1
print(f"\nTotal: {results['ok']} converted, {results['error']} errors")
convert_directory('music/', output_format='mp3', bitrate='192k')
Split on silence
from pydub import AudioSegment
from pydub.silence import split_on_silence, detect_silence
from pathlib import Path
audio = AudioSegment.from_file('long_interview.mp3')
# Detect silence regions
silences = detect_silence(audio, min_silence_len=1000, silence_thresh=-40)
print(f"Silence regions: {len(silences)}")
# Split into segments
segments = split_on_silence(
audio,
min_silence_len=1000, # minimum 1 second of silence
silence_thresh=-40, # silence threshold in dBFS
keep_silence=200, # keep 200ms of silence at each boundary
)
print(f"Segments generated: {len(segments)}")
Path('segments/').mkdir(exist_ok=True)
for i, seg in enumerate(segments, 1):
seg.export(f'segments/segment_{i:03d}.mp3', format='mp3', bitrate='192k')
print(f" Segment {i}: {len(seg)/1000:.1f}s")
Mix tracks (overlay)
from pydub import AudioSegment
voice = AudioSegment.from_file('voice.wav')
music = AudioSegment.from_file('background.mp3')
# Lower background music by -15 dB
music_low = music - 15
# Loop music if shorter than voice
while len(music_low) < len(voice):
music_low = music_low + music_low
# Trim music to voice length
music_low = music_low[:len(voice)]
# Mix (overlay)
mixed = voice.overlay(music_low)
mixed.export('podcast_with_music.mp3', format='mp3', bitrate='256k')
Related conversions
Audio format pairs that come up most often: