Thursday, 21 April 2011

How to Use WaveFileWriter

In this post I will explain how to use the WaveFileWriter class that is part of NAudio. I will discuss how to use it now in NAudio 1.4 and mention some of the changes that will be coming for NAudio 1.5.

The purpose of WaveFileWriter is to allow you to create a standard .WAV file. WAV files are often thought of as containing uncompressed PCM audio data, but actually they can contain any audio compression, and are often used as containers for telephony compression types such as mu-law, ADPCM, G.722 etc.

NAudio provides a one-line method to produce a WAV file if you have an existing WaveStream derived class that can provide the data (in NAudio 1.5 it can be an IWaveProvider).

string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".wav");
WaveFormat waveFormat = new WaveFormat(8000, 8, 2);
WaveStream sourceStream = new NullWaveStream(waveFormat, 10000);
WaveFileWriter.CreateWaveFile(tempFile, sourceStream);

In the above example, I am using a simple utility class as my source stream, but in a real application this might be the output of a mixer, or the output from some effects or a synthesizer. The most important thing to note is that the Read method of your source stream MUST eventually return 0, otherwise your file will keep on writing until your disk is full! So beware of classes in NAudio (such as WaveChannel32) that can be configured to always return the number of bytes asked for from the Read method.

For greater control over the data you write, you can simply use the WriteData method (renamed to “Write” in NAudio 1.5, as WaveFileWriter will inherit from Stream). WriteData assumes that you are providing raw data in the correct format and will simply write it directly into the data chunk of the WAV file. This is therefore the most general purpose way of writing to a WaveFileWriter, and can be used for both PCM and compressed formats.

byte[] testSequence = new byte[] { 0x1, 0x2, 0xFF, 0xFE };
using (WaveFileWriter writer = new WaveFileWriter(fileName, waveFormat))
{
    writer.WriteData(testSequence, 0, testSequence.Length);
}

WaveFileWriter has an additional constructor that takes a Stream instead of a filename, allowing you to write to any kind of a stream (for example, a MemoryStream). Be aware though that when you dispose the WaveFileWriter, it disposes the output stream, so use the IgnoreDisposeStream utility class to wrap the output stream if you don’t want that to happen.

One of the most commonly used bit depths for PCM WAV files is 16 bit, and so NAudio provides another WriteData overload (to be called WriteSamples in NAudio 1.5) that allows you to supply data as an array of shorts (Int16s). Obviously, this only really makes sense if you are writing to a 16 bit WAV file, but the current implementation will also try to scale the sample value for different bit depths.

short[] samples = new short[1000];
// TODO: fill sample buffer with data
waveFileWriter.WriteData(samples, 0, samples.Length);

Another consideration is that very often after applying various audio effects (even as simple as changing the volume), the audio samples stored as 32 bit floating point numbers (float or Single). To make writing these to the WAV file as simple as possible, a WriteSample function is provided, allowing you to write one sample at a time. If the underlying PCM format is a different bit depth (e.g. 16 or 24 bits), then the WriteSample function will attempt to convert the sample to that bit depth before writing it to a file. NAudio 1.5 will also feature a WriteSamples function to allow arrays of floating point samples to be written. The following example shows one second of a 1kHz sine wave being written to a WAV file using the WriteSample function:

float amplitude = 0.25f;
float frequency = 1000;

for (int n = 0; n < waveFileWriter.WaveFormat.SampleRate; n++)
{
    float sample = (float)(amplitude * Math.Sin((2 * Math.PI * n * frequency) / waveFileWriter.WaveFormat.SampleRate));
    waveFileWriter.WriteSample(sample);
}

Wednesday, 20 April 2011

NAudio 1.4 Release Notes

It has been far too long coming I know, but I have finally got round to uploading a release candidate for NAudio 1.4. Assuming no glaring bugs are reported in the next week or so, I’ll drop the “RC” from the title.

