正弦波点击 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 个样本中降低振幅,但您可能必须更改斜坡的样本数。
最初,我使用 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 个样本中降低振幅,但您可能必须更改斜坡的样本数。