如何在 c# 中扬声器没有声音发出时使用 WasapiLoopbackCapture 录制音频?



下面是我录制来自扬声器的音频的示例代码。它工作正常。但它仅在扬声器发出某些音频时才录制音频。如果没有音频,则不录制。实际上,我正在使用系统的声音进行屏幕录制。wasapiloop 录制的音频长度与屏幕录制长度不匹配,因为 wasapiloop 仅在扬声器有声音时才录制音频。

WasapiCapture waveLoop = new WasapiLoopbackCapture();
waveLoop.Initialize();
waveLoop.DataAvailable += waveLoop_DataAvailable;
waveLoop.Stopped += waveLoop_Stopped;
waveLoop.Start();

我见过一个类似的流堆栈问题,但我没有完全理解它。

静音时的CSCore环回录制

任何帮助将不胜感激。

根据设计,WASAPI 环回捕获仅在实际播放时生成数据 - 您已经弄清楚了这一点。如果您的目标是生成连续的数据流,则负责为环回捕获生成的数据之间的间隙生成静音音频字节。环回捕获数据带有时间戳和不连续标志,因此您可以应用简单的数学运算并确定要添加的零/静默字节数。

更新。检查了 NAudio 代码(尤其是此处(后,我发现DataAvailable事件中静默字节计算的准确数学存在问题:NAudio 忽略环回捕获报告的DataDiscontinuity也称为AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY标志。

当端点上有播放数据时,环回捕获将生成特定持续时间(例如 10 毫秒长(的音频数据包。这些数据包将暴露给您,以便通过WasapiLoopbackCapture接口读取。没有缓冲,只要您读取的数据是连续的,GetBuffer调用就不会引发DataDiscontinuity标志。也就是说,您在第一个数据包中具有标志,然后直到您下次将数据视为连续时看到它。

当播放停止并且环回捕获停止生成数据时,没有来自GetBuffer的新数据。但是,您知道数据包的大小/持续时间,并且在没有数据的情况下,计时器需要用静默字节替换这些缺失的数据(处理具有相应静音数据包大小的序列中的部分填充的最后一个数据包也是一个好主意(。您将注入静默字节GetBuffer直到调用成功并获得可用的新数据。您将在那里DataDiscontinuity标志来指示新的数据序列。

从那时起,您应该停止静音并再次使用读取捕获的数据。此外,由于您知道不涉及缓冲,因此最好使用不连续数据包来更新计时记录:当您拥有数据包时,您可以假设其最后一个字节对应于GetBuffer成功的时间时刻。从那里,您可以得出要填充的静音字节数,以实现组合流的完美连续性。

因此,我们的想法是从 NAudio 获取DataDiscontinuity标志,以免丢失,添加一个计时器以及时生成静音字节,并将所有内容与检查连续性、在计时器回调中添加静音字节并将所有内容组合到连续馈送中。

由于没有公认的答案,我将尝试解释问题和最简单的解决方案。

WASAPI 只会在您正在录制的设备上播放某些内容时发送数据(触发 NAudio 上的 DataAvailable 事件(。

您可以执行另一个答案中提到的操作,即执行复杂且不稳定的数学运算,以尝试估计在哪里用沉默填充数据中断。但是,有一个更简单的解决方案。

简单的解决方案:

为指定的 MMDevice 创建一个 WasapiLoopbackCapture。在下面的示例中,我得到了默认的音频输出终结点:

var device = new MMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

使用 MMDevice 检索到的 WaveFormat 创建一个 SilenceProvider:

var silenceProvider = new SilenceProvider(device.WaveFormat);

在给定的设备上初始化一个WasapiOut播放器传递SilenceProvider,然后调用Play()

using (var wasapiOut = new WasapiOut(device, AudioClientShareMode.Shared, false, 250))
{
wasapiOut.Init(silenceProvider);
wasapiOut.Play();
}

假设您想在另一个线程上执行所有这些操作,并监听某些退出布尔值变为真,类似于while(!exit) { Thread.Sleep(250); }

关于

MMDevice

的说明!MMDevice只能在实例化的线程上使用,在该线程外部使用它将导致 COM 异常(请参阅这篇文章(。

播放静音。Wasapi会认为有东西要录制,并将音频发送到环回设备。

https://mathewsachin.github.io/blog/2017/07/28/mixing-audio.html

另外,@Hans Passant在评论中写了这个链接:

https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/

相关内容

  • 没有找到相关文章

最新更新