在给定时间内将频率从 f1 缓慢上升到 f2 的正弦波

Posted

技术标签:

【中文标题】在给定时间内将频率从 f1 缓慢上升到 f2 的正弦波【英文标题】:sine wave that slowly ramps up frequency from f1 to f2 for a given time 【发布时间】:2012-06-26 01:02:22 【问题描述】:

我正在编写一个 c 程序来生成一个正弦波,它在给定的时间间隔内将频率从 f1 缓慢上升到 f2。

我已经编写了这个 c 程序来将频率从 0 增加到 10 Hz,但问题是频率在完成 360 度后会发生变化。如果我尝试在 0 到 360 度之间改变频率,则过渡不平滑并且很突然。

这是我使用的等式 sin y = Amplitude*sin(freq*phase)

int main(int argc, char *argv[]) 

double y, freq,phase;
int count; // for convenience of plotting in matlab so all the waves are spread on x axis.
  for (freq = 0; freq < 10; freq+=1) 
      for (phase = 0; phase < 360; phase++)  // phase is 360 degrees
      y = 3 * sin((count*6.283185)+(freq*(phase*(3.14159/180))));   
    printf("%f %f %f \n", freq, phase, y);
   
  count++;
  
return EXIT_SUCCESS;

    如何在给定时间段内平滑更改频率? 我应该研究傅立叶变换吗?

【问题讨论】:

要生成所需赫兹的“真实”正弦波频率,请使用计数器和计时器。使用 sin 公式生成的频率取决于程序执行的速度。 【参考方案1】:

如果您希望角频率 (w=2 pi f) 随时间线性变化,则 @​​987654323@ 和 w = w0 + (wn-w0)*t/tn(其中 t 从 0 变为 tnww0 变为wn)。相位是其中的一部分,所以phase = w0 t + (wn-w0)*t^2/(2tn)(正如 oli 所说):

void sweep(double f_start, double f_end, double interval, int n_steps) 
    for (int i = 0; i < n_steps; ++i) 
        double delta = i / (float)n_steps;
        double t = interval * delta;
        double phase = 2 * PI * t * (f_start + (f_end - f_start) * delta / 2);
        while (phase > 2 * PI) phase -= 2 * PI; // optional
        printf("%f %f %f", t, phase * 180 / PI, 3 * sin(phase));
    

(其中间隔为 tn,增量为 t/tn)。

这是等效 python 代码的输出(1-10Hz 超过 5 秒):

from math import pi, sin

def sweep(f_start, f_end, interval, n_steps):
    for i in range(n_steps):
        delta = i / float(n_steps)
        t = interval * delta
        phase = 2 * pi * t * (f_start + (f_end - f_start) * delta / 2)
        print t, phase * 180 / pi, 3 * sin(phase)

sweep(1, 10, 5, 1000)

ps 顺便说一句,如果你正在听这个(或看着它 - 任何涉及人类感知的东西),我怀疑你不想要线性增长,而是指数增长。但那是a different question...

【讨论】:

太棒了!!效果很好。当 n_step 非常大时,相位 -= 2 * PI 几乎为零,因此 sin(0) 为 0。相位 -= 2 * PI 导致正弦波以负值开始。当 while 循环被注释掉时,上图是正确的。你能指出你读到这个的来源吗?这几天我头发都掉光了,你救了我。非常感谢。看了指数增长,但看不懂可以请你直观解释一下。 修复了 c 代码中的 (float) 问题,谢谢。不明白关于相位的评论,但只有在你希望它始终在 0-2PI 范围内时才会出现。我没有在任何地方读过这个——我只是想出来的——但据我所知,它与***条目相同(我链接到它,我稍后检查了)。 另外,您可以进行更直接的计算,而不是重复减法。类似int n = phase / (2 * PI); phase -= n * 2 * PI. @andrew cooke 你是对的,我想念while循环。实现 while 循环的另一种方法是 'phase = fmod(phase, (2 * PI));' 是的(不确定 fmod 是否存在)。【参考方案2】:

如何在给定的时间段内平滑地更改频率?

平滑的正弦曲线需要连续相位。相位是频率的积分,所以如果你有一个频率的线性函数(即从 f1 到 f2 的恒定速率增加),那么相位将是时间的二次函数。

你可以用笔和纸算出数学,或者我可以告诉你,得到的波形称为linear chirp

我应该研究傅立叶变换吗?

线性啁啾的傅里叶变换本身就是线性啁啾,所以可能不是。

【讨论】:

【参考方案3】:

应该相当简单。与其考虑改变频率,不如考虑让物体旋转得越来越快。它经过的角距离可能在 N 秒后是 X,但在 2N 秒后会超过 2X(可能是 4X)。所以想出一个角距离的公式(例如,alpha = k1 * T + k2 * T**2),并取该角距离的正弦值,求出任意时刻T的波形值。

【讨论】:

【参考方案4】:
+ (void) appendChirp:(int[])sampleData size:(int)len 
    withStartFrequency:(double)startFreq withEndFrequency:(double)endFreq 
    withGain:(double)gain 

double sampleRate = 44100.0;

for (int i = 0; i < len; i++) 

    double progress = (double)i / (double)len;
    double frequency = startFreq + (progress * (endFreq - startFreq));
    double waveLength = 1.0 / frequency;

    double timePos = (double)i / sampleRate; 
    double pos = timePos / waveLength;
    double val = sin(pos * 2.0 * M_PI); // -1 to +1 

    sampleData[i] += (int)(val * 32767.0 * gain);



【讨论】:

不完全。尽管这很流畅并且在图形计算器上“看起来不错”,但快速演示足以说明错误。使用此代码生成由开始和结束频率声音包围的声音表明它没有在正确的位置结束。 Sine(startFreq, 1 second), sweep(startFreq, endFreq, 1 second), Sine(endFreq, 1 second),然后,如果您愿意,执行一些频谱分析。我的光谱分析(Cool Edit Pro 2)显示它达到了 650 赫兹左右。 当您的光谱分析显示为 650 赫兹时,您的输入是什么?我的函数可能不准确,但我很确定它的输出取决于它的输入。顺便说一句,这个方法来自我的软件合成器,听起来不错,看起来也不错。 @MusicGenesis 对不起,我忘了告诉你参数!我做了一个从 220 到 440 赫兹的正弦扫描。正如我所说,扫描在 440 正弦播放之前大约 650 赫兹结束。

以上是关于在给定时间内将频率从 f1 缓慢上升到 f2 的正弦波的主要内容,如果未能解决你的问题,请参考以下文章

CC2541广播机制和代码分析(未完成)

如何使用排序从输出文件 1 中的 F1 和 F2 中获取匹配记录,以及从输出文件 2 中的 F2 中获取不匹配记录

如何将归一化频率转换为实际频率

C#Winform编程,怎样在一个窗口f1中调用另一个窗口f2 在f2中数据改变,在f1中相应的也改变

Codeforces 1077(F1+F2) DP 单调队列

二分AtcoderE - Max Min