测量唱歌音符的颤音速率
信息处理
信号分析
声音的
频谱
2022-02-01 18:38:58
2个回答
我使用以下开源工具实现了一个简单的颤音测量算法:
用于音频捕获的sox
用于定期推导基频的aubiopitch
用于编写上述脚本并处理结果的Python
aubioitch 是 aubio 库的一部分,是这里的关键。它接收一个音频文件并输出一个时间和基本频率列表,间隔大约每 6ms(aubiopitch 使用 2048 点FFT,但步进 256 个样本,音频采样率为 44.1kHz)。然后 Python 脚本按如下方式处理:
- 保留两个运行平均值,最后 5 个频率样本中的一个短的,最后 10 个频率样本中的一个长的
- 如果短期平均线大于长期平均线,那么频率正在上升;否则它正在下降
- 注意频率斜率何时改变符号。计算转换次数,跟踪第一次转换和最后一次转换之间的时间
- 将转换次数除以时间,再除以 2(每个周期两次转换),您就得到了以 Hz 为单位的颤音速率。
Python源代码:
import subprocess
import re
import os
TEMP_SOUND = "/tmp/vibrato.delme.wav"
TEMP_DATA = "/tmp/vibrato.delme.txt"
DURATION = 2
VERBOSE = False
print("Recording for {} seconds...".format(DURATION))
subprocess.call(
"/opt/local/bin/sox -d {} trim 0 {} >/dev/null 2>&1".format(TEMP_SOUND, DURATION), shell=True)
print(" ... done. Analyzing...")
subprocess.call(
"/usr/local/bin/aubiopitch {} | tail -n +10 > {}".format(TEMP_SOUND, TEMP_DATA), shell=True)
short_list = []
short_max = 5
long_list = []
long_max = 10
last_slope = None
first_crossing_time = None
time_freq_re = re.compile(r'([^ ]+) ([^ ]+)')
samples = 0
with open(TEMP_DATA, mode="rU") as f:
for line in f:
line = line.strip()
time_freq_match = time_freq_re.match(line)
if not time_freq_match:
print("Whoa!")
exit(1)
time = float(time_freq_match.group(1))
freq = float(time_freq_match.group(2))
short_list.append(freq)
if len(short_list) > short_max:
short_list.pop(0)
long_list.append(freq)
if len(long_list) > long_max:
long_list.pop(0)
short_average = sum(short_list) / short_max
long_average = sum(long_list) / long_max
# If we've collected enough data to analyze
if len(long_list) == long_max:
slope = (short_average > long_average)
# If this is the first time through, remember the slope
if last_slope == None:
last_slope = slope
samples += 1
# Crossing?
if slope != last_slope:
last_slope = slope
# First crossing?
if first_crossing_time == None:
first_crossing_time = time
samples = 0
crossings = 0
else:
crossings += 1
last_crossing_time = time
last_crossing_samples = samples
if VERBOSE:
print("**** crossing ****")
if VERBOSE:
print("short {}, long {}, slope {}".format(short_average, long_average, slope))
total_time = last_crossing_time - first_crossing_time
print("total time {:.4}, samples {}, sample period {:.4} ms, crossings {}, vibrato rate {:.4} Hz".format(
total_time, last_crossing_samples, total_time * 1000 / last_crossing_samples, crossings, crossings / (total_time * 2)))
os.remove(TEMP_SOUND)
os.remove(TEMP_DATA)
示例输出:
DanMBPE:Vibrato griscom$ python ./vibrato.py
Recording for 2 seconds...
... done. Analyzing...
total time 1.84, samples 317, sample period 5.805 ms, crossings 17, vibrato rate 4.619 Hz
DanMBPE:Vibrato griscom$
欢迎提出建议!
其它你可能感兴趣的问题