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

2024-09-24 04:29:11 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我希望音调以对数方式变化,从A10(28160赫兹)到A0(27.5赫兹)结束。每过一秒,音高应平稳下降一个倍频程。在

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

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)

Tags: ofsampleimportnumberoutputgetratetime
2条回答

28160Hz的频率对于48000的采样率来说太高了。在

当采样频率为3000hz时,最大频率小于1.5KHz

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

请看:

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

0https://dsp.stackexchange.com/

给定48000Hz的采样率,您可以采样的最大频率是24000hz。这个最大频率是理想化的,它会少得多。在

要捕获28160Hz的频率,您需要大于56320Hz的采样率。比如64000Hz,或者更好 96000Hz采样率。在

编辑:顺便问一下,为什么频率函数会上升到时间的幂次?**

这会引起一些奇怪的锯齿效应

我认为应该是:

^{pr2}$

在 我懂了。。。你在做频率扫描。从而调整每个采样时间的频率。在

有两个问题。在

混叠

fS速率采样的信号,只有在不包含频率高于fS/2的分量时,才能正确重建。当从样本重构信号时(例如,通过声卡),任何频率在区间[0,fS/2]的信号分量都会被折叠到该区间。在

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

在您的例子中,如果您想要采样频率为28160赫兹的正弦波,采样率必须至少为56320赫兹。在

相位计算错误

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

phase是sin函数的参数。它对时间的导数是instantaneous frequency,这是我们听到的音调的音调。在

在本例中,如果我们将frequency = a10 / 2.0 ** time插入pi2 * frequency * time,则阶段是

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

或者用符号表示法:

φ=2π·A10·2t·t

那么derivative of this

f=2π·A10·2-t·(1−ln 2·t

而不是2π·A10·2t。在

这是使用您的方法得到的实际频率扫描图(考虑到混叠,请注意曲线在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)
    

相关问题 更多 >