【问题标题】: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;
    }

【问题讨论】:

    标签: c# audio synthesizer


    【解决方案1】:

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

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

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

    【讨论】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      • 2011-12-10
      • 1970-01-01
      • 2013-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多