正弦波点击 Beep.BeepBeep

Sine wave clicking with Beep.BeepBeep

最初,我使用 C# 在 windows 控制台中制作歌曲的项目使用 Console.Beep(freq,time);,但它的不可靠性导致不同步。

我改为使用 Beep Beep class. The problem is that when playing a baseline, there was an audible click at the end of every note. Here's an example。一个音符还不错,但是很多个连续音符就受不了了。

即使我在 wave 编辑器中打开它,无论我在哪里裁剪它,它总是点击。

如何在不将任何庞大的库安装到我的 C# 控制台项目中的情况下摆脱这种点击?

在不深入研究数字信号处理理论的情况下很难解释原因,但基本上是因为您的信号有非常突然的启动和停止而引起的,这转化为高频分量,可以听到 'click'.

如果您淡入和淡出信号的前 10% 和后 10%(例如使用 Audacity),您会发现咔哒声消失了。

更新:我会留下答案,因为基本思想仍然成立,但同意应用淡出的解决方案。除了解决点击问题,这也是应用 ADSR 包络曲线的第一步,这将使纯正弦波听起来更有趣,即使不添加泛音..


问题出在提示音 class 上。它并不注意以过零结束,即以完整的正弦波结束。

纠正此问题的最自然方法是控制它创建的样本数量。

不确定这个计算:

double xx = 2 * Math.PI / Frequency;
Samples = (int)( (int)(Samples * xx) / xx);

或者您可以在创建数据后截断数据。

你可以试试这个比较粗略的方法:

..
MS.Seek(0, SeekOrigin.Begin);

byte[] bytes = MS.ToArray();
Array.Reverse(bytes);
int z = 0;
for (int i = 0; i < bytes.Length; i+=4)
{
    int v = bytes[i] +  bytes[i+1] +  bytes[i+2] +  bytes[i+3];
    if (v == 0) { z = i; break; }
}
MS.SetLength(bytes.Length - z);
..

这会找到流中的最后一个零交叉点,即以 4 的倍数开始(或更确切地说结束)的最后连续 4 个零。

我敢肯定,其他更正此问题的方法会不那么骇人听闻,但它应该对初学者有用..

您是否尝试过在声音开始时提高振幅,然后在结束时再次降低振幅。所以,修改那个 beep beep class 的内循环,写成...

    for (int T = 0; T < Samples; T++)
    {
      short Sample = System.Convert.ToInt16(A * Math.Sin(DeltaFT * T));
      BW.Write(Sample);
      BW.Write(Sample);
    }

可以修改成这样

 for (int T=0; T < Samples; T++)
 {
     double AmpRamp = 1.0D;
     if (T<1000) {
         AmpRamp = ((double) T) / 1000.0D;
     } else if (T > Samples - 1000) {
         AmpRamp = ((double) (Samples - T)) / 1000.0D;
     }
     short Sample = System.Convert.ToInt16(A*AmpRamp*Math.Sin(DeltaFT * T));
     BW.Write(Sample);
     BW.Write(Sample);
 }

这会提高前 1000 个样本的振幅,然后在最后 1000 个样本中降低振幅,但您可能必须更改斜坡的样本数。