Tuesday 3 June 2008

Introducing NAudio - .NET Audio Toolkit

Although I have been developing NAudio for six years now, I have never really written much by way of documentation for it. This post is the first in a series that will attempt to rectify this shortcoming.

What Is NAudio?

NAudio is an open source .NET audio toolkit, containing a variety of classes for working with audio files and devices in Windows. Its key features are:

  • Standard WAV file reading and writing
  • A pluggable wave streaming architecture including various mixers, format conversion and some basic effects
  • Full access to installed ACM codecs
  • Individual WAV samples can be accessed
  • Audio playback and recording using the WinMM APIs
  • Handling compressed WAV files using
  • Extensive MIDI file handling and processing
  • Ability to receive MIDI events
  • Soundfont reading
  • OGG file encoding
  • Various experimental DSP functions
  • A selection of Windows Forms controls for audio related GUIs
  • Cakewalk drum map reading
  • RGCAudio sfz sampler file reading
  • Audio output using DirectSound
  • Audio output using ASIO
  • Vista CoreAudio API wrappers
  • DirectX Media Object wrappers

All this resides in NAudio.dll which can easily be linked to and used from your projects. Not all the features listed are production quality yet, but I will be writing up tutorials on some of the more mature parts of the framework in the future.

In addition to this, NAudio is the basis for a variety of utilities, some of which have moved off into their own CodePlex projects.

  • AudioFileInspector is a debugging tool that allows you to simply get textual information on WAV files, MIDI files and SoundFonts.
  • MIDI File Converter can convert MIDI files between different MIDI file types (useful for fixing up some drum loops to work with different sequencers)
  • MIDI File Mapper can batch process MIDI files remapping their notes, and performing other transformations on the data. Very useful for converting MIDI drum loops to work with different drum samplers.
  • MIDI File Splitter is another utility for working with drum loops. It can split long MIDI files at markers.
  • MixDiff demonstrates how to use the Wave Playback features of NAudio. It is intended to allow you to perform double-blind listening tests on different versions of a mix you are working on. It has yet to be properly released.
  • NAudioDemo is still very incomplete, but the intention is to create an application that showcases all the features of NAudio, and so serve as a coding tutorial.

Rationale

When .NET came out I was very excited that Microsoft had created a technology that combined the strengths of MFC, Visual Basic 6 and Java, whilst having very few of their limitations. I wanted to do as much future development in .NET as possible. The only trouble was, a lot of the applications I was writing were dealing with various audio file formats, or processing audio and playing it back.

It seemed that very few open source projects dealing with audio were around back then, and today there still are not that many. So I decided to create my own library, slowly adding whatever bits and pieces I needed to it. I fully expected Microsoft to come along and blow most of it away with a low-level audio stack for .NET but that never happened. And while a handful of other .NET audio libraries have showed up, none seem to have quite the same focus.

History

I started work on NAudio shortly after .NET 1.0 first came out. I had a Pentium III 450MHz PC back then, and one of the first things I implemented was the interop to the WinMM mixer APIs, complete with forms that auto-populated themselves with sliders. I haven't used the code much since, and I expect I would cringe if I looked back at it now.

The next task was to read Wave Files and play them back using the WaveOut APIs. This took me into a whole world of pain - interop in a garbage collected language. Eventually I got it all working and could play 44.1kHz wave files at a latency of around 400ms.

It was about this time I set up a SourceForge project for NAudio but I never really got on with using CVS.

I also ported a lot of code I had written for working with SoundFonts to C#. The only thing I couldn't get working in C# was the API for loading SoundFonts into the Soundblaster Live card. It used pointers to tables of function pointers, which was out of my depth for interop. I did create a managed C++ library for it though, but before long I had become fed up with Soundblaster's appalling driver support in XP and bought a new soundcard so that part of NAudio came to an end.

The next few years were spent slowly ramping up the MIDI library, and enhancing the Wave playback. I also added DirectSound ouput option. In 2005 I started work at a new company, and realised that a lot of the work I had done in NAudio could be useful for one of their products. I started working in earnest on getting ACM working well which eventually allowed me to use NAudio to form the basis of the audio mixing library for one of my company's key products. It performs well, mixing upwards of 30 concurrent streams of audio (albeit at 8kHz sample rate), and passing through various ACM effects. We work at a latency of around 200ms with no noticeable stuttering even on fairly modest PCs (Pentium IV 2Ghz).

During this time some features were added to NAudio that were eventually  dropped. I ported JavaLayer (a Java MP3 decoder) over to C#, but got fed up of constantly needing to update it. Version 1 is out now though, so perhaps it might be worth another go. I also attempted to create wrappers for Steinberg's VST, but the job took longer than I was hoping, and in any case, a promising new project has been created by someone else - VST.NET.

In more recent times, I have added an ASIO output option, and am currently working on adding Vista's CoreAudio WASAPI interface, and access to DirectX Media Objects. I am increasingly trying to write NUnit unit tests for the new features I am developing.

