本文目录导读:

使用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格式文件
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播