So what’s new in this latest release?

  • Major interop improvements to support native x64. Please note that I have not in this release changed the dll’s target platform away from x86 only as I don’t personally have an x64 machine to test on. However, we are now in a state where around 95% of the interop should work fine in x64 mode so feel free to recompile for “any CPU”. You should also note that if you do run in native x64 mode, then you probably will find there are no ACM codecs available, so WaveFormatConversionStream might stop working – another reason to stay targetting x86 for now.
  • There have also been major enhancements to MP3 File Reader, which is the main reason for pushing this new release out. Please read this post for more details as this is a breaking change – you no longer need to use a WaveFormatConversionStream or a BlockAlignReductionStream.
  • More examples IWaveProvider implementers have been added, including the particularly useful BufferedWaveProvider which allows you to queue up buffers to be played on demand.
    • BufferedWaveProvider
    • Wave16toFloatProvider
    • WaveFloatTo16Provider
    • WaveInProvider
    • MonoToStereoProvider16
    • StereoToMonoProvider16
    • WaveRecorder
  • The NAudioDemo project has been updated to attempt to show best practices (or at least good practices) of how you should be using these classes.
  • The NAudioDemo project also now demonstrates how to select the output device for WaveOut, DirectSoundOut, WasapiOut and AsioOut.
  • WaveChannel32 can now take inputs of more bit depths – 8, 16, 24 and IEEE float supported. NAudioDemo shows how to play back these files.
  • A general spring clean removed a bunch of obsolete classes from the library.
  • AsioOut more reliable, although I still think there are more issues to be teased out. Please report whether it works on your hardware.
  • WaveFileReader and WaveFileWriter support for 24 and 32 bit samples
  • Allow arbitrary chunks to appear before fmt chunk in a WAV file
  • Reading and writing WAV files with Cues
  • Obsoleted some old WaveFileWriter and WaveFileReader methods
  • Fixed a longstanding issue with WaveOutReset hanging in function callbacks on certain chipsets
  • Added sequencer specific MIDI event
  • RawWaveSourceStream turns a raw audio data stream into a WaveStream with specified WaveFormat
  • A DMO MP3 Frame Decoder as an alternative to the ACM one
  • Easier selection of DirectSound output device
  • WaveOut uses 2 buffers not 3 by default now (a leftover from the original days of NAudio when my PC had a 400MHz Pentium II processor!).
  • Lots more minor bug fixes & patches applied – see the check-in history for full details

Let me take this opportunity to say thank you to those who have offered many feature suggestions, patches and bug reports. It is also very exciting to see NAudio being used for all kinds of cool applications. I am trying to keep the main project page updated with a list of all of them so get in touch and tell me what you are using NAudio for.

I am sorry I have struggled to keep up with all the questions appearing on the NAudio forums and a growing number of stackoverflow NAudio questions. I try to answer everything, but sometimes I get too far behind and some questions have been left unanswered. NAudio is a spare time project for me, and with the recent birth of my fifth child, time is at a premium, so please be patient! Thanks also to those who have helped out by writing tutorials and answering questions (OpenSebJ and Yuval especially).

Should I Upgrade?

If your app works fine then I actually don’t recommend that you update as there are a (small) number of breaking API changes (mostly for those using Mp3FileReader). However, for all new applications, or if you are prepared to make some small code changes, then I recommend you use the latest.

Using NAudio

Using NAudio is as simple as referencing the NAudio DLL and using whatever pieces you need. (I might have a go at creating a NuGet package for NAudio to make things even easier). To find out how to do something, first look at the NAudioDemo application as this showcases a number of core features. There are also plenty of tutorials on this blog, and on the Documentation tab of the CodePlex site. If they don’t give you an answer, feel free to ask a question on the forums or Stack Overflow.

If you feel that NAudio is missing something you need, remember that NAudio is designed to make it as easy as possible to extend its feature set by inheriting from the base classes and interfaces such as WaveStream or IWaveProvider. In many cases you can add the new feature you need in for the outside.

Future

There were of course, lots of things I wanted to get into this release that will have to wait for the next one. Here’s some of my ideas. Please feel free to get in touch and tell me what would be most beneficial to you.

  • Include an effects framework (based on what I prototyped with Skype Voice Changer)
  • Update the MP3 code to more easily support network streaming
  • Make a Silverlight / WP7 DLL containing the parts of NAudio that do not rely on Windows APIs.
  • ASIO recording support.
  • Possibly switch the source control over to Mercurial to make patching easier
  • And I’ll also try to write a few more tutorials here on my blog, as I know many people have complained about the lack of documentation.