如何使用 NAudio 将原始音频从 WasapiCapture 重新采样到 g711 mulaw?
How to resample raw audio from WasapiCapture to g711 mulaw with NAudio?
我正在尝试使用 NAudio 使客户端模拟软电话,通过捕获本地 microphone/speaker 设备以 g.711 MuLaw 格式发送电话 RTP 数据包,但此过程缺少一些步骤对可用的过时信息没有意义。 MuLaw 与 MediaFoundationResampler 和 WdlResampler 不兼容,ACM 重采样器完全混淆了音频质量,下面的代码将我带到了 PCM,但从那里没有关于如何前进的信息。是否应该在此处添加低通滤波器或其他东西?您是否应该根据 2013 article(无论如何与 MFR 不兼容)将原始字节数据从 WasapiCapture 事件转换为 16 位?
我没有处理音频的知识或经验,所以整个过程对我来说很陌生,需要从这里开始的方向,因为 实际上 post他们是如何 "solved" 的。
private static IWaveIn ActiveMicrophone = new WasapiCapture(ActiveMicrophoneDevice);
ActiveMicrophone.DataAvailable += OnMicrophoneDataAvailableAsync;
...
private async void OnMicrophoneDataAvailableAsync(object sender, WaveInEventArgs e)
{
MemoryStream micStream = new MemoryStream();
micStream.Write(e.Buffer, 0, e.BytesRecorded);
micStream.Position = 0;
var inputStream = new RawSourceWaveStream(micStream, ActiveMicrophone.WaveFormat);
WaveFormat outputFormat = new WaveFormat(8000, 8, 1);
using (var resampler = new MediaFoundationResampler(inputStream, outputFormat))
{
MemoryStream outputStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(outputStream, resampler);
// Do something with outputStream?
}
}
在努力使用这个库并从各种来源收集了几个不同的建议之后,我最终得到了一些最终起作用的东西,希望这可以让其他人省去很多麻烦。
我基本上不得不推出自己的 IWaveProvider 并执行各种自定义过滤器等等,直到它起作用。
public class MuLawResamplerProvider : IWaveProvider
{
public WaveFormat WaveFormat => WaveFormat.CreateMuLawFormat(8000, 1);
private BufferedWaveProvider waveBuffer;
private IWaveProvider ieeeToPcm;
private byte[] sourceBuffer;
/// <summary>
/// Converts from 32-bit Ieee Floating-point format to MuLaw 8khz 8-bit 1 channel.
/// Used for WasapiCapture and WasapiLoopbackCapture.
/// </summary>
/// <param name="audio">The raw audio stream.</param>
/// <param name="inputFormat">The input format.</param>
public MuLawResamplerProvider(byte[] stream, WaveFormat inputFormat)
{
// Root buffer provider.
waveBuffer = new BufferedWaveProvider(inputFormat);
waveBuffer.DiscardOnBufferOverflow = false;
waveBuffer.ReadFully = false;
waveBuffer.AddSamples(stream, 0, stream.Length);
var sampleStream = new WaveToSampleProvider(waveBuffer);
// Stereo to mono filter.
var monoStream = new StereoToMonoSampleProvider(sampleStream)
{
LeftVolume = 2.0f,
RightVolume = 2.0f
};
// Downsample to 8000 filter.
var resamplingProvider = new WdlResamplingSampleProvider(monoStream, 8000);
// Convert to 16-bit in order to use ACM or MuLaw tools.
ieeeToPcm = new SampleToWaveProvider16(resamplingProvider);
sourceBuffer = new byte[ieeeToPcm.WaveFormat.AverageBytesPerSecond];
}
/// <summary>
/// Reset the buffer to the starting position with a new stream.
/// </summary>
/// <param name="stream">New stream to initialize.</param>
public void Reset(byte[] stream)
{
waveBuffer.ClearBuffer();
waveBuffer.AddSamples(stream, 0, stream.Length);
}
/// <summary>
/// Converts the 16-bit ACM stream to 8-bit MuLaw on read.
/// </summary>
/// <param name="destinationBuffer">The destination buffer to output into.</param>
/// <param name="offset">The offset to store at.</param>
/// <param name="readingCount">The requested size to read.</param>
/// <returns></returns>
public int Read(byte[] destinationBuffer, int offset, int readingCount)
{
// Source buffer has twice as many items as the output array.
var sizeOfPcmBuffer = readingCount * 2;
sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sizeOfPcmBuffer);
var sourceBytesRead = ieeeToPcm.Read(sourceBuffer, 0, sizeOfPcmBuffer);
var samplesRead = sourceBytesRead / 2;
var outIndex = 0;
for (var n = 0; n < sizeOfPcmBuffer; n += 2)
{
destinationBuffer[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(sourceBuffer, offset + n));
}
return samplesRead;
}
}
然后你可以用它做一些像这样简单的事情
var resampler = new MuLawResamplerProvider(deviceStream.ToArray(), ActiveWasapiCaptureDevice.WaveFormat);
// Write it out to a local file
string path = @"C:\Temp\" + Guid.NewGuid().ToString() + ".wav";
WaveFileWriter.CreateWaveFile(path, resampler);
resampler.Reset(deviceStream.ToArray());
// Write it into memory for other uses
MemoryStream outputStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(outputStream, resampler);
我正在尝试使用 NAudio 使客户端模拟软电话,通过捕获本地 microphone/speaker 设备以 g.711 MuLaw 格式发送电话 RTP 数据包,但此过程缺少一些步骤对可用的过时信息没有意义。 MuLaw 与 MediaFoundationResampler 和 WdlResampler 不兼容,ACM 重采样器完全混淆了音频质量,下面的代码将我带到了 PCM,但从那里没有关于如何前进的信息。是否应该在此处添加低通滤波器或其他东西?您是否应该根据 2013 article(无论如何与 MFR 不兼容)将原始字节数据从 WasapiCapture 事件转换为 16 位?
我没有处理音频的知识或经验,所以整个过程对我来说很陌生,需要从这里开始的方向,因为
private static IWaveIn ActiveMicrophone = new WasapiCapture(ActiveMicrophoneDevice);
ActiveMicrophone.DataAvailable += OnMicrophoneDataAvailableAsync;
...
private async void OnMicrophoneDataAvailableAsync(object sender, WaveInEventArgs e)
{
MemoryStream micStream = new MemoryStream();
micStream.Write(e.Buffer, 0, e.BytesRecorded);
micStream.Position = 0;
var inputStream = new RawSourceWaveStream(micStream, ActiveMicrophone.WaveFormat);
WaveFormat outputFormat = new WaveFormat(8000, 8, 1);
using (var resampler = new MediaFoundationResampler(inputStream, outputFormat))
{
MemoryStream outputStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(outputStream, resampler);
// Do something with outputStream?
}
}
在努力使用这个库并从各种来源收集了几个不同的建议之后,我最终得到了一些最终起作用的东西,希望这可以让其他人省去很多麻烦。
我基本上不得不推出自己的 IWaveProvider 并执行各种自定义过滤器等等,直到它起作用。
public class MuLawResamplerProvider : IWaveProvider
{
public WaveFormat WaveFormat => WaveFormat.CreateMuLawFormat(8000, 1);
private BufferedWaveProvider waveBuffer;
private IWaveProvider ieeeToPcm;
private byte[] sourceBuffer;
/// <summary>
/// Converts from 32-bit Ieee Floating-point format to MuLaw 8khz 8-bit 1 channel.
/// Used for WasapiCapture and WasapiLoopbackCapture.
/// </summary>
/// <param name="audio">The raw audio stream.</param>
/// <param name="inputFormat">The input format.</param>
public MuLawResamplerProvider(byte[] stream, WaveFormat inputFormat)
{
// Root buffer provider.
waveBuffer = new BufferedWaveProvider(inputFormat);
waveBuffer.DiscardOnBufferOverflow = false;
waveBuffer.ReadFully = false;
waveBuffer.AddSamples(stream, 0, stream.Length);
var sampleStream = new WaveToSampleProvider(waveBuffer);
// Stereo to mono filter.
var monoStream = new StereoToMonoSampleProvider(sampleStream)
{
LeftVolume = 2.0f,
RightVolume = 2.0f
};
// Downsample to 8000 filter.
var resamplingProvider = new WdlResamplingSampleProvider(monoStream, 8000);
// Convert to 16-bit in order to use ACM or MuLaw tools.
ieeeToPcm = new SampleToWaveProvider16(resamplingProvider);
sourceBuffer = new byte[ieeeToPcm.WaveFormat.AverageBytesPerSecond];
}
/// <summary>
/// Reset the buffer to the starting position with a new stream.
/// </summary>
/// <param name="stream">New stream to initialize.</param>
public void Reset(byte[] stream)
{
waveBuffer.ClearBuffer();
waveBuffer.AddSamples(stream, 0, stream.Length);
}
/// <summary>
/// Converts the 16-bit ACM stream to 8-bit MuLaw on read.
/// </summary>
/// <param name="destinationBuffer">The destination buffer to output into.</param>
/// <param name="offset">The offset to store at.</param>
/// <param name="readingCount">The requested size to read.</param>
/// <returns></returns>
public int Read(byte[] destinationBuffer, int offset, int readingCount)
{
// Source buffer has twice as many items as the output array.
var sizeOfPcmBuffer = readingCount * 2;
sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sizeOfPcmBuffer);
var sourceBytesRead = ieeeToPcm.Read(sourceBuffer, 0, sizeOfPcmBuffer);
var samplesRead = sourceBytesRead / 2;
var outIndex = 0;
for (var n = 0; n < sizeOfPcmBuffer; n += 2)
{
destinationBuffer[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(sourceBuffer, offset + n));
}
return samplesRead;
}
}
然后你可以用它做一些像这样简单的事情
var resampler = new MuLawResamplerProvider(deviceStream.ToArray(), ActiveWasapiCaptureDevice.WaveFormat);
// Write it out to a local file
string path = @"C:\Temp\" + Guid.NewGuid().ToString() + ".wav";
WaveFileWriter.CreateWaveFile(path, resampler);
resampler.Reset(deviceStream.ToArray());
// Write it into memory for other uses
MemoryStream outputStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(outputStream, resampler);