As for the future, NAudio will continue to progress based on the features I need for the applications I am working on. I have no shortage of ideas for audio utilities, but sadly time is at a premium. I'm hoping to create a new release in the near future if I can get the Resampler DMO object working nicely with WASAPI.

24 comments:

zoltrain said...

Hey Mark,

Good work with teh Library, I've been looking around on the net for libraries such as yours for a project I'm working on.

It seems these libraries are few and far between when it comes to Audio DSP for C# which is a little dissapointing, I did think that old m$ would have extended some of the .net api to include some fun audio functions but alas we've all been left wanting.

I'm quite interested in your library, and the one thing I did noteice was teh lack of documentation;-) but that happens to the best of us right, no time.

So me thinks I'll go straight to the source to get teh info I need.

what I'm wanting to accomplish is to take 4 wav files and smack them together merging the audio into one file, I had a look at the wavereader/wavemixerstream32 objects, and tried them but with little success, since its your library thought you would probably knwo the best way of doing such a thing. As I really don't want to write my own as processing WAVE files can be a total b$tch.

Anyhelp would be much appreciated.

Cheers,

Ryan

Unknown said...

Hi Kronk,
Assuming your input files are the same format as each other and are 16 bit PCM, you can do this:

WaveFileReader reader1 = new WaveFileReader(file1);
WaveFileReader reader2 = new WaveFileReader(file2);
etc.
then
WaveChannel32 channel1 = new WaveChannel32(reader1);
WaveChannel32 channel2 = new WaveChannel32(reader2);

then you can make a WaveMixer32 and put the channels in as input.

then you probably want a Wave32to16stream to get back into 16 bit world before using a WaveFileWriter to make your WAV file.

Hopefully this explanation makes sense to you. I want to expand on the NAudio documentation in the future, but there is still a lot to do. Let me know if you have any further problems.

Thomas Holloway said...

Woo! I am excited to using this API.

NingDev said...

Dear Mark,
Thanks for your efforts, providing the good Audio library for dot net. I have a question about how to control the line in signals, I want to play the line-in signals directly. But I a new for multimedia scope. Could you give me a help? Could you give me a demo for this. Thank you very much!

Anonymous said...

Is there a way (using your library or without it) to convert MIDI files in wav tracks in c#? I know this question can appear a little generic and ambiguous, but this is wath permits my poor english at moment. For a project I'm working on, I need to do such conversion as fast as possible. I have heared about SoundFonts and i think those may be a good direction... can you help me to find some documentation or C# examples? Thanks in advance.

Unknown said...

Hi Gilberto,

Unfortunately NAudio does not currently support MIDI to WAV. It is one feature I would love to add, but would require some serious development time either to create a SoundFont player or to host VSTi's in managed code.

Anonymous said...

Hello Mark,

You have made a good mark with NAudio. Well appreciated and wonderful effort to bring out such a handy library.

I tried to replicate the "Introduction to Using NAudio " sample as provided by you. I want to convert a 24 Bit Wave file to 16 Bit wave format and play it. Unfortunately an unhandled exception was thrown and it read :

"Mm Exception was unhandled"
Exception message was : "Acm Not Possible calling acmStreamOpen"

Any help on this would be well appreciated Mark.

Thanks
Karthik

My Input : 24 Bit Wave file

Unknown said...

Hi Karthic,
NAudio does not support 24 bit audio directly yet. I hope to add that in a future version. You exception is because NAudio is looking for an ACM codec to do the conversion and you don't have one installed.

Karthik said...

Hi Mark,

Thanks for your reply. I was waiting for them :). Well I appreciate your current work though as of now NAudio does not directly support 24 Bit Wav Files.

A few things to clarify:
1) As of now the NAudio does not support 24 Bit Wav File directly, does this mean NAudio does not process any functionalities if wav is above 16 Bit ( including conversion of 24 Bit Wave file to 16 Bit Wav File) ?

2)I am mainly looking forward to convert a 24 Bit Wav File ( or higher ) to 16 Bit Wav File. I understand from your comment that ACM codec is not installed, will conversion work if I have ACM Codec installed? Can you please suggest any Codec which can be downloaded and installed if it helps for conversion?

Your feedback and suggestions will be very helpful.

Thanks in advance.

Cheers,
Karthik

Unknown said...

Hi Karthik,
You do have the ACM codec installed, but it doesn't do 24 bit either. NAudio can deal with 32 bit floating point audio. To convert you would need to create a derived WaveStream class. In the Read method, for every two bytes requested, read three from the source file. Convert those 3 values into an integer (use BitConverter) and then scale it down to 16 bit range, before writing it into the output buffer. You could look at the Wave32To16 (that might not be its exact name) converter stream that is in NAudio for an example.

Unknown said...

