为什么我的正弦波频率扫描不正确?

时间:2017-07-03 23:52:00

标签: python python-3.x math wav waveform

我正在尝试创建一个发出变化音调的简单WAV文件。但是,写入文件的波形与get_sample返回的数据不对应。

我希望音调以对数方式改变,从A10(28,160 Hz)开始到A0(27.5 Hz)结束。当每一秒通过时,音高应平滑下降一个八度。

实际发生的事情很难解释。语气改变了,但是出于意想不到的方式。而让我的问题更加奇怪的是降低采样率会使问题恶化。在this output中,每秒48,000个样本,音高迅速下降,然后再次上升,再次缓慢下降。在this output每秒3000个样本中,会发生类似的效果,但它更加极端和混乱。我做错了什么?

from math import pi, sin
from sys import byteorder
import wave

def get_sample(time):
    frequency = a10 / 2.0 ** time
    # print('{:.15f} {:.15f} {:.15f}'.format(time, frequency, sin(pi2 * frequency * time)))
    return sin(pi2 * frequency * time)

pi2 = 2 * pi
a10 = 28160.0

NUMBER_OF_CHANNELS = 1
SAMPLE_RATE = 48000  # samples per second
SAMPLE_WIDTH = 3  # bytes
DURATION = 10  # seconds

MAX_SAMPLE_VALUE = 2 ** (SAMPLE_WIDTH * 8 - 1)

samples = bytearray()

for i in range(SAMPLE_RATE * DURATION):
    time = i / SAMPLE_RATE
    sample = round(get_sample(time) * MAX_SAMPLE_VALUE)

    if sample == MAX_SAMPLE_VALUE:
        sample -= 1

    samples.extend(sample.to_bytes(SAMPLE_WIDTH, byteorder, signed=True))

with wave.open('output.wav', 'wb') as output:
    output.setnchannels(NUMBER_OF_CHANNELS)
    output.setsampwidth(SAMPLE_WIDTH)
    output.setframerate(SAMPLE_RATE)
    output.setnframes(NUMBER_OF_CHANNELS * SAMPLE_RATE * DURATION)
    output.setcomptype('NONE', 'not compressed')

    output.writeframes(samples)

2 个答案:

答案 0 :(得分:2)

对于48000的采样率,28,160Hz的频率为高。

采样率为3,000Hz,最大频率小于1.5KHz

这与奈奎斯特采样率有关。简而言之,您可以在给定采样率下采样的最大频率是采样率的1/2。实际上,它的采样率不到1/2。

请参阅:

https://en.wikipedia.org/wiki/Nyquist_frequency

0 https://dsp.stackexchange.com/

考虑到48000Hz的采样率,您可以采样的最大频率为24,000Hz。这个最大频率是IDEALIZED,它会少得多。

要捕获28,160Hz频率,您需要的采样率超过5620Hz。说64,000Hz,或更好 96000Hz采样率。

编辑:BTW 为什么频率功能升至TIME的功率? **

<击>

这会导致一些奇怪的锯齿效果

我认为应该是:

frequency = a10 * time

<击> 我明白了......你正在做一个频率扫描。从而在每个采样时间调整频率。

答案 1 :(得分:1)

有两个问题。

混叠

f S 速率采样的信号只有在不包含高于 f 的频率的分量时才能正确重建小号 / 2。当重建样本信号时,任何频率超出区间[0, f S / 2]的信号分量都会折叠进入该区间(例如,通过你的声卡)。

这称为aliasing,可以通过在采样前对信号进行低通滤波或使采样率足够高来避免。

在您的情况下,如果您想采样频率为28160 Hz的正弦波,采样率必须至少为56320 Hz。

阶段计算错误

def get_sample(time):
    frequency = a10 / 2.0 ** time
    return sin(pi2 * frequency * time)

阶段sin函数的参数。它在时间上的导数是instantaneous frequency,它是我们听到的音调的音高。

在这种情况下,如果我们将frequency = a10 / 2.0 ** time插入pi2 * frequency * time,则阶段为

pi2 * (a10 / 2.0 ** time) * time

或用符号表示法:

φ=2π·A10·2 - t · t

derivative of this

f =2π·A10·2 - t ·(1 - ln 2· t ),

而不是2π·A10·2 - t 正如您所料。

这是使用您的方法获得的实际频率扫描图(考虑混叠,注意曲线在0 Hz和24000 Hz线条处的反射方式),与您的预期相比:

frequency sweep (linear scale)

这是与对数频率刻度相同的图,这是我们将频率视为音高的方式:

frequency sweep (logarithmic scale)

解决方案

通过进行以下更改,您可以获得正确的结果:

  1. SAMPLE_RATE使用足够高的值。

  2. 不要直接从给定时间计算样本,而是保持一个相位值,该相位值以与预期频率成比例的速率递增(将其包裹在2π以使其不会超出界限) ,通过替换

    def get_sample(time):
        frequency = a10 / 2.0 ** time
        return sin(pi2 * frequency * time)
    
    […]
    
    for i in range(SAMPLE_RATE * DURATION):
        time = i / SAMPLE_RATE
        sample = round(get_sample(time) * MAX_SAMPLE_VALUE)
    

    通过

    def get_frequency(time):
        frequency = a10 / 2.0 ** time
        return frequency
    
    […]
    
    phase = 0
    for i in range(SAMPLE_RATE * DURATION):
        time = i / SAMPLE_RATE
        f = get_frequency(time)
        phase = (phase + pi2 * f / SAMPLE_RATE) % pi2
        sample = round(sin(phase) * MAX_SAMPLE_VALUE)