Freigeben über


Playing back Wave files in Silverlight

I’ve been working on Silverlight for a couple of years now, and finally decided to start talking about it in my blog. With the recent beta, we’ve added raw A/V support to Silverlight 3, and that means not only that people will be able to write their own decoders for Silverlight, but also that they can now play back PCM audio through managed code. The easiest way to get PCM data is simply to use a Wave file. Wave files are a container format for multiple audio encodings, so not all files will play back in Silverlight, but the PCM encoded ones will. To help out, I’ve published a sample MediaStreamSource implementation on Code Gallery (https://code.msdn.microsoft.com/wavmss).

The easiest way to try out the code is to open a local wave file from your drive using the OpenFileDialog (C:\Windows\Media has a bunch of little sound bites).

 private void Button_Click(object sender, RoutedEventArgs e) 
{
    OpenFileDialog ofd = new OpenFileDialog(); 

    if (ofd.ShowDialog().Value)
    {
        Stream s = ofd.File.OpenRead();
        WaveMediaStreamSource wavMss = new WaveMediaStreamSource(s);
        try
        {
            me.SetSource(wavMss);
        }
        catch (InvalidOperationException)
        {
            // This file is not valid
        }
    }
} 

This snippet will play the wave file right away. Remember that to get the FileOpenDialog, you need to hook the handler to a user initiated action. An easy way for this, is to put a MediaElement and a button on a page.

 <UserControl x:Class="SampleWave.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <MediaElement Name="me"/>
        <Button Name="b" Width="200" Height="20" Content="Open" Click="Button_Click"/>
    </Grid>
</UserControl>

That’s it, you can now play wave files.

Comments

  • Anonymous
    March 23, 2009
    The comment has been removed

  • Anonymous
    March 24, 2009
    Thanks for the feedback. We're aware of the latency issue and should address it for the release. I don't know how low we'll be able to go (with lower the latencywe need to get the data from the app mre frequently).

  • Anonymous
    April 21, 2009
    Thanks for posting the code for this! We currently are converting our Windows .WAV files to WMA on the server on demand and then playing them via SL2. Unfortunately some of our audio files are in a Dialogic PCM muLaw format. So these go through 2 conversions to be played - first be converted to wav and then converted to WMA. This will simplify things so much!

  • Anonymous
    April 04, 2010
    Hi Gilles, Thanks for this great example. I'm utilising it to try and play a wav file in a seamless loop. But one thing that does not seem to work is the ability to set the Position on the MediaElement back to 0 - it seems to do nothing. I've tried it with the MediaEnded event, and also with a MarkerReached event, but setting the Position seems to do nothing. Is this a bug, or is there a better way of doing this? Thanks, Gary

  • Anonymous
    April 05, 2010
    The comment has been removed

  • Anonymous
    May 09, 2010
    Hi, Thanks for this great library to play this audio type. What i see, it's working only for the PCM wav files, what if the not a PCM wav file, how i can play it , is it possible? Thanks

  • Anonymous
    May 09, 2010
    Silverlight natively only accepts PCM, MP3, WMA and AAC as audio format. If you need to playback a WAV file that is not encoded in one of those formats, then you would need to write or obtain a managed decoder for that format and connect it to your MediaStreamSource.

  • Anonymous
    May 30, 2010
    Hi, Your implementation is great and it almost solved my problems :) Almost because I need to be able to open the stream before it's buffered entirely. Can you point me in the right direction? How to enable audio playback before the file is buffered entirely on the local machine? (or at least get the progress, so user have some visual feedback from the page)

  • Anonymous
    May 30, 2010
    You should be able to enable playback before the file is downloaded by using the async model. As you call BeginGetResponse, you should be able to start getting the stream before it is fully downloaded. As long as your MediaStreamSource implementation doesn't read data past the point of download then you can still play the stream as your downloading it. If you don't have enough data available then you can call ReportSampleProgress to tell the media pipeline that it should start buffering until you get enough data to keep playing.

  • Anonymous
    July 28, 2010
    Hi, This code is incredible!  I've been attempting to modify it for ulaw wav files, but have not yet succeeded.  Is it possible for this wrapper to work with ulaw wave files??  Thanks!

  • Anonymous
    August 02, 2010
    Thanks for the compliments. You should be able to use uLaw as long as you have a decoder available. Since the Silverlight pipeline only accepts PCM, WMA, MP3 and AAC audio, you'll need to decode the file into PCM. First open the file, since it's a WAV it will have the same format as my sample, the format will be uLaw though, when you detect this, you should then feed the compressed data to a uLaw decoder and then feed the output of the decoder to the pipeline. You would initialize the pipeline with the format that the uLaw decoder outputs (it should be PCM). I haven't played with uLaw in particular, but I have tried with other formats and it's fairly simply to connect the different pieces together.

  • Anonymous
    September 06, 2010
    I am having some trouble getting this working correctly I am using the Memorystream object to get the data from a wcf service. The error i get is : at MS.Internal.XcpImports.CheckHResult(UInt32 hr)   at MS.Internal.XcpImports.MediaElement_SetMediaStreamSource(MediaElement mediaElement)   at System.Windows.Controls.MediaElement.SetSource(MediaStreamSource mediaStreamSource) Any help would be appreciated, Thanks

  • Anonymous
    September 07, 2010
    Can you explain a bit how you connect the response from the WCF service to your MediaStreamSource? Do any other the methods of the MediaStreamSource get called? This looks like an error where Silverlight cannot properly connect your MediaStreamSource to the MediaElement.

  • Anonymous
    September 17, 2010
    Awesome Post! I am new to silverlight and this project has helped me beyond expression. I have a question though and would be very grateful for a response. How do I dynamically load an audio file to the WaveMediaStreamSource class? I have tried WebRequest and HTTPWebRequest with no luck as I have received thread and object state errors. I'm new to the WPF world and would appreciate any direction. Many thanks in advance.

  • Anonymous
    September 17, 2010
    Hi, Great blog!!!! Is any way to make the property MediaElement.Position work? I need to set the position where the media starts or change it during playing Thanks in advance.

  • Anonymous
    September 17, 2010
    Michael, You should be able to set the stream from a WebRequest or an HttpWebRequest. When you get the EndResponse callback, you might need to get the stream and then post back to the UI thread to set it in your MediaStreamSource.

  • Anonymous
    September 17, 2010
    Erick, The MediaElement position should work fine with the sample. If you want to set the position before starting playback, you should set your MediaElement AutoPlay property to false. Setup your MediaStreamSource and when you receive the MediaOpened event, you can set the position to where you want it to start and then call play. To change position during playback, you can just change the position property. The following code in my MediaStreamSource sample will get called when the position is changed and the right data will be returned:        /// <summary>        /// Called when asked to seek to a new position        /// </summary>        /// <param name="seekToTime">the time to seek to</param>        protected override void SeekAsync(long seekToTime)        {            if (seekToTime > this.wavParser.Duration)            {                throw new InvalidOperationException("The seek position is beyond the length of the stream");            }            this.currentPosition = this.wavParser.WaveFormatEx.BufferSizeFromAudioDuration(seekToTime) + this.startPosition;            this.currentTimeStamp = seekToTime;            ReportSeekCompleted(seekToTime);        } After the seek has been done, the then Sample request will be for the seeked position. I hope this helps your scenario.

  • Anonymous
    January 25, 2011
    Hi, I have a very urgent reuirement in my project; Playing WAV file in Silverlight application (Silverlight 4 + MVVM Framework + WCF). It's should working like a Media Player. That have a PLAY, PAUSE and STOP BUTTON as well as Seek Bar.I can not convert the file to MP3 or WMV. Is there any way to accomplish the requirement? Thanks in advance for your help.

  • Anonymous
    January 26, 2011
    Hi DG, This should be easily achievable. For a media player functionality, there are a lot of templates and examples that you can use. I don't have any specific links though. For the playback of the wave file. It would be very easy. If you take the code for WavMSS (included in the post) and set it to the source of your MediaElement then you will have wave playback as long as the wave is in PCM format. This would behave just like if you were using an MP3 file (pause, seek, stop) everything should just work.

  • Anonymous
    January 26, 2011
    Hi gillesk, Thank you for your valueable inputs. Here is my Code <MediaElement Name="mediaElement1" /> <TextBlock x:Name="txtDuration" Text="" FontFamily="Verdana" FontSize="26"></TextBlock> <Button x:Name="btnOpen" Click="btnOpen_Click" Height="50" Content="Open Media" Width="95"></Button> <Button x:Name="btnPlay" Content="Play Media" Click="btnPlay_Click" Height="50" Width="95"></Button> <Button x:Name="btnStop" Content="Stop Media" Click="btnStop_Click" Height="50" Width="95"></Button> Code Behind WaveMediaStreamSource wavMss; private void btnOpen_Click(object sender, RoutedEventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); if (ofd.ShowDialog().Value) { Stream s = ofd.File.OpenRead(); wavMss = new WaveMediaStreamSource(s); WavParser wp = new WavParser(s); try { mediaElement1.SetSource(wavMss); mediaElement1.AutoPlay = false; // HOW DO I GET TOTAL DURATION OF MEDIA??????????? txtDuration.Text = "Total Duration : " + wp.Duration.ToString(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } private void btnStop_Click(object sender, RoutedEventArgs e) { mediaElement1.Stop(); } private void btnPlay_Click(object sender, RoutedEventArgs e) { mediaElement1.Play(); } It's a very simple code. I want the Total Duration of WAV file. So that it willl be the Maximum value for slider control; which actually works as SEEK control. <Button x:Name="btnOpen" Click="btnOpen_Click" Height="50" Content="Open Media" Width="95"></Button> <Button x:Name="btnPlay" Content="Play Media" Click="btnPlay_Click" Height="50" Width="95"></Button> <Button x:Name="btnStop" Content="Stop Media" Click="btnStop_Click" Height="50" Width="95"></Button> Code Behind WaveMediaStreamSource wavMss; private void btnOpen_Click(object sender, RoutedEventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); if (ofd.ShowDialog().Value) { Stream s = ofd.File.OpenRead(); wavMss = new WaveMediaStreamSource(s); WavParser wp = new WavParser(s); try { mediaElement1.SetSource(wavMss); mediaElement1.AutoPlay = false; // HOW DO I GET TOTAL DURATION OF MEDIA??????????? txtDuration.Text = "Total Duration : " + wp.Duration.ToString(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } private void btnStop_Click(object sender, RoutedEventArgs e) { mediaElement1.Stop(); } private void btnPlay_Click(object sender, RoutedEventArgs e) { mediaElement1.Play(); } It's a very simple code. I want the Total Duration of WAV file. So that it willl be the Maximum value for slider control; which actually works as SEEK control. Best Regards, DG

  • Anonymous
    January 27, 2011
    Hi DG, To get the duration, you need to query it off the MediaElement after you get the MediaOpened event has fired. You don't even have to worry about the MediaStreamSource for this. Subscribe to the event in the MediaElement: <MediaElement Name="mediaElement1"  MediaOpened="ME_MediaOpened/> Add the EventHandler private void ME_MediaOpened( object sender, RoutedEventArgs e ) {      txtDuration.Text = "Total Duration : " + mediaElement1.NaturalDuration; }

  • Anonymous
    January 28, 2011
    Hi gillesk, Thank you very much. Now, I'm trying to implement it.

  • Anonymous
    February 15, 2011
    hi gillesk your work is price worthy.....thanks alot.....em new to silverlight...when i tried to follow this article i got an error on wavemediastreamsource....how can i refer this in my project....thank u

  • Anonymous
    April 03, 2011
    What would be the causes of a DRM Exception when setting the source to the MediaElement? I've had great success with your wav parser in the past, but the same basic code is now throwing this error. Only difference is the name of the file to load is coming from a wcf service instead of an object in the movie, but I've validated the URL is valid coming from the service. System.Windows.Media.DrmException was unhandled by user code  Message=3220 3220 An error has occurred.  StackTrace:       at MS.Internal.XcpImports.CheckHResult(UInt32 hr)       at MS.Internal.XcpImports.MediaElement_SetMediaStreamSource(MediaElement mediaElement)       at System.Windows.Controls.MediaElement.SetSource(MediaStreamSource mediaStreamSource)

  • Anonymous
    September 19, 2011
    Hi, i'm using your great software to play wav file on SIlverlight 4 client. I've a problem with short audio : the MediaElement plays no audio if the buffer is less than 32K. No error or exception is shown running the code,  do you know if there's a size limit of the buffer to play? Thanks

  • Anonymous
    November 22, 2011
    WaveMediaStreamSource , which namespace this class belongs?

  • Anonymous
    November 22, 2011
    Hi Rami, WaveMediaStreamSource is not part of the silverlight framework. You can download it in source form from the link at the beginning of the post. Thank you.

  • Anonymous
    June 25, 2012
    Hi Giles, How can I load the 8-bit A-Law Encoded Waveformat file(s) in the silverlight using MediaStreamSource class. Your help on this would be greatly appreciated. Cheers Vikas

  • Anonymous
    August 22, 2012
    Great work Giles!!! However, I was wondering, how can i extract the samples while the wave file is playing. I need to extract the samples and boost the volume (using a multiplier) of the wave file. i.e. without using the MediaElement.Volume. Thanks and best regards, Yogesh

  • Anonymous
    August 23, 2012
    Yes, that would be really easy. In the mediastreamsource, as you are getting the samples, you can modify them in any way you like. If you look at the code, the data is handed back to the MediaElement in GetSampleAsync. before giving the data, you can increase the volume, perform some noise cancellation or do any kind of processing that you would like. I had a sample that would gather some of the samples and do a visualization as the sounds were playing.

  • Anonymous
    August 23, 2012
    Exactly Gilles, that's the idea. i.e. to boost the volume in GetSampleAsync. However, i m struggling with reading the samples, perhaps in a byte array, just after "this.wavParser.ProcessDataFromChunk()". Once, i m able to retrieve the sample then i can perform my calculation on it. Any help would be greatly appreciated. Thanks and best regards, Yogesh

  • Anonymous
    August 23, 2012
    You can do a couple of things. You can process the data ahead of time, when you get the stream from your source, you can read the data from the stream and process it, or you can do it in GetSampleAsync. Here's a sample of what it could look like for GetSampleAsync if you wanted to increase the volume by 20%. We read the data into a byte buffer, process it based on the sample size and then wrap it into a MemoryStream to pass back to the MediaElement.        protected override void GetSampleAsync(MediaStreamType mediaStreamType)        {            // Start with one second of data, rounded up to the nearest block.            uint bufferSize = (uint)AlignUp(                this.wavParser.WaveFormatEx.AvgBytesPerSec,                this.wavParser.WaveFormatEx.BlockAlign);            // Figure out how much data we have left in the chunk compared to the            // data that we need.            bufferSize = Math.Min(bufferSize, (uint)this.wavParser.BytesRemainingInChunk);            if (bufferSize > 0)            {                this.wavParser.ProcessDataFromChunk(bufferSize);                // We have a sample, copy it to a byte buffer                byte[] sampleData = new byte[bufferSize];                // Read the data from the stream                this.stream.Read(out sampleData, 0, bufferSize);                if (this.wavParser.WaveFormatEx.BitsPerSample == 8)                {                    // 8-bit are unsigned make them sign process and make them                    // unsigned again                    foreach(byte b in sampleData)                    {                        int signed = (int) b - 128;                        // Increase the volume by 20%                        signed *= 1.2;                        // Move back to unsigned                        signed += 128;                        // Clamp back to a byte                        b = (byte) Math.Min(byte.MinValue, Math.Max(signed, byte.MaxValue));                    }                }                else if (this.wavParser.WaveFormatEx.BitsPerSample == 16)                {                    // 16-bit are signed                    for(int i = 0; i < sampleData.Length; i += 2)                    {                        // Need to verify endianess for this                        int signed = sampleData[i] << 8 + sampleData[i+1];                        // Increase the volume by 20%                        signed = 1.2;                        // Clamp back to a byte                        signed = Math.Min(short.MinValue, Math.Max(signed, short.MaxValue));                        sampleData[i] = (byte) signed >> 8;                        sampleData[i+1] = (byte) signed;                    }                }                // We have processed the data                MemoryStream m = new MemoryStream(sampleData);                // Send out the next sample                MediaStreamSample sample = new MediaStreamSample(                    this.audioDesc,                    this.m,                    0,                    sampleData.Length,                    this.currentTimeStamp,                    this.emptySampleDict);                // Move our timestamp and position forward                this.currentTimeStamp += this.wavParser.WaveFormatEx.AudioDurationFromBufferSize(bufferSize);                this.currentPosition += bufferSize;                / Uncomment to loop forever                // If there are no more bytes in the chunk, start again from the beginning                if (this.wavParser.BytesRemainingInChunk == 0)                {                    this.wavParser.MoveToStartOfChunk();                    this.currentPosition = this.startPosition;                }                */                ReportGetSampleCompleted(sample);            }            else            {                // Report EOS                ReportGetSampleCompleted(new MediaStreamSample(this.audioDesc, null, 0, 0, 0, this.emptySampleDict));            }        }

  • Anonymous
    August 23, 2012
    Thanks a lot Gilles!! This definitely is the way forward. I did copy the Stream to a MemoryStream, but didn't take the wave files bits per sample into account. That's why all my calculation was going wrong. Now I need to find the RMS on the peaks.. Will keep you posted on the outcome. Thanks again!! :) Best regards, Yogesh

  • Anonymous
    January 19, 2013
    Hi, if im usin silverlight to make a audio recorder, and thing is , it doesnt playback, the methods i have have found seem mostly revolve around   "wavemediastreamsource waveMss =new wavemediastreamsource(audiosink.audiodata); mediaelement.setsource(waveMss);" However it does not seem to be working for me

  • Anonymous
    January 22, 2013
    I used WAVmss library to play wave files on silverlight media element and it works great! thanks! It plays the first time I load the file into the element, but when I try and play it a second time it doesn't play. The playback code could not be simpler: private void Play() {    mediaElement.Stop();    mediaElement.Position = TimeSpan.Zero;    mediaElement.Play(); } I have debugged it. The Position property does get set to zero and when I break in after clicking it again its position has moved to the end. I read your answer about playing in loop (blogs.msdn.com/.../playing-back-wave-files-in-silverlight.aspx) but it doesn't fit my needs. I don't want a loopless play if my file. I want the user can hear the file again after it reached the end, can you help me?

  • Anonymous
    January 23, 2013
    You should not have to call Stop. You should be able to simply set the position, that will issue a seek on your MSS and you will start providing samples from the requested position.

  • Anonymous
    January 26, 2013
    Maybe I'm missing something, here is my code where I'm opening wave file and play it successfully for the first time, Here is the file reading and setting as source in my media element:            OpenFileDialog openFileDialog = new OpenFileDialog();            MemoryStream audioSource = new MemoryStream();            if (openFileDialog.ShowDialog() == true)            {                using (FileStream fileStream = openFileDialog.File.OpenRead())                {                    audioSource.SetLength(fileStream.Length);                    fileStream.Read(audioSource.GetBuffer(), 0, (int)fileStream.Length);                }            }            WaveMSS.WaveMediaStreamSource audioStreamSource = new WaveMSS.WaveMediaStreamSource(audioSource);            mediaElement1.SetSource(audioStreamSource); And when the first play over (I know it since I'm getting _MediaEnded event) I can't play this video again. I tried to set the position of the MediaElement but failed to play it again:            mediaElement1.Position = TimeSpan.FromSeconds(0);            mediaElement1.Play(); Instead it sets up the position to the end and pops up again the _MediaEnded event. What can I do?

  • Anonymous
    January 28, 2013
    The comment has been removed

  • Anonymous
    January 29, 2013
    Thanks! It works now!

  • Anonymous
    April 02, 2013
    At ClipFlair Studio (http://studio.clipflair.net) I was having an issue with MediaElement not thowing an event when WAV playback was finished, wonder if this last correction posted above by Gilles (the MoveToChunkOffset) fixes that issue too

  • Anonymous
    September 17, 2014
    hello Gilles..Great post..can you please tell can  silvelight MediaPlayer   play.mov videos.?

  • Anonymous
    September 17, 2014
    Hello Gilles. I have a silverlight project in which media element is used to stream videos from url. url with .mp4 extension is playing but with .mov extension are not working. Here is the sample links : video.ch9.ms/.../DEV-B385_mid.mp4 video.ch9.ms/.../DEV-B385_mid.mov Below is small piece of code for MainPage.xaml.cs of silverlight player private void LoadVideo()    {        var uri = "video.ch9.ms/.../DEV-B385_mid.mov";        mPlayer.Playlist.AutoLoad = true;        mPlayer.Playlist.AutoPlay = true;        mPlayer.Playlist.StretchMode = Stretch.Uniform;        if (mPlayer.Playlist.Items.Count > 0)        {            mPlayer.Playlist.Items[0].MediaSource = new Uri(uri);        }        else        {            PlaylistItem newPlaylistItem = new PlaylistItem            {                MediaSource = new Uri(uri)            };            mPlayer.Playlist.Items.Add(newPlaylistItem);        }        HtmlPage.Document.SetProperty("title", "MyMedia");        tracker.LogVideoStart();    }

  • Anonymous
    September 23, 2014
    MediaElement can't play .MOV, however some newer .MOV files are just .MP4 files, so you could try renaming to .MP4

  • Anonymous
    September 23, 2014
    btw, there is a big issue on MacOS-X that I'm facing at ClipFlair Studio (http://studio.clipflair.net). Silverlight seems to record there at 24-bit by default, while 24-bit playback isn't supported in either MacOS-X or Windows with Silverlight.

  • Anonymous
    November 17, 2014
    The comment has been removed