A buffered Sound object with Play() method

Updated Diamonddrake 0 Tallied Votes 752 Views Share

I needed a way to buffer into memory an entire wav sound file to be played back a moments notice, as if for a game. So i created this class, simple instantiate it by passing to it a string and a bool indicating if you should buffer it to memory. after that you just call its play method whenever you need it and the windows api automatically handles async playback.

Sadly, It works just shy of how well I need it to for my purpose. but it does work very well. I'm not very good with clean up code, I believe everything here should garbage collect nicely, but if anyone has any input on that I would appreciate it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace DDSoundEngine
{
    class SimplePlaySound : IDisposable
    {
        byte[] Sbuffer;

        string path;

        bool _buffer;

        public SimplePlaySound(string fileName, bool buffer)
        {
            if (!System.IO.File.Exists(fileName))
            {
                throw new NotImplementedException("Error: File Name Incorrect");
            }

            _buffer = buffer;

            if (buffer)
            {
                using (FileStream s = new FileStream(fileName, FileMode.Open))
                {
                    Sbuffer = new byte[s.Length];
                    s.Read(Sbuffer, 0, (int)s.Length);
                }
            }
            else
            {
                path = fileName;
            }
        }
        // PlaySound()
        [DllImport("winmm.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern bool PlaySound(string pszSound,
            IntPtr hMod, SoundFlags sf);

        [DllImport("winmm.dll", SetLastError = true)]
        static extern bool PlaySound(byte[] pszSound, IntPtr hmod, SoundFlags fdwSound);

        [DllImport("winmm.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern bool sndPlaySound(IntPtr ptr, int fuSound);


        [Flags]
        public enum SoundFlags : int
        {
            SND_SYNC = 0x0000,  /* play synchronously (default) */
            SND_ASYNC = 0x0001,  /* play asynchronously */
            SND_NODEFAULT = 0x0002,  /* silence (!default) if sound not found */
            SND_MEMORY = 0x0004,  /* pszSound points to a memory file */
            SND_LOOP = 0x0008,  /* loop the sound until next sndPlaySound */
            SND_NOSTOP = 0x0010,  /* don't stop any currently playing sound */
            SND_NOWAIT = 0x00002000, /* don't wait if the driver is busy */
            SND_ALIAS = 0x00010000, /* name is a registry alias */
            SND_ALIAS_ID = 0x00110000, /* alias is a predefined ID */
            SND_FILENAME = 0x00020000, /* name is file name */
            SND_RESOURCE = 0x00040004,  /* name is resource name or atom */
            SND_PURGE = 0x0040 /* used in stopiing sounds*/
        }

        public void Play()
        {
            if (_buffer)
            {
                PlaySound(Sbuffer, IntPtr.Zero, SoundFlags.SND_MEMORY | SoundFlags.SND_ASYNC);
            }
            else
            {
                PlaySound(path, new System.IntPtr(), SoundFlags.SND_SYNC);
            }
        }

        public void PlayLoop()
        {
            if (_buffer)
            {
                PlaySound(Sbuffer, IntPtr.Zero, SoundFlags.SND_MEMORY | SoundFlags.SND_ASYNC | SoundFlags.SND_LOOP);
            }
            else
            {
                PlaySound(path, new System.IntPtr(), SoundFlags.SND_SYNC | SoundFlags.SND_LOOP);
            }
        }

        public void Stop()
        {
            if (_buffer)
            {
                PlaySound((string)null, IntPtr.Zero , 0);
            }
            else
            {
                PlaySound((string)null , IntPtr.Zero , 0);
            }
        }
        
        #region Dispose
        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }


        
        // Track whether Dispose has been called.
        private bool disposed = false;

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    //keep the app from crashing windows API by telling it to stop using any sound resources
                    PlaySound((string)null, IntPtr.Zero, 0);


                }

                disposed = true;

            }
        }
        #endregion


    }
}