本文目录导读:

- 方法1:使用 Python + Librosa + Matplotlib
- 方法2:使用 SoX 命令行工具
- 方法3:使用 FFmpeg + Python
- 方法4:完整的高级版本(带参数控制)
- 使用方法
- 推荐的目录结构
方法1:使用 Python + Librosa + Matplotlib
import os
import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
def create_spectrogram(audio_path, output_path, sr=22050):
"""
生成单个音频文件的频谱图
"""
# 加载音频文件
y, sr = librosa.load(audio_path, sr=sr)
# 生成频谱图
plt.figure(figsize=(12, 8))
# 方法1:Mel频谱图
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='hz')
plt.colorbar(format='%+2.0f dB')
# 方法2:或者使用梅尔频谱
# mel_spec = librosa.feature.melspectrogram(y=y, sr=sr)
# mel_db = librosa.amplitude_to_db(mel_spec, ref=np.max)
# librosa.display.specshow(mel_db, sr=sr, x_axis='time', y_axis='mel')
plt.title(f'Spectrogram - {Path(audio_path).stem}')
plt.tight_layout()
# 保存图片
plt.savefig(output_path, dpi=150, bbox_inches='tight')
plt.close()
def batch_process(input_dir, output_dir, file_extensions=['.mp3', '.wav', '.flac']):
"""
批量处理音频文件
"""
# 创建输出目录
Path(output_dir).mkdir(parents=True, exist_ok=True)
# 遍历输入目录
audio_files = []
for ext in file_extensions:
audio_files.extend(list(Path(input_dir).glob(f'**/*{ext}')))
print(f"找到 {len(audio_files)} 个音频文件")
# 批量处理
for i, audio_path in enumerate(audio_files, 1):
try:
output_path = Path(output_dir) / f"{audio_path.stem}_spectrogram.png"
create_spectrogram(str(audio_path), str(output_path))
print(f"[{i}/{len(audio_files)}] 处理完成: {audio_path.name}")
except Exception as e:
print(f"[{i}/{len(audio_files)}] 处理失败: {audio_path.name} - {e}")
# 使用示例
if __name__ == "__main__":
input_directory = "input_audio" # 输入音频目录
output_directory = "output_spectrograms" # 输出图片目录
batch_process(input_directory, output_directory)
方法2:使用 SoX 命令行工具
#!/bin/bash
# batch_spectrogram.sh
INPUT_DIR="input_audio"
OUTPUT_DIR="output_spectrograms"
mkdir -p "$OUTPUT_DIR"
# 批量处理所有.wav文件
for file in "$INPUT_DIR"/*.wav; do
if [ -f "$file" ]; then
filename=$(basename "$file" .wav)
echo "处理文件: $filename"
sox "$file" -n spectrogram -o "$OUTPUT_DIR/${filename}_spectrogram.png"
fi
done
# 处理其他格式(需要先转换)
for file in "$INPUT_DIR"/*.mp3; do
if [ -f "$file" ]; then
filename=$(basename "$file" .mp3)
echo "处理文件: $filename"
# 先转换为WAV,然后生成频谱
sox "$file" -t wav - | sox - -n spectrogram -o "$OUTPUT_DIR/${filename}_spectrogram.png"
fi
done
echo "批量处理完成!"
方法3:使用 FFmpeg + Python
import subprocess
import os
from pathlib import Path
def generate_spectrogram_ffmpeg(input_file, output_file, width=1920, height=1080):
"""
使用FFmpeg生成频谱图
"""
cmd = [
'ffmpeg',
'-i', input_file,
'-lavfi', f'spectrogram=s={width}x{height}:mode=combined:color=rainbow',
'-frames:v', '1',
output_file,
'-y' # 覆盖已存在的文件
]
try:
subprocess.run(cmd, check=True, capture_output=True)
return True
except subprocess.CalledProcessError as e:
print(f"FFmpeg处理失败: {e.stderr.decode()}")
return False
def batch_process_ffmpeg(input_dir, output_dir):
"""
使用FFmpeg批量处理
"""
Path(output_dir).mkdir(parents=True, exist_ok=True)
audio_formats = ['.mp3', '.wav', '.flac', '.m4a', '.ogg']
files_processed = 0
for ext in audio_formats:
for audio_file in Path(input_dir).glob(f'*{ext}'):
output_file = Path(output_dir) / f"{audio_file.stem}_spectrogram.png"
if generate_spectrogram_ffmpeg(str(audio_file), str(output_file)):
files_processed += 1
print(f"处理完成: {audio_file.name}")
else:
print(f"处理失败: {audio_file.name}")
return files_processed
# 使用示例
if __name__ == "__main__":
input_dir = "input_audio"
output_dir = "output_spectrograms"
count = batch_process_ffmpeg(input_dir, output_dir)
print(f"成功处理 {count} 个文件")
方法4:完整的高级版本(带参数控制)
import os
import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
import argparse
from concurrent.futures import ThreadPoolExecutor
class SpectrogramGenerator:
def __init__(self, input_dir, output_dir,
spectrogram_type='mel', # 'mel', 'stft', 'chroma'
size=(12, 8), dpi=150,
colormap='viridis', max_workers=4):
self.input_dir = Path(input_dir)
self.output_dir = Path(output_dir)
self.spectrogram_type = spectrogram_type
self.size = size
self.dpi = dpi
self.colormap = colormap
self.max_workers = max_workers
# 确保输出目录存在
self.output_dir.mkdir(parents=True, exist_ok=True)
def generate_spectrogram(self, audio_path):
"""生成单个频谱图"""
try:
y, sr = librosa.load(str(audio_path), sr=None)
plt.figure(figsize=self.size)
if self.spectrogram_type == 'mel':
# 梅尔频谱
mel_spec = librosa.feature.melspectrogram(y=y, sr=sr)
mel_db = librosa.amplitude_to_db(mel_spec, ref=np.max)
librosa.display.specshow(mel_db, sr=sr, x_axis='time',
y_axis='mel', cmap=self.colormap)
elif self.spectrogram_type == 'stft':
# STFT频谱
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
librosa.display.specshow(D, sr=sr, x_axis='time',
y_axis='hz', cmap=self.colormap)
elif self.spectrogram_type == 'chroma':
# 色度频谱
chroma = librosa.feature.chroma_stft(y=y, sr=sr)
librosa.display.specshow(chroma, sr=sr, x_axis='time',
y_axis='chroma', cmap=self.colormap)
plt.colorbar(format='%+2.0f dB')
plt.title(f'{self.spectrogram_type.upper()} Spectrogram - {audio_path.stem}')
plt.tight_layout()
output_path = self.output_dir / f"{audio_path.stem}_{self.spectrogram_type}_spectrogram.png"
plt.savefig(str(output_path), dpi=self.dpi, bbox_inches='tight')
plt.close()
return True, audio_path.name
except Exception as e:
return False, f"{audio_path.name}: {str(e)}"
def batch_process(self):
"""批量处理所有音频文件"""
audio_extensions = ['.mp3', '.wav', '.flac', '.m4a', '.ogg', '.wma']
audio_files = []
for ext in audio_extensions:
audio_files.extend(list(self.input_dir.glob(f'*{ext}')))
print(f"找到 {len(audio_files)} 个音频文件")
# 使用线程池并行处理
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
results = list(executor.map(self.generate_spectrogram, audio_files))
# 统计结果
success = sum(1 for r in results if r[0])
failed = len(results) - success
print(f"处理完成:成功 {success} 个,失败 {failed} 个")
# 显示失败的文件
if failed > 0:
print("失败的文件:")
for result in results:
if not result[0]:
print(f" - {result[1]}")
def main():
parser = argparse.ArgumentParser(description='批量生成音频频谱图')
parser.add_argument('input_dir', help='输入音频目录')
parser.add_argument('output_dir', help='输出图片目录')
parser.add_argument('--type', choices=['mel', 'stft', 'chroma'],
default='mel', help='频谱图类型')
parser.add_argument('--width', type=int, default=12, help='图片宽度(英寸)')
parser.add_argument('--height', type=int, default=8, help='图片高度(英寸)')
parser.add_argument('--dpi', type=int, default=150, help='图片分辨率')
parser.add_argument('--colormap', default='viridis', help='颜色映射')
parser.add_argument('--workers', type=int, default=4, help='并行处理线程数')
args = parser.parse_args()
generator = SpectrogramGenerator(
input_dir=args.input_dir,
output_dir=args.output_dir,
spectrogram_type=args.type,
size=(args.width, args.height),
dpi=args.dpi,
colormap=args.colormap,
max_workers=args.workers
)
generator.batch_process()
if __name__ == "__main__":
main()
使用方法
安装依赖
# 安装Python依赖 pip install librosa matplotlib numpy # 或者安装FFmpeg # Ubuntu/Debian sudo apt-get install ffmpeg # macOS brew install ffmpeg # Windows # 下载FFmpeg并添加到PATH
运行脚本
# 使用高级版(推荐) python spectrogram_generator.py input_audio output_spectrograms --type mel --workers 8 # 使用基本版 python batch_spectrogram.py
推荐的目录结构
project/
├── input_audio/ # 存放音频文件
│ ├── song1.mp3
│ ├── song2.wav
│ └── song3.flac
├── output_spectrograms/ # 输出频谱图
│ ├── song1_mel_spectrogram.png
│ ├── song2_mel_spectrogram.png
│ └── song3_mel_spectrogram.png
├── batch_spectrogram.py # 脚本文件
└── requirements.txt # 依赖文件
这些脚本可以根据需求进行调整,比如修改频谱图样式、输出格式、并行处理数量等。