合成器从一个频率滑到另一个频率

Posted

技术标签:

【中文标题】合成器从一个频率滑到另一个频率【英文标题】:Synthesizer Slide from One Frequency to Another 【发布时间】:2012-12-26 04:08:31 【问题描述】:

我正在使用 NAudio 在 C# 中编写一个合成器。我试图让它在频率之间平滑滑动。但我有一种感觉,我不了解所涉及的数学。在切换到正确的下一个音高之前,它会以高音疯狂滑动。

从一个音高滑到另一个音高的数学正确方法是什么?

代码如下:

public override int Read(float[] buffer, int offset, int sampleCount) int sampleRate = WaveFormat.SampleRate;

        for (int n = 0; n < sampleCount; n++)
        
            if (nextFrequencyQueue.Count > 0)
                                
                nextFrequency = nextFrequencyQueue.Dequeue();
            

            if (nextFrequency > 0 && Frequency != nextFrequency)
            
                if (Frequency == 0) //special case for first note
                
                    Frequency = nextFrequency;
                
                else //slide up or down to next frequency
                
                    if (Frequency < nextFrequency)
                    
                        Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);
                    
                    if (Frequency > nextFrequency)
                    
                        Frequency = Clamp(Frequency - frequencyStep, Frequency, nextFrequency);
                    
                
            

            buffer[n + offset] = (float)(Amplitude * Math.Sin(2 * Math.PI * time * Frequency));
            try
            
                time += (double)1 / (double)sampleRate;
            
            catch
            
                time = 0;
            
        
        return sampleCount;
    

【问题讨论】:

【参考方案1】:

您正在使用绝对时间来确定波函数,因此当您稍微改变频率时,如果您以该新频率开始运行,下一个样本将会是什么。

我不知道已建立的最佳方法,但一种可能足够好的简单方法是计算相位 (φ = t mod 1/fold) 并调整 t 以保留相位在新频率下(t = φ/fnew)。

更平滑的方法是保留一阶导数。这更加困难,因为与波本身不同,一阶导数的幅度随频率而变化,这意味着保持相位是不够的。无论如何,考虑到您正在平稳地改变频率,这种增加的复杂性几乎肯定是多余的。

【讨论】:

你对灵态的看法是完全正确的。那是我没有考虑到的。这家伙:msdn.microsoft.com/en-us/magazine/hh882455.aspx 似乎考虑了相位(在下载的代码中)。我稍后会试试。简而言之,如果您想象 Sin(theta) 产生音频的时间螺旋,他会跟踪角度以保持相位并滑动他会增加“旋转速度”。 我确实试过了,但高频比低频响亮。我不记得我是否在旧代码中体验过这种效果......无论如何,这是我关于 SO 的新问题,如果你想破解它:***.com/questions/14115606/…【参考方案2】:

一种方法是使用波表。你在一个数组中构造一个完整的正弦波周期,然后在你的 Read 函数中你可以简单地查找它。您阅读的每个样本,您都会根据所需的输出频率计算出一个数量。然后,当您想要滑行到新频率时,您计算新的增量以查找表,然后不是直接去那里,而是逐步调整增量以在设定的时间段内移动到新值('glide ' 或滑音时间)。

【讨论】:

我以前听人推荐过波表。有什么好的教程吗? 这里有一个相当详细的one【参考方案3】:
  Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);

人耳不是这样工作的,它是高度非线性。自然是对数的。中间 C 的频率为 261.626 Hz。下一个音符 C# 与前一个音符的关系是 Math.Pow(2, 1/12.0) 或大约 1.0594631。所以 C# 是 277.183 Hz,增量为 15.557 Hz。

刻度上的下一个 C 具有两倍的频率,即 523.252 Hz。之后的 C# 是 554.366 Hz,增量为 31.084 Hz。注意增量是如何翻倍的。所以你的代码 sn-p 中的 frequencyStep 不应该是加法,它应该是 乘法

  buffer[n + offset] = (float)(Amplitude * Math.Sin(2 * Math.PI * time * Frequency));

这也是一个问题。您计算的样本不会从一个频率平滑过渡到下一个频率。当“频率”改变时有一个步骤。您必须对“时间”应用偏移量,以便它在采样时间“时间 - 1”产生完全相同的采样值,与您之前使用频率的先前值计算的值相同。这些步骤会产生带有许多谐波的高频伪影,这些谐波对人耳来说非常明显。

背景信息可在此Wikipedia article 中找到。它将有助于可视化您生成的波形,您将很容易诊断出阶跃问题。我将复制 Wiki 图片:

【讨论】:

以上是关于合成器从一个频率滑到另一个频率的主要内容,如果未能解决你的问题,请参考以下文章

声音合成:使用 AS3 在频率之间插值

FPGA教程案例30基于FPGA的DDS直接数字频率合成器之三——借助MATLAB进行频率精度分析

在 Numpy 中从一个音高到另一个音高的正弦波滑音

FPGA学习之 直接数字频率合成器(DDS)

FPGA学习之 直接数字频率合成器(DDS)

FPGA学习之 直接数字频率合成器(DDS)