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

时间:2012-12-26 04:08:32

标签: c# audio synthesizer

我正在使用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;
    }

3 个答案:

答案 0 :(得分:2)

您正在使用绝对时间来确定波函数,因此当您稍微更改频率时,下一个样本就是您在新频率下开始运行时的样本。

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

更顺畅的方法是保留一阶导数。这更加困难,因为与波本身不同,一阶导数的幅度随频率而变化,这意味着保持相位是不够的。无论如何,这种增加的复杂性几乎肯定是过度的,因为你可以顺利地改变频率。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

  Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);

人耳不像那样工作,它非常非线性。自然是对数的。中间C的频率为261.626Hz。下一个注释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。注意增量加倍。因此,代码段中的frequencyStep不应该是一个补充,它应该是乘法

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

这也是一个问题。您计算的样本不能平滑地从一个频率转换到下一个频率。 “频率”发生变化时有一个步骤。您必须将偏移应用于“时间”,以便在采样时间“时间 - 1”生成完全相同的样本值,该值与先前使用之前的“频率”值计算的值相同。这些步骤产生高频伪像,其中有许多谐波对人耳来说是明显的。

Wikipedia article中提供了背景信息。它有助于可视化您生成的波形,您可以轻松诊断步骤问题。我将复制Wiki图像:

enter image description here