好的,所以我正在制作语音聊天软件。我正在使用 NAudio,一个优秀的库。
但我有一个问题。发生某些事情时,缓冲区会上升。我猜它来自示例,当操作系统加载某些内容并且语音聊天应用程序被“暂停”一秒钟时。在此期间,它将数据添加到缓冲区中,使当前数据延迟。
而且由于接收器一直在以相同的速度播放,它总是会延迟。
现在我有一个“解决方案”,即当缓冲区达到一定长度时清除缓冲区。尽管这根本不理想,而且更像是一个技巧而不是解决方案。
现在到代码部分。首先我初始化我使用的东西。
private NAudio.Wave.WaveInEvent SendStream = new WaveInEvent();
private NAudio.Wave.AsioOut Aut;
private NAudio.Wave.WaveFormat waveformat = new WaveFormat(48000, 16, 2);
private WasapiLoopbackCapture Waloop = new WasapiLoopbackCapture();
private NAudio.Wave.BufferedWaveProvider waveProvider;
waveProvider = new NAudio.Wave.BufferedWaveProvider(waveformat);
waveProvider.DiscardOnBufferOverflow = true;
SendStream.WaveFormat = waveformat;
使用waveformat,所以我不必一直重写它。使用 DiscardOnBufferOverflow,所以如果我在缓冲区上设置一定的长度,例如 20 毫秒。它将丢弃上面的任何内容,否则它将返回异常。但是我认为,如果我不设置长度,它不会做任何事情,默认情况下它可能是无限的。
仅此而已,SendStream 是一个 WaveInEvent,这意味着当我使用 DataAvailable 时它将在 BackgroundThread 上运行。Waloop 几乎相同,只是它是一个环回。waveprovider 用于接收部分播放音频。Waveformat 是,很好的波形,设置它很重要,并且至少在我的应用程序中是一样的。
这里是接收部分。如您所见,它将数据放入字节数组中,然后播放。没什么奇怪的。
byte[] byteData = udpClient.Receive(ref remoteEP);
waveProvider.AddSamples(byteData, 0, byteData.Length);
这是发送/记录部分。
private void Sendv2()
{
try
{
if (connect == true)
{
if (AudioDevice == "Wasapi Loopback")
{
SendStream.StopRecording();
Waloop.StartRecording();
}
else
{
Waloop.StopRecording();
SendStream.StartRecording();
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
void Sending(object sender, NAudio.Wave.WaveInEventArgs e)
{
if (connect == true && MuteMic.Checked == false)
{
udpClient.Send(e.Buffer, e.BytesRecorded, otherPartyIP.Address.ToString(), 1500);
}
}
void SendWaloop(object sender, NAudio.Wave.WaveInEventArgs e)
{
byte[] newArray16Bit = new byte[e.BytesRecorded / 2];
short two;
float value;
for (int i = 0, j = 0; i < e.BytesRecorded; i += 4, j += 2)
{
value = (BitConverter.ToSingle(e.Buffer, i));
two = (short)(value * short.MaxValue);
newArray16Bit[j] = (byte)(two & 0xFF);
newArray16Bit[j + 1] = (byte)((two >> 8) & 0xFF);
}
if (connect == true && MuteMic.Checked == false)
{
udpClient.Send(newArray16Bit, newArray16Bit.Length, otherPartyIP.Address.ToString(), 1500);
}
}
Waloop 是一个 Loopback,所以它通过另一个“通道”,但它在这里并不重要。
非常简单,当数据可用时(当它记录时)并且如果连接为真等,它只会发送缓冲区。
非常像接收器部分,但其他方式。
现在我目前如何解决这个问题是这样的:
if (waveProvider.BufferedDuration.Milliseconds > 40)
{
waveProvider.ClearBuffer();
TimesBufferClear++;
}
因此,如果缓冲区超过 40 毫秒,我将清除缓冲区(这是在 600 毫秒间隔的计时器中)。(TimesBufferClear++;只是这样我就可以跟踪它被清除的时间)
现在可悲的是,我不知道如何防止缓冲区增加,并将其设置为强制状态(20ms 等)只会导致播放变得越来越糟,因为缓冲区越高,因为它并没有真正停止,它只是忽略了我认为的上述部分。
这是输入设备的创建。在我的实现中它与 ASIO 和 Wasapi 有点不同,但它的工作原理几乎相同,唯一真正的区别是我告诉 UI ASIO 是打开或关闭,正如你在代码中看到的那样,最后我添加了发送流(任何输入、麦克风等)和 Waloop(正在播放的环回声音)的 DataAvailable 事件。
private void CheckAsio()
{
if (NAudio.Wave.AsioOut.isSupported())
{
Aut = new NAudio.Wave.AsioOut();
ASIO.Text += "\nSupported: " + Aut.DriverName;
ASIO.ForeColor = System.Drawing.Color.Green;
Aut.Init(waveProvider);
Aut.Play();
SendStream.NumberOfBuffers = 2;
SendStream.BufferMilliseconds = 10;
}
else
{
AsioSettings.Enabled = false;
ASIO.Text += "\n Not Supported: Wasapi used";
ASIO.ForeColor = System.Drawing.Color.DarkGray;
Wasout = new WasapiOut(AudioClientShareMode.Shared, 0);
Wasout.Init(waveProvider);
Wasout.Play();
SendStream.NumberOfBuffers = 2;
SendStream.BufferMilliseconds = 9;
}
SendStream.DataAvailable += Sending;
Waloop.DataAvailable += SendWaloop;
}
我不确定这是否可以解决。但是因为我没有看到其他语音聊天程序有它,我猜肯定有什么可以做的。