我正在使用naudio和SineWaveProvider32代码直接从http://mark-dot-net.blogspot.com/2009/10/playback-of-sine-wave-in-naudio.html生成 正弦波音。 SineWaveProvider32类中的相关代码:
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
buffer[n + offset] =
(float)(Amplitude * Math.Sin((2 * Math.PI * sample * Frequency) / sampleRate));
sample++;
if (sample >= sampleRate) sample = 0;
}
return sampleCount;
}
我每秒都获得点击/节拍,所以我改变了
if (sample >= sampleRate) sample = 0;
到
if (sample >= (int)(sampleRate / Frequency)) sample = 0;
这样每秒都会修复点击次数(因此&#34;示例&#34;始终相对于过零,而不是采样率)。
但是,每当我设置Amplitude变量时,我都会点击一下。我尝试只在缓冲区[]处于过零状态时设置它, 认为振幅的突然跳跃可能导致问题。那并没有解决问题。我将Amplitude设置为之间的值 0.25和0.0
我尝试按照NAudio change volume in runtime中的建议调整延迟和缓冲区数量 也没有效果。
我的代码更改了幅度:
public async void play(int durationMS, float amplitude = .25f)
{
PitchPlayer pPlayer = new PitchPlayer(this.frequency, amplitude);
pPlayer.play();
await Task.Delay(durationMS/2);
pPlayer.provider.Amplitude = .15f;
await Task.Delay(durationMS /2);
pPlayer.stop();
}
答案 0 :(得分:0)
咔嗒声是由波形中的不连续引起的。这很难在像这样的类中修复,因为理想情况下,您会将音量从一个值缓慢增加到另一个值。这可以通过修改代码以具有目标幅度来完成,然后如果当前幅度不等于目标幅度,则通过循环每次计算的小增量量向其移动。因此,在10毫秒的时间段内,您将从旧振幅移动到新振幅。但不幸的是,你需要自己写这篇文章。
对于频率逐渐变化而不是幅度的类似概念,请查看portamento in NAudio上的博文。
答案 1 :(得分:0)
角速度
就角速度而言,比频率更容易思考。每个样本增加sin()函数的角度自变量的数量。
使用弧度角时,一个完整的圆的周期为2 * pi,因此1 Hz的角速度为(2 * pi)/ T =(2 * pi)/ 1 / f = f * 2 * pi = 1 * 2 * pi [rad / s]
采样率以[每秒采样数]为单位,角速度以[弧度每秒为单位],因此只需将角速度除以样本即可获得[每个样本角度]速率以获得[弧度/秒] / [样本/秒] = [弧度/样本]。
该数字用于不断增加每个样本的sin()函数角度-无需乘法。
要从一个频率扫描到另一个频率,您只需在多个样本上以小步长从一个角度增量移动到另一个角度即可。
通过在频率之间进行扫描,相邻样本之间将形成连续的链,并且瞬态会随着时间的流逝而平稳地散布。
从一个振幅移动到另一个振幅也可以分布在多个样本上,以避免剧烈的瞬变。
与在一个样本中将输出从一个级别逐步过渡到另一个级别相比,淡入和淡出渐进地调整声音的开始和结束的幅度更为优雅。
尖锐的台阶在水面上产生环,并向世界传播。
关于sin()计算
为了快速计算,最好旋转幅度长度的矢量,并仅在角速度变化时才计算sn = sin(delta),cs = cos(delta):
其中幅度^ 2 = x ^ 2 + y ^ 2,则每个新样本可以计算为:
px = x * cs - y * sn;
py = x * sn + y * cs;
要增加幅度,只需将px和py乘以系数1.01。要制作下一个示例,请设置x = px,y = py并再次使用cs和sn再次运行px,py计算。
py或px可以用作信号输出,并且相位相差90度。
在第一个样本上,您可以设置x =振幅和y = 0。