Как ресамплировать необработанный звук из WasapiCapture в g711 с помощью NAudio? - PullRequest
0 голосов
/ 11 июля 2019

Я пытаюсь использовать NAudio, чтобы заставить клиента имитировать программный телефон для отправки RTP-пакетов телефонии в формате MuLaw g.711 путем захвата локальных микрофонов / динамиков, но в этом процессе отсутствуют некоторые шаги, которые не выполняются смысл с устаревшей информацией. MuLaw несовместим с MediaFoundationResampler и WdlResampler, ресемплер ACM полностью искажает качество звука, и приведенный ниже код возвращает меня в PCM, но оттуда нет информации о том, как двигаться дальше. Здесь должен быть фильтр нижних частот или что-то еще добавлено? Предполагается ли преобразовывать необработанные байтовые данные из события WasapiCapture в 16-битные в соответствии со статьей 2013 (которая в любом случае несовместима с MFR)?

У меня нет знаний или опыта работы со звуком, поэтому весь этот процесс для меня чужд, и мне нужно какое-то направление относительно того, что делать дальше, поскольку единственный ближайший ответ на самом деле не писал, как они "решил" это.

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

1 Ответ

0 голосов
/ 12 июля 2019

Поработав с этой библиотекой и собрав несколько разных рекомендаций из разных источников, я получил кое-что, что наконец-то сработало, так что, надеюсь, это избавит других от головной боли.

Iпо сути, мне приходилось накатывать свой собственный 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);
...