Mark,
thanks for all the work. I'm trying to do something a bit different. I want to take an analog input signal (processed through a NI DAQ card and inputted to my program as a 2D array of voltage values) and convert it to sound. Based on what i've seen your dll, it looks like i want to create an input stream. is this possible with the methods available?
thanks,

Jim

Unknown said...

ps. I should add that i bring in a fresh array every 150ms. the array is 150 samples in size. I've thought of trying to create a wav file format, but the system needs to update continuously while data is being collected.

Unknown said...

Hi Jim,
yes this is possible. Create a class derived from WaveStream. In the Read method, make return the raw bytes from your input.

You also need to override WaveFormat to tell NAudio what format to use, but apart from that you are ready to play it with the WaveOut class.

Length and Position don't really matter as you won't support repositioning while streaming.

noqqu said...

Hi Mark,
i'm trying to play one file on the front output of a sound card and another file on the rear output of the same sound card. Can you tell me how can I achieve this?

Thanks for your help

Anonymous said...

Hi Mark,

Great tool. I've just about got it working for our needs, but I get an error on the final step.

Basically we need to mix multiple wav files into a single file. I've coded against your library as you had suggested to Kronk back on 14 June 2008. Everything looks good until the WaveFileWriter tries to write the resulting mixed file. It returns the error below. Interestingly, it does start to write the file; the resulting file is exactly 4KB on disk which is the 1st buffer read size in the CreateWaveFile function.

? ex.ToString
System.ArgumentException: Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.
at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
at NAudio.Wave.WaveFileReader.Read(Byte[] array, Int32 offset, Int32 count)
at NAudio.Wave.WaveChannel32.Read(Byte[] destBuffer, Int32 offset, Int32 numBytes)
at NAudio.Wave.WaveMixerStream32.Read(Byte[] buffer, Int32 offset, Int32 count)
at NAudio.Wave.Wave32To16Stream.Read(Byte[] destBuffer, Int32 offset, Int32 numBytes)
at NAudio.Wave.WaveFileWriter.CreateWaveFile(String filename, WaveStream stream)
at UltPursuitData.MediaFile.GetTestSoundFile(HttpServerUtility HTTPServer)

Here's my test code:
Dim strFile As String = FileRoot & "mediafiles\bugsbunny3.wav" 'test file
Dim strFile2 As String = FileRoot & "mediafiles\hankhill3.wav" 'test file
Dim m As New NAudio.Wave.WaveMixerStream32
Dim r As New NAudio.Wave.WaveFileReader(strFile)
Dim r2 As New NAudio.Wave.WaveFileReader(strFile2)
Dim c As New NAudio.Wave.WaveChannel32(r)
Dim c2 As New NAudio.Wave.WaveChannel32(r2)

'add sound files to mixer
m.AddInputStream(c)
m.AddInputStream(c2)

Dim x As New NAudio.Wave.Wave32To16Stream(m)

strPath = FileRoot & "mediafiles\testsoundfile.wav"

This is the line that causes error:
NAudio.Wave.WaveFileWriter.CreateWaveFile(strPath, x)

Any advice?

Thanks!

Randy

J. Pablo Fernández said...

I'm trying to do the same thing as Max, mix two wavs and getting the same result.

Unknown said...

this problem has been fixed and checked in at Codeplex now

Anonymous said...

Hi Mark
I got to playing with NAudio and I got it to work. All I want to do is record a simple 15 sec. sound byte. I discovered a problem with overloading WaveIn in version 1.3.8.0

waveInStream = new WaveIn(44100,2);

I cant overload in this version. VS. returns a does not accept 2 overloads error. It works ok in default if I leave out the overloads.

waveInStream = new WaveIn();

If I use version 1.3.5.0 the overloads work ok.

Unknown said...

@anonymous - you need to set the WaveFormat property of WaveIn. The signature was changed in 1.3.8

Anonymous said...

Hi

I'm interesed in playing SoundFont notes with NAudio but can't find any example on the whole internet.

Can anyone please help me?

Thanks.

ivg2003@hotmail.com

Unknown said...

you would need to build a software synthesiser in order to play sounds from a SoundFont with NAudio. this would be quite a big undertaking

Philip Jones said...

Hi Mark,

I found NAudio yesterday and I have been trying it out for my MIDI applications. It is really good and easy to use even though I have little experience of C# or .NET
Can you offer any hints on how to send a MIDI SysEx string?

thanks
Philip J

edoluz said...

Hello Mark.
Thank you very much for your articles.
I really hope you can answer to this question:
- is there a way to change volume of a recorded track (voice) with NAUDIO? I'm not thinking to normalize it, but with something like an "audio compressor" make the loudest parts of the track quieter, enabling the volume of the whole thing to be raised without clipping.

We're looking for this so we can have all the recoded voices at the same volume (for example 0 dB).

Best regards,

Edo

Unknown said...

NAudio does have a compressor algorithm, in the SimpleCompressor class, but to be honest, I need to make this a bit easier to work with, by making it implement the ISampleProvider interface.