SoundPlayer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / Sys / System / Media / SoundPlayer.cs / 1 / SoundPlayer.cs

                            namespace System.Media { 
    using System;
    using System.IO;
    using System.ComponentModel;
    using System.Runtime.InteropServices; 
    using System.Runtime.Serialization;
    using System.Diagnostics; 
    using System.Threading; 
    using System.Net;
    using System.Globalization; 
    using System.Security.Permissions;
    using System.Security;
    using System.Diagnostics.CodeAnalysis;
 
    /// 
    [ 
    Serializable, 
    ToolboxItem(false),
    SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes"), // This is the first class added to System.Media namespace. 
    SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly"), // vsw 427356
    HostProtection(UI = true)
    ]
    public class SoundPlayer : Component, ISerializable { 

        const int blockSize = 1024; 
        const int defaultLoadTimeout = 10000;// 10 secs 
        private Uri uri = null;
        private string soundLocation = String.Empty; 
        private int loadTimeout = defaultLoadTimeout;

        private object tag = null;
 
        // used to lock all synchronous calls to the SoundPlayer object
        private ManualResetEvent semaphore = new ManualResetEvent(true); 
 
        // the worker copyThread
        // we start the worker copyThread ONLY from entry points in the SoundPlayer API 
        // we also set the tread to null only from the entry points in the SoundPlayer API
        private Thread copyThread = null;

        // local buffer information 
        int currentPos = 0;
        private Stream stream = null; 
        private bool isLoadCompleted = false; 
        private Exception lastLoadException = null;
        private bool doesLoadAppearSynchronous = false; 
        private byte[] streamData = null;
        private AsyncOperation asyncOperation = null;
        private readonly SendOrPostCallback loadAsyncOperationCompleted;
 
        // event
        private static readonly object EventLoadCompleted = new object(); 
        private static readonly object EventSoundLocationChanged = new object(); 
        private static readonly object EventStreamChanged = new object();
 
        /// 
        public SoundPlayer() {
            loadAsyncOperationCompleted =
                new SendOrPostCallback(LoadAsyncOperationCompleted); 
        }
 
        ///  
        public SoundPlayer(string soundLocation) : this() {
            if(soundLocation == null) { 
                soundLocation = String.Empty;
            }
            SetupSoundLocation(soundLocation);
        } 

        ///  
        public SoundPlayer(Stream stream) : this() { 
            this.stream = stream;
        } 

        /**
         * Constructor used in deserialization
         */ 
        /// 
        [ 
            SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // SerializationInfo stores LoadTimeout as an object. 
            SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")        // Serialization constructor needs a Context parameter.
        ] 
        protected SoundPlayer(SerializationInfo serializationInfo, StreamingContext context) {
            foreach(SerializationEntry entry in serializationInfo) {
                switch (entry.Name) {
                    case "SoundLocation" : 
                        SetupSoundLocation((string) entry.Value);
                        break; 
                    case "Stream" : 
                        stream = (Stream) entry.Value;
                        // when we deserialize a stream we have to reset its seek position 
                        // vsWhidbey 180361
                        if (stream.CanSeek) {
                            stream.Seek(0, SeekOrigin.Begin);
                        } 
                        break;
                    case "LoadTimeout" : 
                        this.LoadTimeout = (int) entry.Value; 
                        break;
                } 
            }
        }

        ///  
        public int LoadTimeout {
            get { 
                return loadTimeout; 
            }
            set { 
                if (value < 0) {
                    throw new ArgumentOutOfRangeException("LoadTimeout", value, SR.GetString(SR.SoundAPILoadTimeout));
                }
 
                loadTimeout = value;
            } 
        } 

        ///  
        public string SoundLocation {
            get {
                if (uri != null && uri.IsFile) {
                    FileIOPermission fiop = new FileIOPermission(PermissionState.None); 
                    fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
                    fiop.Demand(); 
                } 
                return soundLocation;
            } 
            set {
                if (value == null)
                    value = String.Empty;
 
                if (soundLocation.Equals(value))
                    return; 
 
                SetupSoundLocation(value);
 
                OnSoundLocationChanged(EventArgs.Empty);
            }
        }
 
        /// 
        public Stream Stream { 
            get { 
                // if the path is set, we should return null
                // Path and Stream are mutually exclusive 
                if (uri != null)
                    return null;
                return this.stream;
            } 
            set {
                if (stream == value) 
                    return; 

                SetupStream(value); 

                OnStreamChanged(EventArgs.Empty);
            }
        } 

        ///  
        public bool IsLoadCompleted { 
            get {
                return isLoadCompleted; 
            }
        }

        ///  
        public object Tag {
            get { 
                return tag; 
            }
            set { 
                tag = value;
            }
        }
 
        /// 
        public void LoadAsync() { 
            // if we have a file there is nothing to load - we just pass the file to the PlaySound function 
            // if we have a stream, then we start loading the stream async
            // 
            if (uri!= null && uri.IsFile){
                Debug.Assert(stream == null, "we can't have a stream and a path at the same time");
                isLoadCompleted = true;
 
                FileInfo fi = new FileInfo(uri.LocalPath);
                if (!fi.Exists) { 
                    throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation); 
                }
 
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
                return;
            }
 
            // if we are actively loading, keep it running
            if (copyThread != null && copyThread.ThreadState == System.Threading.ThreadState.Running) { 
                return; 
            }
            isLoadCompleted = false; 
            streamData = null;
            currentPos = 0;

            asyncOperation = AsyncOperationManager.CreateOperation(null); 

            LoadStream(false); 
        } 

        private void LoadAsyncOperationCompleted(object arg) 
        {
            OnLoadCompleted((AsyncCompletedEventArgs)arg);
        }
 
        // called for loading a stream synchronously
        // called either when the user is setting the path/stream and we are loading 
        // or when loading took more time than the time out 
        private void CleanupStreamData() {
            this.currentPos = 0; 
            this.streamData = null;
            this.isLoadCompleted = false;
            this.lastLoadException = null;
            this.doesLoadAppearSynchronous = false; 
            this.copyThread = null;
            this.semaphore.Set(); 
        } 

        ///  
        public void Load() {
            // if we have a file there is nothing to load - we just pass the file to the PlaySound function
            // if we have a stream, then we start loading the stream [....]
            // 
            if (uri != null && uri.IsFile){
                Debug.Assert(stream == null, "we can't have a stream and a path at the same time"); 
                FileInfo fi = new FileInfo(uri.LocalPath); 
                if (!fi.Exists) {
                    throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation); 
                }
                isLoadCompleted = true;
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
                return; 
            }
 
            LoadSync(); 
        }
 
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // FileIOPermission based on URI path, but path isn't gonna change during scope of Demand
        private void LoadAndPlay(int flags) {
            // bug 16794: when the user does not specify a sound location nor a stream, play Beep
            if (String.IsNullOrEmpty(soundLocation) && stream == null) { 
                SystemSounds.Beep.Play();
                return; 
            } 

            if (uri != null && uri.IsFile) { 
                // VSW 580992: With more than one thread, someone could call SoundPlayer::set_Location
                // between the time LoadAndPlay demands FileIO and the time it calls PlaySound under elevation.
                //
                // Another scenario is someone calling SoundPlayer::set_Location between the time 
                // LoadAndPlay validates the sound file and the time it calls PlaySound.
                // The SoundPlayer will end up playing an un-validated sound file. 
                // The solution is to store the uri.LocalPath on a local variable 
                string localPath = uri.LocalPath;
 
                // request permission to read the file:
                // pass the full path to the FileIOPermission
                FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read, localPath);
                perm.Demand(); 

                // play the path 
                isLoadCompleted = true; 
                System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
 
                System.ComponentModel.IntSecurity.UnmanagedCode.Assert();
                // ValidateSoundFile calls into the MMIO API so we need UnmanagedCode permissions to do that.
                // And of course we need UnmanagedCode permissions to all Win32::PlaySound method.
                try { 
                    // don't use uri.AbsolutePath because that gives problems when there are whitespaces in file names
                    ValidateSoundFile(localPath); 
                    UnsafeNativeMethods.PlaySound(localPath, IntPtr.Zero, NativeMethods.SND_NODEFAULT | flags); 
                } finally {
                    System.Security.CodeAccessPermission.RevertAssert(); 
                }
            } else {
                LoadSync();
                ValidateSoundData(streamData); 
                System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
 
                System.ComponentModel.IntSecurity.UnmanagedCode.Assert(); 
                try {
                    UnsafeNativeMethods.PlaySound(streamData, IntPtr.Zero, NativeMethods.SND_MEMORY | NativeMethods.SND_NODEFAULT | flags); 
                } finally {
                    System.Security.CodeAccessPermission.RevertAssert();
                }
            } 
        }
 
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // WebPermission based on URI path, but path isn't gonna change during scope of Demand 
        private void LoadSync() {
 
            Debug.Assert((uri == null || !uri.IsFile), "we only load streams");

            // first make sure that any possible download ended
            if (!semaphore.WaitOne(LoadTimeout, false)) { 
                if (copyThread != null)
                    copyThread.Abort(); 
                CleanupStreamData(); 
                throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut));
            } 

            // if we have data, then we are done
            if (streamData != null)
                return; 

            // setup the http stream 
            if (uri != null && !uri.IsFile && stream == null) { 
                WebPermission webPerm = new WebPermission(NetworkAccess.Connect, uri.AbsolutePath);
                webPerm.Demand(); 
                WebRequest webRequest = WebRequest.Create(uri);
                webRequest.Timeout = LoadTimeout;

                WebResponse webResponse; 
                webResponse = webRequest.GetResponse();
 
                // now get the stream 
                stream = webResponse.GetResponseStream();
            } 

            if (stream.CanSeek) {
                // if we can get data synchronously, then get it
                LoadStream(true); 
            } else {
                // the data can't be loaded synchronously 
                // load it async, then wait for it to finish 
                doesLoadAppearSynchronous = true; // to avoid OnFailed call.
                LoadStream(false); 

                if(!semaphore.WaitOne(LoadTimeout, false)) {
                    if (copyThread != null)
                        copyThread.Abort(); 
                    CleanupStreamData();
                    throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut)); 
                } 

                doesLoadAppearSynchronous = false; 

                if (lastLoadException != null)
                {
                    throw lastLoadException; 
                }
            } 
 
            // we don't need the worker copyThread anymore
            this.copyThread = null; 
        }

        private void LoadStream(bool loadSync) {
            if (loadSync && stream.CanSeek) { 
                int streamLen = (int) stream.Length;
                currentPos = 0; 
                streamData = new byte[streamLen]; 
                stream.Read(streamData, 0, streamLen);
                isLoadCompleted = true; 
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
            } else {
                // lock any synchronous calls on the Sound object
                semaphore.Reset(); 
                // start loading
                copyThread = new Thread(new ThreadStart(this.WorkerThread)); 
                copyThread.Start(); 
            }
        } 

        /// 
        public void Play() {
            LoadAndPlay(NativeMethods.SND_ASYNC); 
        }
 
        ///  
        public void PlaySync() {
            LoadAndPlay(NativeMethods.SND_[....]); 
        }

        /// 
        public void PlayLooping() { 
            LoadAndPlay(NativeMethods.SND_LOOP | NativeMethods.SND_ASYNC);
        } 
 
        private static Uri ResolveUri(string partialUri) {
            Uri result = null; 
            try {
                result = new Uri(partialUri);
            } catch (UriFormatException) {
                // eat URI parse exceptions 
            }
 
            if (result == null) { 
                // try relative to appbase
                try { 
                    result = new Uri(Path.GetFullPath(partialUri));
                } catch (UriFormatException) {
                    // eat URI parse exceptions
                } 
            }
            return result; 
        } 

        private void SetupSoundLocation(string soundLocation) { 
            // if we are loading a file, stop it right now
            //
            if (copyThread != null) {
                copyThread.Abort(); 
                CleanupStreamData();
            } 
 
            uri = ResolveUri(soundLocation);
 
            this.soundLocation = soundLocation;
            stream = null;
            if (uri == null) {
                if (!String.IsNullOrEmpty(soundLocation)) 
                    throw new UriFormatException(SR.GetString(SR.SoundAPIBadSoundLocation));
            } else { 
                if (!uri.IsFile) { 
                    // we are referencing a web resource ...
                    // 

                    // we treat it as a stream...
                    //
                    streamData = null; 
                    currentPos = 0;
                    isLoadCompleted = false; 
                } 
            }
        } 

        private void SetupStream(Stream stream) {
            if (this.copyThread != null) {
                copyThread.Abort(); 
                CleanupStreamData();
            } 
 
            this.stream = stream;
            this.soundLocation = String.Empty; 
            this.streamData = null;
            this.currentPos = 0;
            isLoadCompleted = false;
            if (stream != null) { 
                uri = null;
            } 
        } 

        ///  
        public void Stop() {
            IntSecurity.SafeSubWindows.Demand();
            UnsafeNativeMethods.PlaySound((byte[]) null, IntPtr.Zero, NativeMethods.SND_PURGE);
        } 

        ///  
        public event AsyncCompletedEventHandler LoadCompleted { 
            add {
                Events.AddHandler(EventLoadCompleted, value); 
            }
            remove {
                Events.RemoveHandler(EventLoadCompleted, value);
            } 
        }
 
        ///  
        public event EventHandler SoundLocationChanged {
            add { 
                Events.AddHandler(EventSoundLocationChanged, value);
            }
            remove {
                Events.RemoveHandler(EventSoundLocationChanged, value); 
            }
        } 
 
        /// 
        public event EventHandler StreamChanged { 
            add {
                Events.AddHandler(EventStreamChanged, value);
            }
            remove { 
                Events.RemoveHandler(EventStreamChanged, value);
            } 
        } 

        ///  
        protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e) {
            AsyncCompletedEventHandler eh = (AsyncCompletedEventHandler) Events[EventLoadCompleted];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        ///  
        protected virtual void OnSoundLocationChanged(EventArgs e) {
            EventHandler eh = (EventHandler) Events[EventSoundLocationChanged];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        ///  
        protected virtual void OnStreamChanged(EventArgs e) {
            EventHandler eh = (EventHandler) Events[EventStreamChanged];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        [ 
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")   // The set of reasons why WorkerThread should fail is not finite
        ]
        private void WorkerThread() {
            try 
            {
                // setup the http stream 
                if (uri != null && !uri.IsFile && stream == null) { 
                    WebRequest webRequest = WebRequest.Create(uri);
 
                    WebResponse webResponse = webRequest.GetResponse();

                    stream = webResponse.GetResponseStream();
                } 

                this.streamData = new byte[blockSize]; 
 
                int readBytes = stream.Read(streamData, currentPos, blockSize);
                int totalBytes = readBytes; 

                while (readBytes > 0) {
                    currentPos += readBytes;
                    if (streamData.Length < currentPos + blockSize) { 
                        byte[] newData = new byte[streamData.Length * 2];
                        Array.Copy(streamData, newData, streamData.Length); 
                        streamData = newData; 
                    }
                    readBytes = stream.Read(streamData, currentPos, blockSize); 
                    totalBytes += readBytes;
                }

                lastLoadException = null; 
            }
            catch (Exception exception) 
            { 
                lastLoadException = exception;
            } 
            catch
            {
                throw;
            } 

            if (!doesLoadAppearSynchronous) 
            { 
                // Post notification back to the UI thread.
                asyncOperation.PostOperationCompleted( 
                    loadAsyncOperationCompleted,
                    new AsyncCompletedEventArgs(lastLoadException, false, null));
            }
            isLoadCompleted = true; 
            semaphore.Set();
        } 
 
        private unsafe void ValidateSoundFile(string fileName) {
            NativeMethods.MMCKINFO ckRIFF = new NativeMethods.MMCKINFO(); 
            NativeMethods.MMCKINFO ck = new NativeMethods.MMCKINFO();
            NativeMethods.WAVEFORMATEX waveFormat = null;
            int dw;
 
            IntPtr hMIO = UnsafeNativeMethods.mmioOpen(fileName, IntPtr.Zero, NativeMethods.MMIO_READ | NativeMethods.MMIO_ALLOCBUF);
 
            if (hMIO == IntPtr.Zero) 
                throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation);
 
            try {
                ckRIFF.fccType = mmioFOURCC('W', 'A','V','E');
                if (UnsafeNativeMethods.mmioDescend(hMIO, ckRIFF, null, NativeMethods.MMIO_FINDRIFF) != 0)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveFile, this.soundLocation)); 

                while (UnsafeNativeMethods.mmioDescend(hMIO, ck, ckRIFF, 0) == 0) { 
                    if (ck.dwDataOffset + ck.cksize > ckRIFF.dwDataOffset + ckRIFF.cksize) 
                        throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
                    if (ck.ckID == mmioFOURCC('f','m','t',' ')) {
                            if (waveFormat == null) {
                                dw = ck.cksize;
                                if (dw < Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX))) 
                                    dw =  Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX));
 
                                waveFormat = new NativeMethods.WAVEFORMATEX(); 
                                byte[] data = new byte[dw];
                                if (UnsafeNativeMethods.mmioRead(hMIO, data, dw) != dw) 
                                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIReadError, this.soundLocation));
                                fixed(byte* pdata = data) {
                                    Marshal.PtrToStructure((IntPtr) pdata, waveFormat);
                                } 
                            } else {
                                // 
                                // multiple formats? 
                                //
                            } 
                    }
                    UnsafeNativeMethods.mmioAscend(hMIO, ck, 0);
                }
 
                if (waveFormat == null)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 
 
                if (waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_PCM &&
                    waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM && 
                    waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported));

            } finally { 
                    if (hMIO != IntPtr.Zero)
                        UnsafeNativeMethods.mmioClose(hMIO, 0); 
            } 
        }
 
        private static void ValidateSoundData(byte[] data) {
            int position = 0;
            Int16 wFormatTag = -1;
            bool fmtChunkFound = false; 

            // the RIFF header should be at least 12 bytes long. 
            if (data.Length < 12) 
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
            // validate the RIFF header
            if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
            if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') 
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
            // we only care about "fmt " chunk 
            position = 12;
            int len = data.Length; 
            while (!fmtChunkFound && position < len - 8) {
                if (data[position] == (byte)'f' && data[position + 1] == (byte)'m' && data[position + 2] == (byte)'t' && data[position+3] == (byte)' ') {
                    //
                    // fmt chunk 
                    //
                    fmtChunkFound = true; 
                    int chunkSize = BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]); 
                    //
                    // get the cbSize from the WAVEFORMATEX 
                    //

                    int sizeOfWAVEFORMAT = 16;
                    if (chunkSize != sizeOfWAVEFORMAT) { 
                        // we are dealing w/ WAVEFORMATEX
                        // do extra validation 
                        int sizeOfWAVEFORMATEX = 18; 

                        // make sure the buffer is big enough to store a short 
                        if (len < position + 8 + sizeOfWAVEFORMATEX - 1)
                            throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));

                        Int16 cbSize = BytesToInt16(data[position+8 + sizeOfWAVEFORMATEX - 1], 
                                                    data[position+8 + sizeOfWAVEFORMATEX-2]);
                        if (cbSize + sizeOfWAVEFORMATEX != chunkSize) 
                            throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 
                    }
 
                    // make sure the buffer passed in is big enough to store a short
                    if(len < position + 9)
                        throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
                    wFormatTag = BytesToInt16(data[position+9], data[position+8]); 

                    position += chunkSize + 8; 
                } else { 
                    position += 8 + BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]);
                } 
            }

            if (!fmtChunkFound)
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 

            if (wFormatTag != NativeMethods.WAVE_FORMAT_PCM && 
                wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM && 
                wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported)); 
        }

        private static Int16 BytesToInt16(byte ch0, byte ch1) {
            int res; 
            res = (int) ch1;
            res |= (int) (((int)ch0) << 8); 
            return (Int16) res; 
        }
        private static int BytesToInt(byte ch0, byte ch1, byte ch2, byte ch3) { 
            return mmioFOURCC((char) ch3, (char)ch2, (char) ch1, (char)ch0);
        }

        private static int mmioFOURCC(char ch0, char ch1, char ch2, char ch3) { 
            int result = 0;
            result |= ((int) ch0); 
            result |= ((int) ch1) << 8; 
            result |= ((int) ch2) << 16;
            result |= ((int) ch3) << 24; 
            return result;
        }

        ///  
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] // vsw 427356 
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { 
            if (!String.IsNullOrEmpty(this.soundLocation)) {
                info.AddValue("SoundLocation", this.soundLocation); 
            }

            if (this.stream != null) {
                info.AddValue("Stream", this.stream); 
            }
 
            info.AddValue("LoadTimeout", this.loadTimeout); 
        }
 
        private class IntSecurity {
            // Constructor added because of FxCop rules
            private IntSecurity() {}
 
            private static CodeAccessPermission safeSubWindows;
 
            internal static CodeAccessPermission SafeSubWindows { 
                get {
                    if (safeSubWindows == null) { 
                        safeSubWindows = new UIPermission(UIPermissionWindow.SafeSubWindows);
                    }

                    return safeSubWindows; 
                }
            } 
        } 

        private class NativeMethods { 
            // Constructor added because of FxCop rules
            private NativeMethods() {}

            internal const int WAVE_FORMAT_PCM        = 0x0001, 
            WAVE_FORMAT_ADPCM                       = 0x0002,
            WAVE_FORMAT_IEEE_FLOAT                  = 0x0003; 
 
            internal const int MMIO_READ              = 0x00000000,
            MMIO_ALLOCBUF                           = 0x00010000, 
            MMIO_FINDRIFF                           = 0x00000020;

            internal const int SND_[....] = 0000,
            SND_ASYNC = 0x0001, 
            SND_NODEFAULT = 0x0002,
            SND_MEMORY = 0x0004, 
            SND_LOOP = 0x0008, 
            SND_PURGE = 0x0040,
            SND_FILENAME = 0x00020000, 
            SND_NOSTOP = 0x0010;

            [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
            internal class MMCKINFO { 
                internal int      ckID;
                internal int      cksize; 
                internal int      fccType; 
                internal int      dwDataOffset;
                internal int      dwFlags; 
            }

            [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
            internal class WAVEFORMATEX { 
                internal System.Int16     wFormatTag;
                internal System.Int16     nChannels; 
                internal int              nSamplesPerSec; 
                internal int              nAvgBytesPerSec;
                internal System.Int16     nBlockAlign; 
                internal System.Int16     wBitsPerSample;
                internal System.Int16     cbSize;
            }
        } 

        private class UnsafeNativeMethods { 
            // Constructor added because of FxCop rules 
            private UnsafeNativeMethods() {}
 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern bool PlaySound([MarshalAs(UnmanagedType.LPWStr)] string soundName, IntPtr hmod, int soundFlags);

            [DllImport(ExternDll.WinMM, ExactSpelling=true, CharSet=CharSet.Auto)] 
            internal static extern bool PlaySound(byte[] soundName, IntPtr hmod, int soundFlags);
 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments")] 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern IntPtr mmioOpen(string fileName, IntPtr not_used, int flags); 

            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioAscend(IntPtr hMIO, NativeMethods.MMCKINFO lpck, int flags);
 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioDescend(IntPtr hMIO, 
                                                   [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lpck, 
                                                   [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lcpkParent,
                                                   int flags); 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioRead(IntPtr hMIO, [MarshalAs(UnmanagedType.LPArray)] byte[] wf, int cch);

            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)] 
            internal static extern int mmioClose(IntPtr hMIO, int flags);
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
namespace System.Media { 
    using System;
    using System.IO;
    using System.ComponentModel;
    using System.Runtime.InteropServices; 
    using System.Runtime.Serialization;
    using System.Diagnostics; 
    using System.Threading; 
    using System.Net;
    using System.Globalization; 
    using System.Security.Permissions;
    using System.Security;
    using System.Diagnostics.CodeAnalysis;
 
    /// 
    [ 
    Serializable, 
    ToolboxItem(false),
    SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes"), // This is the first class added to System.Media namespace. 
    SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly"), // vsw 427356
    HostProtection(UI = true)
    ]
    public class SoundPlayer : Component, ISerializable { 

        const int blockSize = 1024; 
        const int defaultLoadTimeout = 10000;// 10 secs 
        private Uri uri = null;
        private string soundLocation = String.Empty; 
        private int loadTimeout = defaultLoadTimeout;

        private object tag = null;
 
        // used to lock all synchronous calls to the SoundPlayer object
        private ManualResetEvent semaphore = new ManualResetEvent(true); 
 
        // the worker copyThread
        // we start the worker copyThread ONLY from entry points in the SoundPlayer API 
        // we also set the tread to null only from the entry points in the SoundPlayer API
        private Thread copyThread = null;

        // local buffer information 
        int currentPos = 0;
        private Stream stream = null; 
        private bool isLoadCompleted = false; 
        private Exception lastLoadException = null;
        private bool doesLoadAppearSynchronous = false; 
        private byte[] streamData = null;
        private AsyncOperation asyncOperation = null;
        private readonly SendOrPostCallback loadAsyncOperationCompleted;
 
        // event
        private static readonly object EventLoadCompleted = new object(); 
        private static readonly object EventSoundLocationChanged = new object(); 
        private static readonly object EventStreamChanged = new object();
 
        /// 
        public SoundPlayer() {
            loadAsyncOperationCompleted =
                new SendOrPostCallback(LoadAsyncOperationCompleted); 
        }
 
        ///  
        public SoundPlayer(string soundLocation) : this() {
            if(soundLocation == null) { 
                soundLocation = String.Empty;
            }
            SetupSoundLocation(soundLocation);
        } 

        ///  
        public SoundPlayer(Stream stream) : this() { 
            this.stream = stream;
        } 

        /**
         * Constructor used in deserialization
         */ 
        /// 
        [ 
            SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // SerializationInfo stores LoadTimeout as an object. 
            SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")        // Serialization constructor needs a Context parameter.
        ] 
        protected SoundPlayer(SerializationInfo serializationInfo, StreamingContext context) {
            foreach(SerializationEntry entry in serializationInfo) {
                switch (entry.Name) {
                    case "SoundLocation" : 
                        SetupSoundLocation((string) entry.Value);
                        break; 
                    case "Stream" : 
                        stream = (Stream) entry.Value;
                        // when we deserialize a stream we have to reset its seek position 
                        // vsWhidbey 180361
                        if (stream.CanSeek) {
                            stream.Seek(0, SeekOrigin.Begin);
                        } 
                        break;
                    case "LoadTimeout" : 
                        this.LoadTimeout = (int) entry.Value; 
                        break;
                } 
            }
        }

        ///  
        public int LoadTimeout {
            get { 
                return loadTimeout; 
            }
            set { 
                if (value < 0) {
                    throw new ArgumentOutOfRangeException("LoadTimeout", value, SR.GetString(SR.SoundAPILoadTimeout));
                }
 
                loadTimeout = value;
            } 
        } 

        ///  
        public string SoundLocation {
            get {
                if (uri != null && uri.IsFile) {
                    FileIOPermission fiop = new FileIOPermission(PermissionState.None); 
                    fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
                    fiop.Demand(); 
                } 
                return soundLocation;
            } 
            set {
                if (value == null)
                    value = String.Empty;
 
                if (soundLocation.Equals(value))
                    return; 
 
                SetupSoundLocation(value);
 
                OnSoundLocationChanged(EventArgs.Empty);
            }
        }
 
        /// 
        public Stream Stream { 
            get { 
                // if the path is set, we should return null
                // Path and Stream are mutually exclusive 
                if (uri != null)
                    return null;
                return this.stream;
            } 
            set {
                if (stream == value) 
                    return; 

                SetupStream(value); 

                OnStreamChanged(EventArgs.Empty);
            }
        } 

        ///  
        public bool IsLoadCompleted { 
            get {
                return isLoadCompleted; 
            }
        }

        ///  
        public object Tag {
            get { 
                return tag; 
            }
            set { 
                tag = value;
            }
        }
 
        /// 
        public void LoadAsync() { 
            // if we have a file there is nothing to load - we just pass the file to the PlaySound function 
            // if we have a stream, then we start loading the stream async
            // 
            if (uri!= null && uri.IsFile){
                Debug.Assert(stream == null, "we can't have a stream and a path at the same time");
                isLoadCompleted = true;
 
                FileInfo fi = new FileInfo(uri.LocalPath);
                if (!fi.Exists) { 
                    throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation); 
                }
 
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
                return;
            }
 
            // if we are actively loading, keep it running
            if (copyThread != null && copyThread.ThreadState == System.Threading.ThreadState.Running) { 
                return; 
            }
            isLoadCompleted = false; 
            streamData = null;
            currentPos = 0;

            asyncOperation = AsyncOperationManager.CreateOperation(null); 

            LoadStream(false); 
        } 

        private void LoadAsyncOperationCompleted(object arg) 
        {
            OnLoadCompleted((AsyncCompletedEventArgs)arg);
        }
 
        // called for loading a stream synchronously
        // called either when the user is setting the path/stream and we are loading 
        // or when loading took more time than the time out 
        private void CleanupStreamData() {
            this.currentPos = 0; 
            this.streamData = null;
            this.isLoadCompleted = false;
            this.lastLoadException = null;
            this.doesLoadAppearSynchronous = false; 
            this.copyThread = null;
            this.semaphore.Set(); 
        } 

        ///  
        public void Load() {
            // if we have a file there is nothing to load - we just pass the file to the PlaySound function
            // if we have a stream, then we start loading the stream [....]
            // 
            if (uri != null && uri.IsFile){
                Debug.Assert(stream == null, "we can't have a stream and a path at the same time"); 
                FileInfo fi = new FileInfo(uri.LocalPath); 
                if (!fi.Exists) {
                    throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation); 
                }
                isLoadCompleted = true;
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
                return; 
            }
 
            LoadSync(); 
        }
 
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // FileIOPermission based on URI path, but path isn't gonna change during scope of Demand
        private void LoadAndPlay(int flags) {
            // bug 16794: when the user does not specify a sound location nor a stream, play Beep
            if (String.IsNullOrEmpty(soundLocation) && stream == null) { 
                SystemSounds.Beep.Play();
                return; 
            } 

            if (uri != null && uri.IsFile) { 
                // VSW 580992: With more than one thread, someone could call SoundPlayer::set_Location
                // between the time LoadAndPlay demands FileIO and the time it calls PlaySound under elevation.
                //
                // Another scenario is someone calling SoundPlayer::set_Location between the time 
                // LoadAndPlay validates the sound file and the time it calls PlaySound.
                // The SoundPlayer will end up playing an un-validated sound file. 
                // The solution is to store the uri.LocalPath on a local variable 
                string localPath = uri.LocalPath;
 
                // request permission to read the file:
                // pass the full path to the FileIOPermission
                FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read, localPath);
                perm.Demand(); 

                // play the path 
                isLoadCompleted = true; 
                System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
 
                System.ComponentModel.IntSecurity.UnmanagedCode.Assert();
                // ValidateSoundFile calls into the MMIO API so we need UnmanagedCode permissions to do that.
                // And of course we need UnmanagedCode permissions to all Win32::PlaySound method.
                try { 
                    // don't use uri.AbsolutePath because that gives problems when there are whitespaces in file names
                    ValidateSoundFile(localPath); 
                    UnsafeNativeMethods.PlaySound(localPath, IntPtr.Zero, NativeMethods.SND_NODEFAULT | flags); 
                } finally {
                    System.Security.CodeAccessPermission.RevertAssert(); 
                }
            } else {
                LoadSync();
                ValidateSoundData(streamData); 
                System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
 
                System.ComponentModel.IntSecurity.UnmanagedCode.Assert(); 
                try {
                    UnsafeNativeMethods.PlaySound(streamData, IntPtr.Zero, NativeMethods.SND_MEMORY | NativeMethods.SND_NODEFAULT | flags); 
                } finally {
                    System.Security.CodeAccessPermission.RevertAssert();
                }
            } 
        }
 
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // WebPermission based on URI path, but path isn't gonna change during scope of Demand 
        private void LoadSync() {
 
            Debug.Assert((uri == null || !uri.IsFile), "we only load streams");

            // first make sure that any possible download ended
            if (!semaphore.WaitOne(LoadTimeout, false)) { 
                if (copyThread != null)
                    copyThread.Abort(); 
                CleanupStreamData(); 
                throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut));
            } 

            // if we have data, then we are done
            if (streamData != null)
                return; 

            // setup the http stream 
            if (uri != null && !uri.IsFile && stream == null) { 
                WebPermission webPerm = new WebPermission(NetworkAccess.Connect, uri.AbsolutePath);
                webPerm.Demand(); 
                WebRequest webRequest = WebRequest.Create(uri);
                webRequest.Timeout = LoadTimeout;

                WebResponse webResponse; 
                webResponse = webRequest.GetResponse();
 
                // now get the stream 
                stream = webResponse.GetResponseStream();
            } 

            if (stream.CanSeek) {
                // if we can get data synchronously, then get it
                LoadStream(true); 
            } else {
                // the data can't be loaded synchronously 
                // load it async, then wait for it to finish 
                doesLoadAppearSynchronous = true; // to avoid OnFailed call.
                LoadStream(false); 

                if(!semaphore.WaitOne(LoadTimeout, false)) {
                    if (copyThread != null)
                        copyThread.Abort(); 
                    CleanupStreamData();
                    throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut)); 
                } 

                doesLoadAppearSynchronous = false; 

                if (lastLoadException != null)
                {
                    throw lastLoadException; 
                }
            } 
 
            // we don't need the worker copyThread anymore
            this.copyThread = null; 
        }

        private void LoadStream(bool loadSync) {
            if (loadSync && stream.CanSeek) { 
                int streamLen = (int) stream.Length;
                currentPos = 0; 
                streamData = new byte[streamLen]; 
                stream.Read(streamData, 0, streamLen);
                isLoadCompleted = true; 
                OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
            } else {
                // lock any synchronous calls on the Sound object
                semaphore.Reset(); 
                // start loading
                copyThread = new Thread(new ThreadStart(this.WorkerThread)); 
                copyThread.Start(); 
            }
        } 

        /// 
        public void Play() {
            LoadAndPlay(NativeMethods.SND_ASYNC); 
        }
 
        ///  
        public void PlaySync() {
            LoadAndPlay(NativeMethods.SND_[....]); 
        }

        /// 
        public void PlayLooping() { 
            LoadAndPlay(NativeMethods.SND_LOOP | NativeMethods.SND_ASYNC);
        } 
 
        private static Uri ResolveUri(string partialUri) {
            Uri result = null; 
            try {
                result = new Uri(partialUri);
            } catch (UriFormatException) {
                // eat URI parse exceptions 
            }
 
            if (result == null) { 
                // try relative to appbase
                try { 
                    result = new Uri(Path.GetFullPath(partialUri));
                } catch (UriFormatException) {
                    // eat URI parse exceptions
                } 
            }
            return result; 
        } 

        private void SetupSoundLocation(string soundLocation) { 
            // if we are loading a file, stop it right now
            //
            if (copyThread != null) {
                copyThread.Abort(); 
                CleanupStreamData();
            } 
 
            uri = ResolveUri(soundLocation);
 
            this.soundLocation = soundLocation;
            stream = null;
            if (uri == null) {
                if (!String.IsNullOrEmpty(soundLocation)) 
                    throw new UriFormatException(SR.GetString(SR.SoundAPIBadSoundLocation));
            } else { 
                if (!uri.IsFile) { 
                    // we are referencing a web resource ...
                    // 

                    // we treat it as a stream...
                    //
                    streamData = null; 
                    currentPos = 0;
                    isLoadCompleted = false; 
                } 
            }
        } 

        private void SetupStream(Stream stream) {
            if (this.copyThread != null) {
                copyThread.Abort(); 
                CleanupStreamData();
            } 
 
            this.stream = stream;
            this.soundLocation = String.Empty; 
            this.streamData = null;
            this.currentPos = 0;
            isLoadCompleted = false;
            if (stream != null) { 
                uri = null;
            } 
        } 

        ///  
        public void Stop() {
            IntSecurity.SafeSubWindows.Demand();
            UnsafeNativeMethods.PlaySound((byte[]) null, IntPtr.Zero, NativeMethods.SND_PURGE);
        } 

        ///  
        public event AsyncCompletedEventHandler LoadCompleted { 
            add {
                Events.AddHandler(EventLoadCompleted, value); 
            }
            remove {
                Events.RemoveHandler(EventLoadCompleted, value);
            } 
        }
 
        ///  
        public event EventHandler SoundLocationChanged {
            add { 
                Events.AddHandler(EventSoundLocationChanged, value);
            }
            remove {
                Events.RemoveHandler(EventSoundLocationChanged, value); 
            }
        } 
 
        /// 
        public event EventHandler StreamChanged { 
            add {
                Events.AddHandler(EventStreamChanged, value);
            }
            remove { 
                Events.RemoveHandler(EventStreamChanged, value);
            } 
        } 

        ///  
        protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e) {
            AsyncCompletedEventHandler eh = (AsyncCompletedEventHandler) Events[EventLoadCompleted];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        ///  
        protected virtual void OnSoundLocationChanged(EventArgs e) {
            EventHandler eh = (EventHandler) Events[EventSoundLocationChanged];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        ///  
        protected virtual void OnStreamChanged(EventArgs e) {
            EventHandler eh = (EventHandler) Events[EventStreamChanged];
            if (eh != null)
            { 
                eh(this, e);
            } 
        } 

        [ 
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")   // The set of reasons why WorkerThread should fail is not finite
        ]
        private void WorkerThread() {
            try 
            {
                // setup the http stream 
                if (uri != null && !uri.IsFile && stream == null) { 
                    WebRequest webRequest = WebRequest.Create(uri);
 
                    WebResponse webResponse = webRequest.GetResponse();

                    stream = webResponse.GetResponseStream();
                } 

                this.streamData = new byte[blockSize]; 
 
                int readBytes = stream.Read(streamData, currentPos, blockSize);
                int totalBytes = readBytes; 

                while (readBytes > 0) {
                    currentPos += readBytes;
                    if (streamData.Length < currentPos + blockSize) { 
                        byte[] newData = new byte[streamData.Length * 2];
                        Array.Copy(streamData, newData, streamData.Length); 
                        streamData = newData; 
                    }
                    readBytes = stream.Read(streamData, currentPos, blockSize); 
                    totalBytes += readBytes;
                }

                lastLoadException = null; 
            }
            catch (Exception exception) 
            { 
                lastLoadException = exception;
            } 
            catch
            {
                throw;
            } 

            if (!doesLoadAppearSynchronous) 
            { 
                // Post notification back to the UI thread.
                asyncOperation.PostOperationCompleted( 
                    loadAsyncOperationCompleted,
                    new AsyncCompletedEventArgs(lastLoadException, false, null));
            }
            isLoadCompleted = true; 
            semaphore.Set();
        } 
 
        private unsafe void ValidateSoundFile(string fileName) {
            NativeMethods.MMCKINFO ckRIFF = new NativeMethods.MMCKINFO(); 
            NativeMethods.MMCKINFO ck = new NativeMethods.MMCKINFO();
            NativeMethods.WAVEFORMATEX waveFormat = null;
            int dw;
 
            IntPtr hMIO = UnsafeNativeMethods.mmioOpen(fileName, IntPtr.Zero, NativeMethods.MMIO_READ | NativeMethods.MMIO_ALLOCBUF);
 
            if (hMIO == IntPtr.Zero) 
                throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation);
 
            try {
                ckRIFF.fccType = mmioFOURCC('W', 'A','V','E');
                if (UnsafeNativeMethods.mmioDescend(hMIO, ckRIFF, null, NativeMethods.MMIO_FINDRIFF) != 0)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveFile, this.soundLocation)); 

                while (UnsafeNativeMethods.mmioDescend(hMIO, ck, ckRIFF, 0) == 0) { 
                    if (ck.dwDataOffset + ck.cksize > ckRIFF.dwDataOffset + ckRIFF.cksize) 
                        throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
                    if (ck.ckID == mmioFOURCC('f','m','t',' ')) {
                            if (waveFormat == null) {
                                dw = ck.cksize;
                                if (dw < Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX))) 
                                    dw =  Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX));
 
                                waveFormat = new NativeMethods.WAVEFORMATEX(); 
                                byte[] data = new byte[dw];
                                if (UnsafeNativeMethods.mmioRead(hMIO, data, dw) != dw) 
                                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIReadError, this.soundLocation));
                                fixed(byte* pdata = data) {
                                    Marshal.PtrToStructure((IntPtr) pdata, waveFormat);
                                } 
                            } else {
                                // 
                                // multiple formats? 
                                //
                            } 
                    }
                    UnsafeNativeMethods.mmioAscend(hMIO, ck, 0);
                }
 
                if (waveFormat == null)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 
 
                if (waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_PCM &&
                    waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM && 
                    waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
                    throw new InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported));

            } finally { 
                    if (hMIO != IntPtr.Zero)
                        UnsafeNativeMethods.mmioClose(hMIO, 0); 
            } 
        }
 
        private static void ValidateSoundData(byte[] data) {
            int position = 0;
            Int16 wFormatTag = -1;
            bool fmtChunkFound = false; 

            // the RIFF header should be at least 12 bytes long. 
            if (data.Length < 12) 
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
            // validate the RIFF header
            if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
            if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') 
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
 
            // we only care about "fmt " chunk 
            position = 12;
            int len = data.Length; 
            while (!fmtChunkFound && position < len - 8) {
                if (data[position] == (byte)'f' && data[position + 1] == (byte)'m' && data[position + 2] == (byte)'t' && data[position+3] == (byte)' ') {
                    //
                    // fmt chunk 
                    //
                    fmtChunkFound = true; 
                    int chunkSize = BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]); 
                    //
                    // get the cbSize from the WAVEFORMATEX 
                    //

                    int sizeOfWAVEFORMAT = 16;
                    if (chunkSize != sizeOfWAVEFORMAT) { 
                        // we are dealing w/ WAVEFORMATEX
                        // do extra validation 
                        int sizeOfWAVEFORMATEX = 18; 

                        // make sure the buffer is big enough to store a short 
                        if (len < position + 8 + sizeOfWAVEFORMATEX - 1)
                            throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));

                        Int16 cbSize = BytesToInt16(data[position+8 + sizeOfWAVEFORMATEX - 1], 
                                                    data[position+8 + sizeOfWAVEFORMATEX-2]);
                        if (cbSize + sizeOfWAVEFORMATEX != chunkSize) 
                            throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 
                    }
 
                    // make sure the buffer passed in is big enough to store a short
                    if(len < position + 9)
                        throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
                    wFormatTag = BytesToInt16(data[position+9], data[position+8]); 

                    position += chunkSize + 8; 
                } else { 
                    position += 8 + BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]);
                } 
            }

            if (!fmtChunkFound)
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader)); 

            if (wFormatTag != NativeMethods.WAVE_FORMAT_PCM && 
                wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM && 
                wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
                throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported)); 
        }

        private static Int16 BytesToInt16(byte ch0, byte ch1) {
            int res; 
            res = (int) ch1;
            res |= (int) (((int)ch0) << 8); 
            return (Int16) res; 
        }
        private static int BytesToInt(byte ch0, byte ch1, byte ch2, byte ch3) { 
            return mmioFOURCC((char) ch3, (char)ch2, (char) ch1, (char)ch0);
        }

        private static int mmioFOURCC(char ch0, char ch1, char ch2, char ch3) { 
            int result = 0;
            result |= ((int) ch0); 
            result |= ((int) ch1) << 8; 
            result |= ((int) ch2) << 16;
            result |= ((int) ch3) << 24; 
            return result;
        }

        ///  
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] // vsw 427356 
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { 
            if (!String.IsNullOrEmpty(this.soundLocation)) {
                info.AddValue("SoundLocation", this.soundLocation); 
            }

            if (this.stream != null) {
                info.AddValue("Stream", this.stream); 
            }
 
            info.AddValue("LoadTimeout", this.loadTimeout); 
        }
 
        private class IntSecurity {
            // Constructor added because of FxCop rules
            private IntSecurity() {}
 
            private static CodeAccessPermission safeSubWindows;
 
            internal static CodeAccessPermission SafeSubWindows { 
                get {
                    if (safeSubWindows == null) { 
                        safeSubWindows = new UIPermission(UIPermissionWindow.SafeSubWindows);
                    }

                    return safeSubWindows; 
                }
            } 
        } 

        private class NativeMethods { 
            // Constructor added because of FxCop rules
            private NativeMethods() {}

            internal const int WAVE_FORMAT_PCM        = 0x0001, 
            WAVE_FORMAT_ADPCM                       = 0x0002,
            WAVE_FORMAT_IEEE_FLOAT                  = 0x0003; 
 
            internal const int MMIO_READ              = 0x00000000,
            MMIO_ALLOCBUF                           = 0x00010000, 
            MMIO_FINDRIFF                           = 0x00000020;

            internal const int SND_[....] = 0000,
            SND_ASYNC = 0x0001, 
            SND_NODEFAULT = 0x0002,
            SND_MEMORY = 0x0004, 
            SND_LOOP = 0x0008, 
            SND_PURGE = 0x0040,
            SND_FILENAME = 0x00020000, 
            SND_NOSTOP = 0x0010;

            [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
            internal class MMCKINFO { 
                internal int      ckID;
                internal int      cksize; 
                internal int      fccType; 
                internal int      dwDataOffset;
                internal int      dwFlags; 
            }

            [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
            internal class WAVEFORMATEX { 
                internal System.Int16     wFormatTag;
                internal System.Int16     nChannels; 
                internal int              nSamplesPerSec; 
                internal int              nAvgBytesPerSec;
                internal System.Int16     nBlockAlign; 
                internal System.Int16     wBitsPerSample;
                internal System.Int16     cbSize;
            }
        } 

        private class UnsafeNativeMethods { 
            // Constructor added because of FxCop rules 
            private UnsafeNativeMethods() {}
 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern bool PlaySound([MarshalAs(UnmanagedType.LPWStr)] string soundName, IntPtr hmod, int soundFlags);

            [DllImport(ExternDll.WinMM, ExactSpelling=true, CharSet=CharSet.Auto)] 
            internal static extern bool PlaySound(byte[] soundName, IntPtr hmod, int soundFlags);
 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments")] 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern IntPtr mmioOpen(string fileName, IntPtr not_used, int flags); 

            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioAscend(IntPtr hMIO, NativeMethods.MMCKINFO lpck, int flags);
 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioDescend(IntPtr hMIO, 
                                                   [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lpck, 
                                                   [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lcpkParent,
                                                   int flags); 
            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
            internal static extern int mmioRead(IntPtr hMIO, [MarshalAs(UnmanagedType.LPArray)] byte[] wf, int cch);

            [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)] 
            internal static extern int mmioClose(IntPtr hMIO, int flags);
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK