【问题标题】:How to resample raw audio from WasapiCapture to g711 mulaw with NAudio?如何使用 NAudio 将原始音频从 WasapiCapture 重新采样到 g711 mulaw?
【发布时间】:2019-07-10 22:19:54
【问题描述】:

我正在尝试使用 NAudio 使客户端模拟软件电话,通过捕获本地麦克风/扬声器设备来发送 g.711 MuLaw 格式的电话 RTP 数据包,但是此过程缺少一些不会使感知可用的过时信息。 MuLaw 与 MediaFoundationResampler 和 WdlResampler 不兼容,ACM 重采样器完全混淆了音频质量,下面的代码让我进入 PCM,但从那里没有关于如何前进的信息。是否应该有一个低通滤波器或这里添加的东西?您是否应该根据2013 article(无论如何与 MFR 不兼容)将 WasapiCapture 事件中的原始字节数据转换为 16 位?

我没有处理音频的知识或经验,所以整个过程对我来说是陌生的,需要一些指导,从这里开始,因为only closest answer 并没有实际发布他们是如何“解决”它的。

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?
    }
}

【问题讨论】:

    标签: c# naudio


    【解决方案1】:

    在与这个库苦苦挣扎并从不同来源收集了几个不同的建议之后,我最终得到了一些最终奏效的东西,所以希望这可以为其他人省去很多麻烦。

    我基本上不得不推出自己的 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-07
      • 2018-01-14
      • 2017-07-08
      • 1970-01-01
      相关资源
      最近更新 更多