简体   繁体   English

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

[英]Why is my sine wave frequency sweep not correct?

I'm trying to create a simple WAV file that emits a changing pitch. 我正在尝试创建一个简单的WAV文件,它会发出变化的音调。 However, the waveform that gets written to the file does not correspond to the data that get_sample returns. 但是,写入文件的波形与get_sample返回的数据不对应。

I expect the tone to change in a logarithmic fashion, starting at A10 (28,160 Hz) and to end at A0 (27.5 Hz). 我期望音调以对数方式改变,从A10(28,160 Hz)开始到A0(27.5 Hz)结束。 As each second passes, the pitch should smoothly drop by one octave. 当每秒通过时,音高应平滑下降一个八度。

What is actually happening is difficult to explain. 实际发生的事情很难解释。 The tone changes, but in unintended ways. 语气改变了,但是出于意想不到的方式。 And what makes my problem more peculiar is that lowering the sample rate worsens the problem. 而让我的问题更加奇怪的是降低采样率会使问题恶化。 In this output at 48,000 samples per second, the pitch rapidly drops and then rises again just to slowly fall once more. 此输出中 ,每秒48,000个样本,音高迅速下降,然后再次上升,再次缓慢下降。 In this output at 3,000 samples per second, a similar effect happens, but it is more extreme and chaotic. 这个每秒3000个样本的输出中,会发生类似的效果,但它更加极端和混乱。 What am I doing wrongly? 我做错了什么?

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)

A Frequency of 28,160Hz is to high for a sample rate of 48000. 对于48000的采样率,28,160Hz的频率为高。

And a sample rate of 3,000Hz , the maximum frequency would be less than 1.5KHz 并且采样率为3,000Hz,最大频率将小于1.5KHz

This is related to the Nyquist sample rate. 这与奈奎斯特采样率有关。 In short the maximum frequency you can sample at a given sample rate is 1/2 the Sample rate. 简而言之,您可以在给定采样率下采样的最大频率是采样率的1/2。 In reality it is less than 1/2 the sample rate. 实际上它小于采样率的1/2。

Please See: 请参见:

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

0 https://dsp.stackexchange.com/ 0 https://dsp.stackexchange.com/

Given a 48000Hz sample rate, the maximum frequency you can sample is 24,000Hz. 如果采样率为48000Hz,则可采样的最大频率为24,000Hz。 This maximum frequency is IDEALIZED , it would be much less. 这个最大频率是IDEALIZED,它会少得多。

To capture 28,160Hz frequency, you will need a sample rate more than 56,320Hz. 要捕获28,160Hz频率,您需要的采样率超过5620Hz。 Say 64,000Hz, or better yet 96000Hz sample rate. 说64,000Hz,或更好的96000Hz采样率。

EDIT: BTW Why is frequency function raising to the power of TIME ? 编辑:BTW 为什么频率功能提升到TIME的功率? ** **

That would cause some weird aliasing effects 这会导致一些奇怪的混叠效果

I believe it should be Should be: 我认为它应该是:

 
 
 
  
  frequency = a10 * time
 
  

I see... you're doing a frequency Sweep. 我明白了......你正在做一个频率扫描。 thus adjusting the Frequency at each sample time. 从而在每个采样时间调整频率。

There are two problems. 有两个问题。

Aliasing 别名

A signal sampled at a rate f S can only be reconstructed correctly if it contains no components at frequencies higher than f S /2. 以速率f S采样的信号只有在其中不包含频率高于f S / 2的分量时才能正确地重建。 Any signal components with frequencies outside the interval [0, f S /2] get folded into that interval when reconstructing the signal from the samples (eg by your soundcard). 当重建来自样本的信号时(例如,通过声卡),任何频率超出区间[0, f S / 2]的信号分量都会折叠到该间隔中。

This is called aliasing and can be avoided by either low-pass filtering a signal before sampling it or by making the sampling rate sufficiently high. 这称为混叠 ,可以通过在采样之前对信号进行低通滤波或通过使采样速率足够高来避免。

In your case, if you want to sample a sine wave with a frequency of 28160 Hz, the sampling rate must be at least 56320 Hz. 在您的情况下,如果要采样频率为28160 Hz的正弦波,采样率必须至少为56320 Hz。

Incorrect computation of phase 相位计算不正确

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

The phase is the argument to the sin function. 阶段sin函数的参数。 Its derivative with respect to time is the instantaneous frequency which is the pitch of the tone we hear. 它在时间上的导数是瞬时频率 ,它是我们听到的音调的音高。

In this case, if we plug frequency = a10 / 2.0 ** time into pi2 * frequency * time , the phase is 在这种情况下,如果我们将frequency = a10 / 2.0 ** time插入pi2 * frequency * time ,则相位为

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

or in symbolic notation: 或用符号表示法:

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

The derivative of this is then 那么就是衍生物

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

and not 2π · A10 · 2 t as you had expected. 而不是像你预期的那样2π·A10·2 - t

This is a plot of the actual frequency sweep that you get using your method (taking into account aliasing, notice how the curve is reflected at the 0 Hz and 24000 Hz lines), vs. what you had intended: 这是使用您的方法获得的实际频率扫描图(考虑混叠,注意曲线在0 Hz和24000 Hz线上的反射方式),与您的预期相比:

频率扫描(线性标度)

And here is the same plot with logarithmic frequency scale, which is how we perceive frequencies as pitches: 这是与对数频率标度相同的图,这是我们如何将频率视为音高:

频率扫描(对数刻度)

Solution

You get the correct result by making the following changes: 通过进行以下更改,您可以获得正确的结果:

  1. Use a high enough value for SAMPLE_RATE . SAMPLE_RATE使用足够高的值。

  2. Don't calculate the sample directly from the given time, instead maintain a phase value which is incremented at a rate proportional to the intended frequency (wrap it at 2π so that it doesn't grow out of bounds), by replacing 不要直接从给定时间计算样本,而是保持一个相位值,该相位值以与预期频率成比例的速率递增(将其包裹在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) 

    by 通过

     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) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM