生成颤音正弦波

时间:2015-01-28 05:25:45

标签: python audio python-3.x sin

我试图通过在两个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

1 个答案:

答案 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()

enter image description here

请注意,频率振荡的跨度正在增加(正如您最初听到的那样)。应用上面链接的修正产生:

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)

enter image description here 我希望,这更接近预期。

另一种概念化的方法,在循环每个时间步骤(如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)