0 Comments

A few years back I wrote a blog post explaining how you can perform “input driven resampling” with NAudio using the ACM resampler codec installed on your system.

Input driven resampling is when you have blocks of audio you are receiving (say from the microphone) and you want to pass them to the resampler, and ask it to give you however many samples that input turns into.

For playing back audio, generally output driven resampling is what you want, and NAudio has lots of good options available for that. But a common use case for input driven resampling is where you want to save the audio you’re recording from the microphone in a different format (usually a lower sample rate) than the microphone is providing the audio samples in.

Now the input driven ACM resampling option is fine if you’re working on desktop windows which has the ACM resampler, and if your audio is 16 bit. But if you’re dealing with IEEE floating point audio and you’re on Windows Azure or a non-windows platform, you might not have access to built-in codecs.

So in this post, I want to explain how we can use a C# port of the Cockos WDL resampler to perform input driven resampling of our audio.

I made the port so NAudio could provide the WdlResamplingSampleProvider which provides a fully managed output-driven resampler.

But I never made the underlying WdlResampler class public. That’s because I wanted to clean up it’s interface a bit before sharing it. However, it is fully self-contained, so there is no difficulty just copying the code from this class and including it in your own project.

Let’s see how we can use it. I’ll simulate input driven resampling, by reading 1000 samples at a time from an input file, but this audio could just as easily be coming from the soundcard via WasapiCapture. And then I’ll pass each block of input samples into the WdlResampler, and ask to read out whatever’s available. I’ll use an output buffer that’s bigger than I need so I’ll always fully read out resampled audio.

There’s a few points worth noting here.

  • I’m using AudioFileReader so the input audio is floating point samples. The WdlResampler only works with floating point
  • The WdlResampler has a SetFeedMode method that allows us to specify we are in input driven mode
  • In this example I’m downsampling to 16kHz (assuming the input file is >16kHz)
  • The WdlResampler uses counts of sample “frames” rather than number of samples. In a stereo file, a sample frame is two samples – a left and a right sample. I haven’t checked if the resampler supports changing the channel count – I suspect not, but you can easily change channel count as a separate step.

 

int outRate = 16000;
var inFile = @"E:\Some Input File.mp3";
var outFile = @"E:\Resampled Output File.wav";

using (var reader = new AudioFileReader(inFile))
using (var writer = new WaveFileWriter(outFile, WaveFormat.CreateIeeeFloatWaveFormat(outRate, reader.WaveFormat.Channels)))
{
    var read = 0;
    var buffer = new float[1000];
    var resampler = new WdlResampler();
    resampler.SetMode(true, 2, false);
    resampler.SetFilterParms();
    resampler.SetFeedMode(true); // input driven
    resampler.SetRates(reader.WaveFormat.SampleRate, outRate);

    
    while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        int framesAvailable = read / reader.WaveFormat.Channels;
        float[] inBuffer;
        int inBufferOffset;
        int inNeeded = resampler.ResamplePrepare(framesAvailable, writer.WaveFormat.Channels, out inBuffer, out inBufferOffset);
     
        Array.Copy(buffer,0,inBuffer,inBufferOffset,inNeeded * reader.WaveFormat.Channels);
        
        int inAvailable = inNeeded;
        float[] outBuffer = new float[2000]; // plenty big enough
        int framesRequested = outBuffer.Length / writer.WaveFormat.Channels;
        int outAvailable = resampler.ResampleOut(outBuffer, 0, inAvailable, framesRequested, writer.WaveFormat.Channels);

        writer.WriteSamples(outBuffer, 0, outAvailable * writer.WaveFormat.Channels);
    }
}

So that’s how you can perform input driven resampling in fully managed C#. Admittedly it is a little more cumbersome than the equivalent output driven code:

int outRate = 16000;
var inFile = @"E:\some input file.mp3";
var outFile = @"E:\resampled wdl.wav";
using (var reader = new AudioFileReader(inFile))
{
    var resampler = new WdlResamplingSampleProvider(reader, outRate);
    WaveFileWriter.CreateWaveFile16(outFile, resampler);
}

But hopefully this helps people who are stuck with how to perform input driven resampling of IEEE floating point samples on systems without built-in codecs. I’ve provided a gist with the full code here.

And I’ll probably make the WdlResampler class in NAudio public in the future so you won’t need to take a copy.

Note – if you are using WasapiCapture or WasapiLoopbackCapture and you are wondering how to turn the incoming byte array into a float array, then check out my article here for some techniques – the WaveBuffer one is probably the best choice.

Want to get up to speed with the the fundamentals principles of digital audio and how to got about writing audio applications with NAudio? Be sure to check out my Pluralsight courses, Digital Audio Fundamentals, and Audio Programming with NAudio.
Vote on HN
comments powered by Disqus