我试图通过在两个430Hz和450Hz之间振荡来创建颤音,将16位样本存储在列表wav
中。然而,可听频率似乎增加了整个夹子的振荡范围。有谁知道为什么?
编辑:重写代码更清晰/简洁
# vibrato.py
maxamp = 2**15 - 1 # max signed short
wav = []
(t, dt) = (0, 1 / 44100)
while t < 6.0:
f = 440 + 10 * math.sin(2 * math.pi * 6 * t)
samp = maxamp * math.sin(2 * math.pi * f * t)
wav.append(samp)
t += dt
-
更新:因为响应使用numpy,我将更新我的代码for plain python3
# vibrato.py
maxamp = 2**15 - 1 # max signed short
wav = []
(t, dt) = (0, 1 / 44100)
phase = 0
while t < 6.0:
f = 440 + 10 * math.sin(2 * math.pi * 6 * t)
phase += 2 * math.pi * f * t
samp = maxamp * math.sin(phase)
wav.append(samp)
t += dt
答案 0 :(得分:3)
问题与隐含的相变有关,随着频率的变化而变化。简而言之,当您计算相对于时间线中每个点的响应时,重要的是要注意每次振荡的相位对于每个频率都是不同的(除了它们的起点之外)。一切都一样)。因此,在频率之间移动就像在不同阶段之间移动。对于在两个不同频率之间移动的情况,可以通过基于频率变化调整整体信号相位来校正 post hoc 。我已经解释了这个in another answer,所以在这里不再解释它,但这里只是展示突出问题的初始情节,以及如何解决问题。在这里,主要的补充是良好的诊断图的重要性,正确的图是谱图。
以下是一个例子:
import numpy as np
dt = 1./44100
time = np.arange(0., 6., dt)
frequency = 440. - 10*np.sin(2*math.pi*time*1.) # a 1Hz oscillation
waveform = np.sin(2*math.pi*time*frequency)
Pxx, freqs, bins, im = plt.specgram(waveform, NFFT=4*1024, Fs=44100, noverlap=90, cmap=plt.cm.gist_heat)
plt.show()
请注意,频率振荡的跨度正在增加(正如您最初听到的那样)。应用上面链接的修正产生:
dt = 1./defaults['framerate']
time = np.arange(0., 6., dt)
frequency = 440. - 10*np.sin(2*math.pi*time*1.) # a 1Hz oscillation
phase_correction = np.add.accumulate(time*np.concatenate((np.zeros(1), 2*np.pi*(frequency[:-1]-frequency[1:]))))
waveform = np.sin(2*math.pi*time*frequency + phase_correction)
我希望,这更接近预期。
另一种概念化的方法,在循环每个时间步骤(如OP所做的)的背景下更有意义,并且更接近物理模型,是在每个步骤跟踪相位并确定考虑前一步骤的幅度和相位,并将这些与新频率相结合的新幅度。我没有耐心让它在纯Python中运行,但是在numpy中,解决方案看起来像这样,并给出了类似的结果:
dt = 1./44100
time = np.arange(0., 6., dt)
f = 440. - 10*np.sin(2*math.pi*time*1.) # a 1Hz oscillation
delta_phase = 2 * math.pi * f * dt
phase = np.cumsum(delta_phase) # add up the phase differences along timeline (same as np.add.accumulate)
wav = np.sin(phase)