本文目录导读:

使用pydub(推荐,支持多种格式)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
批量修改音频声道数脚本
支持格式: mp3, wav, flac, ogg, m4a 等
"""
import os
import argparse
from pydub import AudioSegment
from concurrent.futures import ThreadPoolExecutor
import logging
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 支持的音频格式
SUPPORTED_FORMATS = {'.mp3', '.wav', '.flac', '.ogg', '.m4a', '.wma', '.aac'}
def change_audio_channels(input_path, output_path, channels, sample_rate=None, bitrate=None):
"""
修改单个音频文件的声道数
Args:
input_path: 输入文件路径
output_path: 输出文件路径
channels: 目标声道数 (1=单声道, 2=立体声)
sample_rate: 采样率 (可选)
bitrate: 比特率 (可选)
"""
try:
# 读取音频文件
audio = AudioSegment.from_file(input_path)
# 修改声道数
if channels == 1:
audio = audio.set_channels(1)
logger.info(f"转换为单声道: {input_path}")
elif channels == 2:
audio = audio.set_channels(2)
logger.info(f"转换为立体声: {input_path}")
else:
logger.error(f"不支持的声道数: {channels},仅支持1(单声道)或2(立体声)")
return False
# 修改采样率(如果指定)
if sample_rate:
audio = audio.set_frame_rate(sample_rate)
# 导出文件
export_params = {"format": os.path.splitext(output_path)[1][1:]}
if bitrate:
export_params["bitrate"] = bitrate
audio.export(output_path, **export_params)
logger.info(f"成功导出: {output_path}")
return True
except Exception as e:
logger.error(f"处理文件 {input_path} 时出错: {e}")
return False
def batch_process(input_dir, output_dir, channels, sample_rate=None, bitrate=None,
recursive=False, max_workers=4, overwrite=False, keep_structure=True):
"""
批量处理音频文件
Args:
input_dir: 输入目录
output_dir: 输出目录
channels: 目标声道数
sample_rate: 采样率
bitrate: 比特率
recursive: 是否递归处理子目录
max_workers: 最大线程数
overwrite: 是否覆盖已有文件
keep_structure: 是否保持目录结构
"""
# 创建输出目录
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 收集所有音频文件
audio_files = []
if recursive:
for root, dirs, files in os.walk(input_dir):
for file in files:
if os.path.splitext(file)[1].lower() in SUPPORTED_FORMATS:
audio_files.append(os.path.join(root, file))
else:
for file in os.listdir(input_dir):
if os.path.splitext(file)[1].lower() in SUPPORTED_FORMATS:
audio_files.append(os.path.join(input_dir, file))
if not audio_files:
logger.warning(f"在 {input_dir} 中没有找到支持的音频文件")
return
logger.info(f"找到 {len(audio_files)} 个音频文件")
# 准备处理任务
tasks = []
for input_path in audio_files:
if keep_structure:
# 保持目录结构
rel_path = os.path.relpath(input_path, input_dir)
output_path = os.path.join(output_dir, rel_path)
os.makedirs(os.path.dirname(output_path), exist_ok=True)
else:
# 所有文件放在同一目录
filename = os.path.basename(input_path)
output_path = os.path.join(output_dir, filename)
# 检查文件是否已存在
if os.path.exists(output_path) and not overwrite:
logger.info(f"跳过已存在的文件: {output_path}")
continue
tasks.append((input_path, output_path, channels, sample_rate, bitrate))
# 多线程处理
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(change_audio_channels, *task) for task in tasks]
for future in futures:
try:
future.result()
except Exception as e:
logger.error(f"处理任务时出错: {e}")
def main():
parser = argparse.ArgumentParser(description="批量修改音频声道数")
parser.add_argument("-i", "--input", required=True, help="输入目录或文件")
parser.add_argument("-o", "--output", required=True, help="输出目录")
parser.add_argument("-c", "--channels", type=int, required=True,
choices=[1, 2], help="目标声道数 (1=单声道, 2=立体声)")
parser.add_argument("-sr", "--sample-rate", type=int, help="采样率 (可选)")
parser.add_argument("-br", "--bitrate", type=str, help="比特率 (可选,如 '192k')")
parser.add_argument("-r", "--recursive", action="store_true", help="递归处理子目录")
parser.add_argument("-w", "--workers", type=int, default=4, help="处理线程数 (默认: 4)")
parser.add_argument("--overwrite", action="store_true", help="覆盖已存在的文件")
parser.add_argument("--no-structure", action="store_true",
help="不保持目录结构,所有文件输出到同一目录")
args = parser.parse_args()
# 检查输入是文件还是目录
if os.path.isfile(args.input):
# 处理单个文件
input_format = os.path.splitext(args.input)[1].lower()
if input_format not in SUPPORTED_FORMATS:
logger.error(f"不支持的音频格式: {input_format}")
return
# 创建输出目录
os.makedirs(args.output, exist_ok=True)
# 生成输出文件名
filename = os.path.basename(args.input)
output_path = os.path.join(args.output, filename)
# 处理单个文件
if change_audio_channels(args.input, output_path, args.channels,
args.sample_rate, args.bitrate):
logger.info(f"成功处理文件: {args.input}")
else:
logger.error(f"处理文件失败: {args.input}")
elif os.path.isdir(args.input):
# 批量处理目录
batch_process(
args.input,
args.output,
args.channels,
args.sample_rate,
args.bitrate,
args.recursive,
args.workers,
args.overwrite,
not args.no_structure
)
else:
logger.error(f"输入路径不存在: {args.input}")
if __name__ == "__main__":
main()
安装依赖
# 安装pydub pip install pydub # 安装音频处理后端(至少安装一个) # 方案一:安装ffmpeg # Ubuntu/Debian: sudo apt-get install ffmpeg # macOS: brew install ffmpeg # Windows: 下载ffmpeg并添加到PATH # 方案二:安装libav # Ubuntu/Debian: sudo apt-get install libav-tools
使用方法
# 1. 批量转换目录下所有音频为单声道 python change_channels.py -i ./input_audio -o ./output_audio -c 1 -r # 2. 转换为立体声并指定采样率 python change_channels.py -i ./input -o ./output -c 2 -sr 44100 -r # 3. 转换为单声道并指定比特率 python change_channels.py -i ./input -o ./output -c 1 -br 192k -r # 4. 处理单个文件 python change_channels.py -i song.mp3 -o ./output -c 1 # 5. 使用多线程加速(默认4线程) python change_channels.py -i ./input -o ./output -c 1 -w 8 -r # 6. 覆盖已存在的文件 python change_channels.py -i ./input -o ./output -c 2 -r --overwrite # 7. 不保持目录结构 python change_channels.py -i ./input -o ./output -c 1 -r --no-structure
使用ffmpeg命令行(无需Python)
如果不想安装Python,可以直接使用ffmpeg命令行:
#!/bin/bash
# batch_change_channels.sh
# 批量转换当前目录下所有音频为单声道
for file in *.mp3 *.wav *.flac; do
if [ -f "$file" ]; then
filename="${file%.*}"
ffmpeg -i "$file" -ac 1 "${filename}_mono.${file##*.}"
fi
done
使用sox(更轻量级)
#!/bin/bash
# batch_change_channels_sox.sh
# 批量转换所有wav为单声道
for file in *.wav; do
if [ -f "$file" ]; then
filename="${file%.*}"
sox "$file" -c 1 "${filename}_mono.wav"
fi
done
选择建议:
- 推荐使用Python脚本,功能最全面,支持多种格式
- 如果只需要简单处理,可以使用ffmpeg命令行
- sox适合处理wav格式文件