Saving Microphone stream to wave format in Windows Phone
This article explains how to get audio input from the microphone, convert it to .wav format, and save it to IsolatedStorage.
Article Metadata
Tested with
Compatibility
Article
Introduction
As the microphone's stream buffer provide raw data we need to convert it into a well known file format before saving in order to allow users to play the recorded file everywhere. The easiest solution is to use the .wav file format.
Microphone Sample demonstrates how to get audio input from the microphone in a Silverlight for Windows Phone application. This project is used in this guide and we will be focused on saving process.
Saving into WAV format
As we use isolated storage we need to use the following namespaces:
using System.Windows;
using System.IO.IsolatedStorage;
using System.Windows.Resources;
Then add the following functions to your code:
private void SaveToIsolatedStorage()
{
// first, we grab the current apps isolated storage handle
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
// we give our file a filename
string strSaveName = "myFile.wav";
// if that file exists...
if (isf.FileExists(strSaveName))
{
// then delete it
isf.DeleteFile(strSaveName);
}
// now we set up an isolated storage stream to point to store our data
IsolatedStorageFileStream isfStream =
new IsolatedStorageFileStream(strSaveName,
FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication());
isfStream.Write(stream.ToArray(), 0, stream.ToArray().Length);
// ok, done with isolated storage... so close it
isfStream.Close();
}
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);
}
public void WriteWavHeader(Stream stream, int sampleRate)
{
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);
}
On function delegated to begin the record process include the call to WriteWavHeader() before the microphone.Start() function
private void recordButton_Click(object sender, EventArgs e)
{
...
stream.SetLength(0);
WriteWavHeader(stream, microphone.SampleRate);
// Start recording
microphone.Start();
...
}
After microphone.Stop() add the call to UpdateWavHeader and to SaveToIsolatedStorage
microphone.Stop();
UpdateWavHeader(stream);
SaveToIsolatedStorage();


Contents
Hamishwillee - Nice article
Hi
I gave a very minor subedit and added an abstract. Please check it out.
The use case you've given is saving to IsolatedStorage, and you've also said that you're converting to .wav so file can be used everywhere. The hole in the argument is therefore that once in IsolatedStorage its not available everywhere. I suggest you put an explicit note on how the file an be retrieved.
I don't believe that its possible to save the file to the device media library (correct me if I'm wrong) but another cool thing to show would be uploading to skydrive - or at least point to other articles that show that.
Thank you!
Regards
Hamishhamishwillee 02:40, 4 September 2012 (EEST)
Galazzo - SkyDrive
Hi Hamish,
I created an article on how upload files to SkyDrive and added a link to it.
Hope is useful,
Regards
Sebastianogalazzo 14:38, 6 September 2012 (EEST)
Hamishwillee - Yes is is
Skydrive article looks good!hamishwillee 10:24, 10 September 2012 (EEST)
Dmehers - Your code?
It looks to me like you lifted most of the code from my blog post http://damianblog.com/2011/02/07/storing-wp7-recorded-audio-as-wav-format-streams/
Could you please include an acknowledgement if you do that.dmehers 18:04, 29 March 2013 (EET)
Chintandave er - @Dmehers
Thanks for asking to author and adding your blog link to this article. I think that would be enough.
Thanks, Chintan Dave.
Wiki Moderator.Chintandave er 21:30, 30 March 2013 (EET)
Hamishwillee - Please wait for response before modifying the article
Hey Dmehers
The wiki policy is to acknowledge the source of used materials as you have suggested. However if your article has been reused a lot on Internet without reference it is more than possible that to a casual search this code is "in the public domain" - ie it has no obvious source. In that case he would not necessarily have even seen this source, and even if he did, to assume you were not the original author either. Note that even if the code was in the public domain if it forms a large portion of the article I'd still hope for some acknowledgement that bits of it have been taken from the Internet (if only to explain what value is added though the duplication of material).
I've asked galazzo to comment. Please give him a reasonable amount of time to do so.
Regards
Hamishhamishwillee 06:17, 2 April 2013 (EEST)
Galazzo -
Hello,
here I am, sorry for delay, but it's a very busy endless period.
@Dmehers it's the first time I have a look to the blog you mentioned. @Hamishwillie you got the point. Googling it's easy to find this code from other sources with no author's references, and this is a fact as it's a fact that the code lines are the same. So @Dmehers I'm sure you are the first who wrote that lines, but honestly there is no clear evidence of it surfing the web.
Another fact is that, Inside my articles I always added references when I had the unique source, so I would have no problem to add the refenerce link.
I really don't feel I did what you asserted as wave is a "file format", and there is no way to write a code different from whose published except for function's signature and changing debug messages and comments!
If the file format says that at offset 20 must be written the "file format" and the C# instruction to write that information is :
... sorry, but I didn't believed I could have done wrong to someone.
From my point of view, It didn't thougth that worth neither to change function's signatures, comments or debug messages of a standard code I found as a day someone could say that his way to write the wave file format is unique.
If so, from my developer point of view I would have to add references to all blogs that write about how to treat wave format.
My position could be wrong and I can apologize with all people affected.
Finally, hoping to close this talk:
@Dmehers I've never visited the blog mentioned. Sorry if you thought I grabbed intentionally that code.
@Hamishwillie, @Chintandave, @Dmehers ( all users ) feel free to add all link you think worth to add to references to accomplish to site's policies.galazzo 18:18, 2 April 2013 (EEST)
Hamishwillee - Thanks Sebastiano, Dmehers
I have added an acknowledgement note up the top that should satisfy everyone - it states that the code is from the Internet (unattributed) and that Damien asserts he is the author. These sorts of things do happen, and as long as we all show goodwill (as done here) and appreciate there there is no bad intent no harm is done.
Thanks everyone for being grown up about this.
I will delete all comments on this article next time I visit.hamishwillee 02:16, 3 April 2013 (EEST)
Dmehers -
I'm sure it wasn't intentional. Most of my comments came from the spec I referenced, but if you search for the method call I used then I think it's clear the code originally came from my posting.
https://www.google.com/search?q=%22WriteWavHeader(Stream+stream%2C+int+sampleRate)%22
Really not a big deal - I kind of wish I'd not reacted - was just a bit of a surprise.dmehers 19:35, 5 April 2013 (EEST)
Hamishwillee - All fine
I think we're all happy now. Next time round I'll delete all comments because they have been addressed.hamishwillee 01:45, 6 April 2013 (EEST)