How to Cut and Trim MP3 Files in Python
The pydub library provides an intuitive interface for audio manipulation, treating audio files like Python sequences. It wraps FFmpeg to handle various audio formats including MP3, WAV, and FLAC.
Installation and Setup
Install pydub from PyPI:
pip install pydub
You must also have FFmpeg installed on your system:
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS with Homebrew
brew install ffmpeg
# Windows - download from ffmpeg.org and add to PATH
Without FFmpeg installed and accessible in your system PATH, pydub will raise an error when processing MP3 files. WAV files work without FFmpeg but with limited functionality.
Basic Trimming Operations
Pydub uses milliseconds for all time values. Audio segments support Python slice notation:
from pydub import AudioSegment
# Load the audio file
song = AudioSegment.from_mp3("input.mp3")
# Get duration in milliseconds
duration_ms = len(song)
print(f"Duration: {duration_ms / 1000:.2f} seconds")
# Extract first 10 seconds
first_10_sec = song[:10000]
# Extract last 5 seconds
last_5_sec = song[-5000:]
# Remove first 3 seconds
without_intro = song[3000:]
# Export the trimmed audio
first_10_sec.export("trimmed.mp3", format="mp3")
Extracting a Middle Section
Use slice notation with start and end positions:
from pydub import AudioSegment
song = AudioSegment.from_mp3("podcast.mp3")
# Extract from 30 seconds to 60 seconds
start_ms = 30 * 1000
end_ms = 60 * 1000
clip = song[start_ms:end_ms]
clip.export("clip.mp3", format="mp3")
Helper Function for Time Conversion
Working with minutes and seconds is more intuitive than raw milliseconds:
from pydub import AudioSegment
def time_to_ms(minutes=0, seconds=0, milliseconds=0):
"""Convert time to milliseconds."""
return (minutes * 60 * 1000) + (seconds * 1000) + milliseconds
def ms_to_time(ms):
"""Convert milliseconds to (minutes, seconds, milliseconds)."""
minutes = ms // 60000
seconds = (ms % 60000) // 1000
remaining_ms = ms % 1000
return minutes, seconds, remaining_ms
def extract_segment(audio_path, start_time, end_time, output_path):
"""
Extract a segment from an audio file.
Times can be tuples of (minutes, seconds) or milliseconds.
"""
song = AudioSegment.from_file(audio_path)
if isinstance(start_time, tuple):
start_ms = time_to_ms(minutes=start_time[0], seconds=start_time[1])
else:
start_ms = start_time
if isinstance(end_time, tuple):
end_ms = time_to_ms(minutes=end_time[0], seconds=end_time[1])
else:
end_ms = end_time
clip = song[start_ms:end_ms]
clip.export(output_path, format="mp3")
return len(clip)
# Extract from 1:30 to 2:45
duration = extract_segment(
"song.mp3",
start_time=(1, 30),
end_time=(2, 45),
output_path="excerpt.mp3"
)
print(f"Extracted {duration / 1000:.2f} seconds")
Adding Fade Effects
Smooth transitions with fade in and fade out:
from pydub import AudioSegment
song = AudioSegment.from_mp3("track.mp3")
# Extract a clip
clip = song[30000:60000]
# Add 2-second fade in and 3-second fade out
clip_with_fades = clip.fade_in(2000).fade_out(3000)
clip_with_fades.export("clip_faded.mp3", format="mp3")
Fade effects help avoid abrupt starts and endings when extracting clips from longer recordings. A fade duration of 500-2000ms typically sounds natural.
Concatenating Audio Segments
Combine multiple clips into a single file:
from pydub import AudioSegment
song = AudioSegment.from_mp3("full_song.mp3")
# Extract multiple segments
intro = song[:15000]
chorus = song[60000:90000]
outro = song[-20000:]
# Concatenate with the + operator
compilation = intro + chorus + outro
# Or use reduce for multiple segments
from functools import reduce
segments = [intro, chorus, outro]
compilation = reduce(lambda a, b: a + b, segments)
compilation.export("highlights.mp3", format="mp3")
Removing a Section from Audio
Remove unwanted portions like advertisements:
from pydub import AudioSegment
podcast = AudioSegment.from_mp3("podcast.mp3")
# Remove section from 5:00 to 6:00 (ad break)
ad_start = 5 * 60 * 1000 # 5 minutes
ad_end = 6 * 60 * 1000 # 6 minutes
before_ad = podcast[:ad_start]
after_ad = podcast[ad_end:]
clean_podcast = before_ad + after_ad
clean_podcast.export("podcast_clean.mp3", format="mp3")
Splitting Audio into Equal Parts
Divide a file into multiple segments:
from pydub import AudioSegment
import math
def split_audio(audio_path, segment_length_sec, output_prefix):
"""Split audio into segments of specified length."""
audio = AudioSegment.from_file(audio_path)
segment_length_ms = segment_length_sec * 1000
total_segments = math.ceil(len(audio) / segment_length_ms)
segments = []
for i in range(total_segments):
start = i * segment_length_ms
end = min((i + 1) * segment_length_ms, len(audio))
segment = audio[start:end]
output_path = f"{output_prefix}_part{i + 1:03d}.mp3"
segment.export(output_path, format="mp3")
segments.append(output_path)
print(f"Created: {output_path} ({len(segment) / 1000:.2f}s)")
return segments
# Split into 5-minute segments
parts = split_audio("long_recording.mp3", 300, "recording")
Adjusting Export Quality
Control bitrate and other export parameters:
from pydub import AudioSegment
song = AudioSegment.from_mp3("input.mp3")
clip = song[:30000]
# High quality export
clip.export(
"high_quality.mp3",
format="mp3",
bitrate="320k"
)
# Lower quality for smaller file size
clip.export(
"compressed.mp3",
format="mp3",
bitrate="128k"
)
# Export as WAV (lossless)
clip.export("lossless.wav", format="wav")
# Export with metadata
clip.export(
"with_tags.mp3",
format="mp3",
bitrate="256k",
tags={
"artist": "Artist Name",
"title": "Track Title",
"album": "Album Name"
}
)
Error Handling
Robust implementation with proper error handling:
from pydub import AudioSegment
from pydub.exceptions import CouldntDecodeError
from pathlib import Path
def safe_trim_audio(input_path, start_ms, end_ms, output_path):
"""Safely trim audio with error handling."""
input_file = Path(input_path)
if not input_file.exists():
raise FileNotFoundError(f"Input file not found: {input_path}")
try:
audio = AudioSegment.from_file(input_path)
except CouldntDecodeError as e:
raise ValueError(f"Could not decode audio file: {e}")
duration = len(audio)
# Validate time range
if start_ms < 0:
start_ms = 0
if end_ms > duration:
end_ms = duration
if start_ms >= end_ms:
raise ValueError(f"Invalid time range: {start_ms}ms to {end_ms}ms")
clip = audio[start_ms:end_ms]
# Determine format from output extension
output_format = Path(output_path).suffix.lstrip('.') or 'mp3'
clip.export(output_path, format=output_format)
return {
"duration_ms": len(clip),
"output_path": output_path,
"format": output_format
}
# Usage
try:
result = safe_trim_audio("song.mp3", 10000, 40000, "clip.mp3")
print(f"Created {result['duration_ms'] / 1000:.2f}s clip")
except (FileNotFoundError, ValueError) as e:
print(f"Error: {e}")
Batch Processing Multiple Files
Process multiple audio files with the same trimming:
from pydub import AudioSegment
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def trim_file(input_path, output_dir, start_ms, end_ms):
"""Trim a single file and save to output directory."""
try:
audio = AudioSegment.from_file(input_path)
clip = audio[start_ms:end_ms]
output_path = Path(output_dir) / f"trimmed_{Path(input_path).name}"
clip.export(str(output_path), format="mp3")
return str(output_path), None
except Exception as e:
return None, str(e)
def batch_trim(input_files, output_dir, start_ms, end_ms):
"""Trim multiple files in parallel."""
Path(output_dir).mkdir(exist_ok=True)
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [
executor.submit(trim_file, f, output_dir, start_ms, end_ms)
for f in input_files
]
results = []
for future, input_file in zip(futures, input_files):
output, error = future.result()
results.append({
"input": input_file,
"output": output,
"error": error
})
return results
# Process all MP3s in a directory
input_files = list(Path("music").glob("*.mp3"))
results = batch_trim(input_files, "trimmed", 0, 30000)
for r in results:
if r["error"]:
print(f"Failed: {r['input']} - {r['error']}")
else:
print(f"Success: {r['output']}")
Slice Syntax Reference
| Goal | Syntax | Example |
|---|---|---|
| First N seconds | audio[:N*1000] | song[:10000] |
| Last N seconds | audio[-N*1000:] | song[-5000:] |
| Skip first N seconds | audio[N*1000:] | song[3000:] |
| Middle section | audio[start:end] | song[30000:60000] |
| Every other second | audio[::2000] | Creates choppy effect |
Pydub loads the entire audio file into memory. For very large files (hours of audio), consider using FFmpeg directly or processing in chunks.
Pydub's slice-based approach makes audio editing intuitive for Python developers. Combined with FFmpeg's format support, it handles most common audio manipulation tasks efficiently.