Storing WP7 recorded audio as WAV format streams
The Microsoft audio recording example shows how to record audio, but it just gets a PCM encoded stream of samples – if you want to send this audio stream off somewhere else, to be played later, it needs a proper header.
Below I show you how to use a couple of methods I’ve written (WriteWavHeader and UpdateWavHeader), and I include their implementation.
Assuming you start with the Microsoft sample, when you initiate the audio recording you initialize a memory stream into which the samples will be written:
{
_microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
// Allocate memory to hold the audio data
_recordingBuffer = new byte[_microphone.GetSampleSizeInBytes(_microphone.BufferDuration)];
_recordingStream.SetLength(0);
WriteWavHeader(_recordingStream, _microphone.SampleRate);
_microphone.Start();
}
Note the call to WriteWavHeader to write the wav header to the start of the audio stream.
When the recording finishes, you update the header (because we need to fill in fields based on the data length):
{
_microphone.Stop();
UpdateWavHeader(_recordingStream);
}
Here are the implementations of the WriteWavHeader and UpdateWavHeader methods. The comments come from this web page describing the wav header file format.
{
const int bitsPerSample = 16;
const int bytesPerSample = bitsPerSample / 8;
var encoding = System.Text.Encoding.UTF8;
// ChunkID Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form).
stream.Write(encoding.GetBytes("RIFF"), 0, 4);
// NOTE this will be filled in later
stream.Write(BitConverter.GetBytes(0), 0, 4);
// Format Contains the letters "WAVE"(0x57415645 big-endian form).
stream.Write(encoding.GetBytes("WAVE"), 0, 4);
// Subchunk1ID Contains the letters "fmt " (0x666d7420 big-endian form).
stream.Write(encoding.GetBytes("fmt "), 0, 4);
// Subchunk1Size 16 for PCM. This is the size of therest of the Subchunk which follows this number.
stream.Write(BitConverter.GetBytes(16), 0, 4);
// AudioFormat PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression.
stream.Write(BitConverter.GetBytes((short)1), 0, 2);
// NumChannels Mono = 1, Stereo = 2, etc.
stream.Write(BitConverter.GetBytes((short)1), 0, 2);
// SampleRate 8000, 44100, etc.
stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
// ByteRate = SampleRate * NumChannels * BitsPerSample/8
stream.Write(BitConverter.GetBytes(sampleRate * bytesPerSample), 0, 4);
// BlockAlign NumChannels * BitsPerSample/8 The number of bytes for one sample including all channels.
stream.Write(BitConverter.GetBytes((short)(bytesPerSample)), 0, 2);
// BitsPerSample 8 bits = 8, 16 bits = 16, etc.
stream.Write(BitConverter.GetBytes((short)(bitsPerSample)), 0, 2);
// Subchunk2ID Contains the letters "data" (0x64617461 big-endian form).
stream.Write(encoding.GetBytes("data"), 0, 4);
// NOTE to be filled in later
stream.Write(BitConverter.GetBytes(0), 0, 4);
}
public void UpdateWavHeader(Stream stream)
{
if (!stream.CanSeek) throw new Exception("Can't seek stream to update wav header");
var oldPos = stream.Position;
// ChunkSize 36 + SubChunk2Size
stream.Seek(4, SeekOrigin.Begin);
stream.Write(BitConverter.GetBytes((int)stream.Length - 8), 0, 4);
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 This is the number of bytes in the data.
stream.Seek(40, SeekOrigin.Begin);
stream.Write(BitConverter.GetBytes((int)stream.Length - 44), 0, 4);
stream.Seek(oldPos, SeekOrigin.Begin);
}
May 2nd, 2011 - 02:25
Thanks a lot! I was having trouble saving the raw microphone PCM to isolated storage and then playing it back. I was only ever to make it work once per execution of my program. After that, the sound was corrupted. But by simply storing the WAV instead, everything works as expected.
June 14th, 2011 - 14:56
That was a breath of fresh air.. exactly what I was looking for. Works a treat.
Thank you.
June 24th, 2011 - 09:56
thanx a lot!
July 5th, 2011 - 05:17
Hello Damian, cool post but i am running into some errors using this. could you please show the code in full because i find it a little confusing as the variables used in the microphone link you provided are a bit different from what you used so i do not know if they have to be created or they replace some of the things from the microphone link. Please could you show from start to finish? Thank You
August 18th, 2011 - 04:18
Thank you for the post, but I’m having a problem here. when a play the recorded sound on wp7 it just plays fine but when I upload the recorded sound using a web service and play it on a pc it plays the sound too fast even after adding the header like in your post it remained the same… what could be the cause of this problem??
August 24th, 2011 - 18:50
I’m also having problems with this… I seem to get a lot of garbage in the file after the header. :/
August 24th, 2011 - 19:07
Sorry no I didn’t read propperly! DUH! It works great!!!
January 15th, 2012 - 11:34
hey….some one please tell me the name of the wave file created….where can i get that….or tell me how to access the wave file created..please help me out..
March 6th, 2012 - 13:32
God bless
April 12th, 2012 - 13:02
Thanks for the explanation and the code! Excellent work.
April 25th, 2012 - 04:20
Hi, do you think its possible to store the recordings into a local database in wp7 instead of isolated storage?? thanks =)
May 16th, 2012 - 03:48
Great, saved me a good amount of time trying to figure that out myself!
June 12th, 2012 - 07:06
Thank you very much!!!
July 27th, 2012 - 03:07
Thank a lot,but it seems like this code does not work in windows system,how can I make it work,should I change something,?
November 1st, 2012 - 09:44
hi,damian, pcm encoded stream can be convert into mp3(mpegAudiolayer3) ?