把 300 首现场录音的调性写成可检索的文本
上周整理一块 2T 的移动硬盘,里面是我从 2016 年攒到现在的现场录音,大部分是 audience 录音,格式乱:有 16bit/44.1k 的 wav,有从视频里扒出来的 m4a,也有几首是别人转手给我的 mp3 320。想按调性分组做歌单,第一反应是拿现成工具批量分析,结果在 300 首这个量级上就卡住了:多数在线分析服务一次只能传一首,本地能跑的要么依赖一堆 Python 库,要么对 m4a 直接报错。
我的做法很土,分两步。第一步统一转成 wav,顺手去掉视频容器带来的偏移问题:
for f in *.m4a *.mp3; do
ffmpeg -i "$f" -vn -ac 1 -ar 22050 "${f%.*}.wav"
done
单声道 22050 对调性判断够用,别拿它做别的。第二步用一个很小的 Python 脚本读 wav 的过零率和自相关,粗估基频,再按十二平均律映射到音名。核心就这几行:
import wave, numpy as np
w = wave.open(path)
sr = w.getframerate()
x = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(float)
x = x[:sr*20]
xc = np.correlate(x, x, 'full')[len(x)-1:]
lag = np.argmax(xc[50:1000]) + 50
f0 = sr / lag
跑完把每首的估算音名、文件名、时长写进一份 TSV,再用 grep 和 sort 翻。300 首里大约七成能给出稳定的主音,剩下三成是鼓多、人声飘或者调性本身模糊的曲子。
为什么这样做:调性分析本质是统计问题,不是精确测量。整首歌里转调、离调、无调性段落都会污染结果,所以我只取前 20 秒,并且接受它只是个“参考标签”。代价是它会把一首 D 小调的歌标成 F 大调——关系大小调在音高集合上本来就分不开,这不算 bug。
哪里会失效:清唱、打击乐主导、现场观众声比乐器大的段落,基本不用信;有移调夹的吉他现场,估出来的调可能整体偏;文件名里带“encore”的往往前半段是掌声,取前 20 秒等于白跑。我自己踩的坑是拿它去分一首 12 分钟的后摇,结果被标成三个不同的调,因为前 20 秒只是前奏。
现在这份 TSV 就放在歌单目录里,想找“降 B 的慢歌”直接 grep。你们现场录音多的人,是按调性、按 BPM 还是干脆按年份分?我有点想再加一列 BPM,但还没想好值不值得。


0 条评论
还没有评论,写下第一条。