本文目录导读:

我来介绍几种批量检测音乐调性的方法,从最简单到最专业。
使用Python + librosa库(最推荐)
import librosa
import numpy as np
import os
from pathlib import Path
def detect_key(audio_path):
"""检测单个音频文件的调性"""
try:
# 加载音频文件
y, sr = librosa.load(audio_path, sr=22050)
# 计算色谱图
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)
# 使用Krumhansl-Schmuckler算法检测调性
key_temp = librosa.feature.tonnetz(y=librosa.effects.harmonic(y), sr=sr)
# 计算每个调性的分数
key_dict = {
'C': 0, 'C#': 1, 'D': 2, 'D#': 3, 'E': 4, 'F': 5,
'F#': 6, 'G': 7, 'G#': 8, 'A': 9, 'A#': 10, 'B': 11
}
# 使用模板匹配
chroma_mean = np.mean(chroma, axis=1)
# 大调模板
major_template = np.array([1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1])
# 小调模板
minor_template = np.array([1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0])
best_key = None
best_score = -1
for i in range(12):
# 循环移位模板
rotated_major = np.roll(major_template, i)
rotated_minor = np.roll(minor_template, i)
# 计算相关性
major_score = np.corrcoef(chroma_mean, rotated_major)[0, 1]
minor_score = np.corrcoef(chroma_mean, rotated_minor)[0, 1]
if major_score > best_score:
best_score = major_score
key_name = list(key_dict.keys())[i]
best_key = f"{key_name} Major"
if minor_score > best_score:
best_score = minor_score
key_name = list(key_dict.keys())[i]
best_key = f"{key_name} Minor"
return best_key, best_score
except Exception as e:
return f"Error: {str(e)}", 0
def batch_detect_keys(folder_path):
"""批量检测文件夹中所有音频文件的调性"""
results = []
# 支持的音频格式
audio_formats = ['.mp3', '.wav', '.flac', '.m4a', '.ogg']
# 遍历文件夹
for file_path in Path(folder_path).rglob('*'):
if file_path.suffix.lower() in audio_formats:
print(f"检测中: {file_path.name}")
key, confidence = detect_key(str(file_path))
results.append({
'filename': file_path.name,
'key': key,
'confidence': confidence,
'path': str(file_path)
})
return results
# 使用方法
if __name__ == "__main__":
import pandas as pd
# 设置音频文件夹路径
audio_folder = "your_audio_folder_path" # 修改为你的文件夹路径
results = batch_detect_keys(audio_folder)
# 保存为CSV文件
df = pd.DataFrame(results)
df.to_csv("music_key_detection_results.csv", index=False, encoding='utf-8')
# 打印结果
print("\n检测结果:")
print(df.to_string())
使用Essentia库(更专业)
# 安装essentia pip install essentia-tensorflow
import essentia.standard as es
import os
import pandas as pd
from pathlib import Path
def detect_key_essentia(audio_path):
"""使用Essentia检测调性"""
try:
# 加载音频
loader = es.MonoLoader(filename=audio_path)
audio = loader()
# 检测调性
key_extractor = es.KeyExtractor()
key, scale, strength = key_extractor(audio)
return f"{key} {scale}", strength
except Exception as e:
return f"Error: {str(e)}", 0
def batch_detect_keys_essentia(folder_path):
results = []
audio_formats = ['.mp3', '.wav', '.flac', '.m4a', '.ogg']
for file_path in Path(folder_path).rglob('*'):
if file_path.suffix.lower() in audio_formats:
print(f"检测中: {file_path.name}")
key, confidence = detect_key_essentia(str(file_path))
results.append({
'filename': file_path.name,
'key': key,
'confidence': confidence
})
return results
# 使用方法
results = batch_detect_keys_essentia("your_audio_folder")
df = pd.DataFrame(results)
df.to_csv("key_detection_results.csv", index=False)
命令行工具MIR-QBSH
# 安装 pip install mir-eval # 或者下载工具: https://github.com/rabitt/qbsh
使用音乐分析软件API
Sonic Visualiser + VAMP插件
# 命令行批量处理 sonic-annotator -t key_detection.n3 -w csv *.wav
AcousticBrainz API
import requests
import json
import os
def detect_key_api(audio_path):
"""使用AcousticBrainz API(需要上传音频)"""
url = "https://acousticbrainz.org/api/v1/high-level"
with open(audio_path, 'rb') as f:
files = {'audio': f}
response = requests.post(url, files=files)
if response.status_code == 200:
data = response.json()
if 'tonal' in data and 'key_key' in data['tonal']:
return data['tonal']['key_key'], data['tonal'].get('key_scale', '')
return None, None
完整的批处理脚本(带GUI)
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import pandas as pd
from pathlib import Path
import threading
import time
class KeyDetectorGUI:
def __init__(self):
self.root = tk.Tk()
self.root.title("批量音乐调性检测工具")
self.root.geometry("600x500")
# 创建UI元素
self.create_widgets()
def create_widgets(self):
# 文件夹选择
tk.Label(self.root, text="选择音频文件夹:").pack(pady=10)
self.folder_path = tk.StringVar()
entry = tk.Entry(self.root, textvariable=self.folder_path, width=50)
entry.pack(pady=5)
tk.Button(self.root, text="浏览文件夹", command=self.select_folder).pack(pady=5)
# 开始按钮
self.start_button = tk.Button(self.root, text="开始检测",
command=self.start_detection,
bg="#4CAF50", fg="white")
self.start_button.pack(pady=20)
# 进度显示
self.progress_label = tk.Label(self.root, text="就绪")
self.progress_label.pack(pady=10)
# 结果文本框
self.result_text = tk.Text(self.root, height=15, width=70)
self.result_text.pack(pady=10)
# 滚动条
scrollbar = tk.Scrollbar(self.result_text)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.result_text.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.result_text.yview)
def select_folder(self):
folder = filedialog.askdirectory()
if folder:
self.folder_path.set(folder)
def detect_key_simple(self, audio_path):
"""简单的调性检测(基于频谱分析)"""
try:
import librosa
y, sr = librosa.load(audio_path, sr=22050, duration=30)
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)
chroma_mean = np.mean(chroma, axis=1)
# 简单模板匹配
major_profile = np.array([6.35, 2.23, 3.48, 2.33, 4.38, 4.09,
2.52, 5.19, 2.39, 3.66, 2.29, 2.88])
minor_profile = np.array([6.33, 2.68, 3.52, 5.38, 2.60, 3.53,
2.54, 4.75, 3.98, 2.69, 3.34, 3.17])
keys = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
best_corr = -1
best_key = "未知"
for i in range(12):
rotated_major = np.roll(major_profile, i)
rotated_minor = np.roll(minor_profile, i)
corr_major = np.corrcoef(chroma_mean, rotated_major)[0, 1]
corr_minor = np.corrcoef(chroma_mean, rotated_minor)[0, 1]
if corr_major > best_corr:
best_corr = abs(corr_major)
best_key = f"{keys[i]} Major"
if corr_minor > best_corr:
best_corr = abs(corr_minor)
best_key = f"{keys[i]} Minor"
return best_key
except Exception as e:
return f"错误: {str(e)}"
def start_detection(self):
if not self.folder_path.get():
messagebox.showerror("错误", "请先选择文件夹")
return
# 在新线程中运行检测
thread = threading.Thread(target=self.run_detection)
thread.start()
def run_detection(self):
import numpy as np
from scipy import stats
folder = self.folder_path.get()
audio_formats = ['.mp3', '.wav', '.flac', '.m4a']
results = []
files = [f for f in Path(folder).rglob('*')
if f.suffix.lower() in audio_formats]
for i, file_path in enumerate(files):
self.progress_label.config(text=f"检测 {i+1}/{len(files)}: {file_path.name}")
key = self.detect_key_simple(str(file_path))
results.append(f"{file_path.name}: {key}")
self.result_text.insert(tk.END, f"{file_path.name}: {key}\n")
self.result_text.see(tk.END)
self.root.update()
time.sleep(0.1) # 避免界面卡死
self.progress_label.config(text="检测完成!")
messagebox.showinfo("完成", f"共检测 {len(files)} 个音频文件")
# 保存结果
output_file = os.path.join(folder, "调性检测结果.csv")
with open(output_file, 'w', encoding='utf-8') as f:
f.write("文件名,调性\n")
for result in results:
f.write(result.replace(": ", ",") + "\n")
self.result_text.insert(tk.END, f"\n结果已保存到: {output_file}")
# 运行GUI
if __name__ == "__main__":
app = KeyDetectorGUI()
app.root.mainloop()
安装依赖
# 基础依赖 pip install librosa numpy pandas scipy # 可选:更专业的库 pip install essentia-tensorflow pip install mir-eval
使用建议
- 准确度考虑:librosa的调性检测准确度约70-80%,专业软件可达90%以上
- 处理速度:处理一个3分钟的音频文件约需1-3秒
- 批量建议:先测试10-20个文件确认结果准确性
- 输出格式:推荐保存为CSV文件方便后续处理
选择哪种方法取决于你的需求:
- 简单快速:方法1(Python+librosa)
- 专业准确:方法2(Essentia)
- 批量处理:方法5(带GUI的脚本)
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播