Windows C#

///////////////////////////////////////////////////////////////////////////////////////////////
//
//    This File is Part of the CallButler Open Source PBX (http://www.codeplex.com/callbutler
//
//    Copyright (c) 2005-2008, Jim Heising
//    All rights reserved.
//
//    Redistribution and use in source and binary forms, with or without modification,
//    are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright notice,
//      this list of conditions and the following disclaimer in the documentation and/or
//      other materials provided with the distribution.
//
//    * Neither the name of Jim Heising nor the names of its contributors may be
//      used to endorse or promote products derived from this software without specific prior
//      written permission.
//
//    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//    IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//    POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Runtime.InteropServices;
using System.IO;
namespace WOSI.Utilities
{
    /// 
    /// Summary description for SoundUtilities.
    /// 

    public sealed class SoundUtils
    {
        private const int MMSYSERR_NOERROR = 0;
        private const int MAXPNAMELEN = 32;
        private const int MIXER_LONG_NAME_CHARS = 64;
        private const int MIXER_SHORT_NAME_CHARS = 16;
        private const int MIXER_GETLINEINFOF_COMPONENTTYPE = 0x3;
        private const int MIXER_GETCONTROLDETAILSF_VALUE = 0x0;
        private const int MIXER_GETLINECONTROLSF_ONEBYTYPE = 0x2;
        private const int MIXER_SETCONTROLDETAILSF_VALUE = 0x0;
        private const int MIXERLINE_COMPONENTTYPE_DST_FIRST = 0x0;
        private const int MIXERLINE_COMPONENTTYPE_SRC_FIRST = 0x1000;
        private const int MIXERLINE_COMPONENTTYPE_DST_SPEAKERS = (MIXERLINE_COMPONENTTYPE_DST_FIRST + 4);
        private const int MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE = (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 3);
        private const int MIXERLINE_COMPONENTTYPE_SRC_LINE = (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 2);
        private const int MIXERCONTROL_CT_CLASS_FADER = 0x50000000;
        private const int MIXERCONTROL_CT_UNITS_UNSIGNED = 0x30000;
        private const int MIXERCONTROL_CT_CLASS_SWITCH = 0x20000000;
        private const int MIXERCONTROL_CT_SC_SWITCH_BOOLEAN = 0x0;
        private const int MIXERCONTROL_CT_UNITS_BOOLEAN = 0x10000;
        private const int MIXERCONTROL_CONTROLTYPE_FADER = (MIXERCONTROL_CT_CLASS_FADER | MIXERCONTROL_CT_UNITS_UNSIGNED);
        private const int MIXERCONTROL_CONTROLTYPE_VOLUME = (MIXERCONTROL_CONTROLTYPE_FADER + 1);
        private const int MIXERCONTROL_CONTROLTYPE_BOOLEAN = (MIXERCONTROL_CT_CLASS_SWITCH | MIXERCONTROL_CT_SC_SWITCH_BOOLEAN | MIXERCONTROL_CT_UNITS_BOOLEAN);
        private const int MIXERCONTROL_CONTROLTYPE_MUTE = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 2);
        private struct MIXERCAPS
        {
            public int wMid;
            public int wPid;
            public int vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
            public string szPname;
            public int fdwSupport;
            public int cDestinations;
        }
        private struct MIXERCONTROL
        {
            public int cbStruct;
            public int dwControlID;
            public int dwControlType;
            public int fdwControl;
            public int cMultipleItems;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)]
            public string szShortName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)]
            public string szName;
            public int lMinimum;
            public int lMaximum;
            [MarshalAs(UnmanagedType.U4, SizeConst = 10)]
            public int reserved;
        }
        private struct MIXERCONTROLDETAILS
        {
            public int cbStruct;
            public int dwControlID;
            public int cChannels;
            public int item;
            public int cbDetails;
            public IntPtr paDetails;
        }
        private struct MIXERCONTROLDETAILS_UNSIGNED
        {
            public int dwValue;
        }
        private struct MIXERCONTROLDETAILS_BOOLEAN
        {
            public int dwValue;
        }
        private struct MIXERLINE
        {
            public int cbStruct;
            public int dwDestination;
            public int dwSource;
            public int dwLineID;
            public int fdwLine;
            public int dwUser;
            public int dwComponentType;
            public int cChannels;
            public int cConnections;
            public int cControls;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)]
            public string szShortName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)]
            public string szName;
            public int dwType;
            public int dwDeviceID;
            public int wMid;
            public int wPid;
            public int vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
            public string szPname;
        }
        private struct MIXERLINECONTROLS
        {
            public int cbStruct;
            public int dwLineID;
            public int dwControl;
            public int cControls;
            public int cbmxctrl;
            public IntPtr pamxctrl;
        }
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerClose(int hmx);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetControlDetailsA(int hmxobj, ref MIXERCONTROLDETAILS pmxcd, int fdwDetails);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetDevCapsA(int uMxId, MIXERCAPS pmxcaps, int cbmxcaps);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetID(int hmxobj, int pumxID, int fdwId);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetLineControlsA(int hmxobj, ref MIXERLINECONTROLS pmxlc, int fdwControls);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetLineInfoA(int hmxobj, ref MIXERLINE pmxl, int fdwInfo);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetNumDevs();
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerMessage(int hmx, int uMsg, int dwParam1, int dwParam2);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerOpen(out int phmx, int uMxId, int dwCallback, int dwInstance, int fdwOpen);
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerSetControlDetails(int hmxobj, ref MIXERCONTROLDETAILS pmxcd, int fdwDetails);
        [DllImport("winmm.dll", EntryPoint = "sndPlaySoundA")]
        private static extern int sndPlaySound(string lpszSoundName, int uFlags);
        private const int SND_ASYNC = 0x1;
        private const int SND_LOOP = 0x8;
        private SoundUtils()
        {
        }
        public static void PlaySound(string filename, bool loop)
        {
            if (File.Exists(filename))
            {
                if (loop)
                    sndPlaySound(filename, SND_ASYNC | SND_LOOP);
                else
                    sndPlaySound(filename, SND_ASYNC);
            }
        }
        public static void StopSound()
        {
            sndPlaySound(null, SND_ASYNC);
        }
        public static void Mute(bool mute)
        {
            // Check if this is vista or XP
            if (System.Environment.OSVersion.Version.Major >= 6)
            {
                EndpointVolume epVol = new EndpointVolume();
                epVol.Mute = mute;
                epVol.Dispose();
            }
            else
            {
                int mixerID = 0;
                if (mixerOpen(out mixerID, 0, 0, 0, 0) == MMSYSERR_NOERROR)
                {
                    MIXERLINE line = new MIXERLINE();
                    line.cbStruct = Marshal.SizeOf(line);
                    line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
                    if (mixerGetLineInfoA(mixerID, ref line, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
                    {
                        int sizeofMIXERCONTROL = 152;
                        MIXERCONTROL mixerControl = new MIXERCONTROL();
                        MIXERLINECONTROLS lineControl = new MIXERLINECONTROLS();
                        lineControl.pamxctrl = Marshal.AllocCoTaskMem(sizeofMIXERCONTROL);
                        lineControl.cbStruct = Marshal.SizeOf(lineControl);
                        lineControl.dwLineID = line.dwLineID;
                        lineControl.dwControl = MIXERCONTROL_CONTROLTYPE_MUTE;
                        lineControl.cControls = 1;
                        lineControl.cbmxctrl = sizeofMIXERCONTROL;
                        mixerControl.cbStruct = sizeofMIXERCONTROL;
                        if (mixerGetLineControlsA(mixerID, ref lineControl, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
                        {
                            mixerControl = (MIXERCONTROL)Marshal.PtrToStructure(lineControl.pamxctrl, typeof(MIXERCONTROL));
                            MIXERCONTROLDETAILS controlDetails = new MIXERCONTROLDETAILS();
                            MIXERCONTROLDETAILS_BOOLEAN muteValue = new MIXERCONTROLDETAILS_BOOLEAN();
                            controlDetails.item = 0;
                            controlDetails.dwControlID = mixerControl.dwControlID;
                            controlDetails.cbStruct = Marshal.SizeOf(controlDetails);
                            controlDetails.cbDetails = Marshal.SizeOf(muteValue);
                            controlDetails.cChannels = 1;
                            muteValue.dwValue = Convert.ToInt32(mute);
                            controlDetails.paDetails = Marshal.AllocCoTaskMem(Marshal.SizeOf(muteValue));
                            Marshal.StructureToPtr(muteValue, controlDetails.paDetails, false);
                            int rc = mixerSetControlDetails(mixerID, ref controlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
                            Marshal.FreeCoTaskMem(controlDetails.paDetails);
                            Marshal.FreeCoTaskMem(lineControl.pamxctrl);
                        }
                    }
                }
            }
        }
    }
    public class EndpointVolume
    {
        #region Interface to COM objects
        const int DEVICE_STATE_ACTIVE = 0x00000001;
        const int DEVICE_STATE_DISABLE = 0x00000002;
        const int DEVICE_STATE_NOTPRESENT = 0x00000004;
        const int DEVICE_STATE_UNPLUGGED = 0x00000008;
        const int DEVICE_STATEMASK_ALL = 0x0000000f;
        [DllImport("ole32.Dll")]
        static public extern uint CoCreateInstance(ref Guid clsid,
        [MarshalAs(UnmanagedType.IUnknown)] object inner,
        uint context,
        ref Guid uuid,
        [MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);
        // C Header file : Include Mmdeviceapi.h (Windows Vista SDK)
        [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IAudioEndpointVolume
        {
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(/* [in] */__in IAudioEndpointVolumeCallback *pNotify) = 0;
            //int RegisterControlChangeNotify(IntPtr pNotify);
            int RegisterControlChangeNotify(DelegateMixerChange pNotify);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(/* [in] */ __in IAudioEndpointVolumeCallback *pNotify) = 0;
            //int UnregisterControlChangeNotify(IntPtr pNotify);
            int UnregisterControlChangeNotify(DelegateMixerChange pNotify);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetChannelCount(/* [out] */ __out UINT *pnChannelCount) = 0;
            int GetChannelCount(ref uint pnChannelCount);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel( /* [in] */ __in float fLevelDB,/* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int SetMasterVolumeLevel(float fLevelDB, Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar( /* [in] */ __in float fLevel,/* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int SetMasterVolumeLevelScalar(float fLevel, Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(/* [out] */ __out float *pfLevelDB) = 0;
            int GetMasterVolumeLevel(ref float pfLevelDB);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar( /* [out] */ __out float *pfLevel) = 0;
            int GetMasterVolumeLevelScalar(ref float pfLevel);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(/* [in] */__in UINT nChannel,float fLevelDB,/* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int SetChannelVolumeLevel(uint nChannel, float fLevelDB, Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(/* [in] */ __in UINT nChannel,float fLevel,/* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(/* [in] */ __in UINT nChannel,/* [out] */__out float *pfLevelDB) = 0;
            int GetChannelVolumeLevel(uint nChannel, ref float pfLevelDB);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(/* [in] */__in UINT nChannel,/* [out] */__out float *pfLevel) = 0;
            int GetChannelVolumeLevelScalar(uint nChannel, ref float pfLevel);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetMute( /* [in] *__in BOOL bMute, /* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int SetMute(int bMute, Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetMute( /* [out] */ __out BOOL *pbMute) = 0;
            int GetMute(ref bool pbMute);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetVolumeStepInfo( /* [out] */ __out UINT *pnStep,/* [out] */__out UINT *pnStepCount) = 0;
            int GetVolumeStepInfo(ref uint pnStep, ref uint pnStepCount);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE VolumeStepUp( /* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int VolumeStepUp(Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE VolumeStepDown(/* [unique][in] */ LPCGUID pguidEventContext) = 0;
            int VolumeStepDown(Guid pguidEventContext);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE QueryHardwareSupport(/* [out] */ __out DWORD *pdwHardwareSupportMask) = 0;
            int QueryHardwareSupport(ref uint pdwHardwareSupportMask);
            //virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetVolumeRange( /* [out] */ __out float *pflVolumeMindB,/* [out] */ __out float *pflVolumeMaxdB,/* [out] */ __out float *pflVolumeIncrementdB) = 0;
            int GetVolumeRange(ref float pflVolumeMindB, ref float pflVolumeMaxdB, ref float pflVolumeIncrementdB);
        }
        [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IMMDeviceCollection
        {
            //HRESULT GetCount([out, annotation("__out")] UINT* pcDevices);
            int GetCount(ref uint pcDevices);
            //HRESULT Item([in, annotation("__in")]UINT nDevice, [out, annotation("__out")] IMMDevice** ppDevice);
            int Item(uint nDevice, ref IntPtr ppDevice);
        }
        [Guid("D666063F-1587-4E43-81F1-B948E807363F"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IMMDevice
        {
            //HRESULT Activate([in, annotation("__in")] REFIID iid, [in, annotation("__in")] DWORD dwClsCtx, [in,unique, annotation("__in_opt")] PROPVARIANT* pActivationParams, [out,iid_is(iid), annotation("__out")] void** ppInterface);
            int Activate(ref Guid iid, uint dwClsCtx, IntPtr pActivationParams, ref IntPtr ppInterface);
            //HRESULT OpenPropertyStore([in, annotation("__in")] DWORD stgmAccess, [out, annotation("__out")] IPropertyStore** ppProperties);
            int OpenPropertyStore(int stgmAccess, ref IntPtr ppProperties);
            //HRESULT GetId([out,annotation("__deref_out")] LPWSTR* ppstrId);
            int GetId(ref string ppstrId);
            //HRESULT GetState([out, annotation("__out")] DWORD* pdwState);
            int GetState(ref int pdwState);
        }
        [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"),
        //[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IMMDeviceEnumerator
        {
            //HRESULT EnumAudioEndpoints([in, annotation("__in")] EDataFlow dataFlow, [in, annotation("__in")] DWORD dwStateMask, [out, annotation("__out")] IMMDeviceCollection** ppDevices);
            int EnumAudioEndpoints(EDataFlow dataFlow, int dwStateMask, ref IntPtr ppDevices);
            //HRESULT GetDefaultAudioEndpoint([in, annotation("__in")] EDataFlow dataFlow, [in, annotation("__in")] ERole role, [out, annotation("__out")] IMMDevice** ppEndpoint);
            int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, ref IntPtr ppEndpoint);
            //HRESULT GetDevice([, annotation("__in")]LPCWSTR pwstrId, [out, annotation("__out")] IMMDevice** ppDevice);
            int GetDevice(string pwstrId, ref IntPtr ppDevice);
            //HRESULT RegisterEndpointNotificationCallback([in, annotation("__in")] IMMNotificationClient* pClient);
            int RegisterEndpointNotificationCallback(IntPtr pClient);
            //HRESULT UnregisterEndpointNotificationCallback([in, annotation("__in")] IMMNotificationClient* pClient);
            int UnregisterEndpointNotificationCallback(IntPtr pClient);
        }
        [Flags]
        enum CLSCTX : uint
        {
            CLSCTX_INPROC_SERVER = 0x1,
            CLSCTX_INPROC_HANDLER = 0x2,
            CLSCTX_LOCAL_SERVER = 0x4,
            CLSCTX_INPROC_SERVER16 = 0x8,
            CLSCTX_REMOTE_SERVER = 0x10,
            CLSCTX_INPROC_HANDLER16 = 0x20,
            CLSCTX_RESERVED1 = 0x40,
            CLSCTX_RESERVED2 = 0x80,
            CLSCTX_RESERVED3 = 0x100,
            CLSCTX_RESERVED4 = 0x200,
            CLSCTX_NO_CODE_DOWNLOAD = 0x400,
            CLSCTX_RESERVED5 = 0x800,
            CLSCTX_NO_CUSTOM_MARSHAL = 0x1000,
            CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,
            CLSCTX_NO_FAILURE_LOG = 0x4000,
            CLSCTX_DISABLE_AAA = 0x8000,
            CLSCTX_ENABLE_AAA = 0x10000,
            CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,
            CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
            CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
            CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER
        }
        public enum EDataFlow
        {
            eRender,
            eCapture,
            eAll,
            EDataFlow_enum_count
        }
        public enum ERole
        {
            eConsole,
            eMultimedia,
            eCommunications,
            ERole_enum_count
        }
        #endregion
        // Private internal var
        object oEnumerator = null;
        IMMDeviceEnumerator iMde = null;
        object oDevice = null;
        IMMDevice imd = null;
        object oEndPoint = null;
        IAudioEndpointVolume iAudioEndpoint = null;
        // TODO
        // Problem #1 : I can't handle a volume changed event by other applications
        // (example while using the program SndVol.exe)
        public delegate void DelegateMixerChange();
        //public DelegateMixerChange delMixerChange = null;
        public delegate void MixerChangedEventHandler();
        //public event MixerChangedEventHandler MixerChanged;
        #region Class Constructor and Dispose public methods
        /// 
        /// Constructor
        /// 

        public EndpointVolume()
        {
            const uint CLSCTX_INPROC_SERVER = 1;
            Guid clsid = new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E");
            Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
            oEnumerator = null;
            uint hResult = CoCreateInstance(ref clsid, null, CLSCTX_INPROC_SERVER, ref IID_IUnknown, out oEnumerator);
            if (hResult != 0 || oEnumerator == null)
            {
                throw new Exception("CoCreateInstance() pInvoke failed");
            }
            iMde = oEnumerator as IMMDeviceEnumerator;
            if (iMde == null)
            {
                throw new Exception("COM cast failed to IMMDeviceEnumerator");
            }
            IntPtr pDevice = IntPtr.Zero;
            int retVal = iMde.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, ref pDevice);
            if (retVal != 0)
            {
                throw new Exception("IMMDeviceEnumerator.GetDefaultAudioEndpoint()");
            }
            int dwStateMask = DEVICE_STATE_ACTIVE | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED;
            IntPtr pCollection = IntPtr.Zero;
            retVal = iMde.EnumAudioEndpoints(EDataFlow.eRender, dwStateMask, ref pCollection);
            if (retVal != 0)
            {
                throw new Exception("IMMDeviceEnumerator.EnumAudioEndpoints()");
            }
            oDevice = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(pDevice);
            imd = oDevice as IMMDevice;
            if (imd == null)
            {
                throw new Exception("COM cast failed to IMMDevice");
            }
            Guid iid = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A");
            uint dwClsCtx = (uint)CLSCTX.CLSCTX_ALL;
            IntPtr pActivationParams = IntPtr.Zero;
            IntPtr pEndPoint = IntPtr.Zero;
            retVal = imd.Activate(ref iid, dwClsCtx, pActivationParams, ref pEndPoint);
            if (retVal != 0)
            {
                throw new Exception("IMMDevice.Activate()");
            }
            oEndPoint = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(pEndPoint);
            iAudioEndpoint = oEndPoint as IAudioEndpointVolume;
            if (iAudioEndpoint == null)
            {
                throw new Exception("COM cast failed to IAudioEndpointVolume");
            }
            /*
            delMixerChange = new DelegateMixerChange(MixerChange);
            retVal = iAudioEndpoint.RegisterControlChangeNotify(delMixerChange);
            if (retVal != 0)
            {
            throw new Exception("iAudioEndpoint.RegisterControlChangeNotify(delMixerChange)");
            }
            */
        }
        /// 
        /// Call this method to release all com objetcs
        /// 

        public virtual void Dispose()
        {
            /*
            if (delMixerChange != null && iAudioEndpoint != null)
            {
            iAudioEndpoint.UnregisterControlChangeNotify(delMixerChange);
            }
            */
            if (iAudioEndpoint != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(iAudioEndpoint);
                iAudioEndpoint = null;
            }
            if (oEndPoint != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(oEndPoint);
                oEndPoint = null;
            }
            if (imd != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(imd);
                imd = null;
            }
            if (oDevice != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(oDevice);
                oDevice = null;
            }
            //System.Runtime.InteropServices.Marshal.ReleaseComObject(pCollection);
            if (iMde != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(iMde);
                iMde = null;
            }
            if (oEnumerator != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(oEnumerator);
                oEnumerator = null;
            }
        }
        #endregion
        #region Private internal functions
        private void MixerChange()
        {
            /*
            if (MixerChanged != null)
            // Notify (raise) event
            MixerChanged();
            */
        }
        #endregion
        #region Public properties
        /// 
        /// Get/set the master mute. WARNING : The set mute do NOT work!
        /// 

        public bool Mute
        {
            get
            {
                bool mute = false;
                int retVal = iAudioEndpoint.GetMute(ref mute);
                if (retVal != 0)
                {
                    throw new Exception("IAudioEndpointVolume.GetMute() failed!");
                }
                return mute;
            }
            set
            {
                //nullGuid = new Guid("{00000000-0000-0000-0000-000000000000}"); // null
                // {dddddddd-dddd-dddd-dddd-dddddddddddd}
                Guid nullGuid = Guid.Empty;
                bool mute = value;
                // TODO
                // Problem #2 : This function always terminate with an internal error!
                int retVal = iAudioEndpoint.SetMute(Convert.ToInt32(mute), nullGuid);
                if (retVal != 0)
                {
                    throw new Exception("IAudioEndpointVolume.SetMute() failed!");
                }
            }
        }
        /// 
        /// Get/set the master volume level. Valid range is from 0.00F (0%) to 1.00F (100%).
        /// 

        public float MasterVolume
        {
            get
            {
                float level = 0.0F;
                int retVal = iAudioEndpoint.GetMasterVolumeLevelScalar(ref level);
                if (retVal != 0)
                {
                    throw new Exception("IAudioEndpointVolume.GetMasterVolumeLevelScalar()");
                }
                return level;
            }
            set
            {
                float level = value;
                Guid nullGuid;
                //nullGuid = new Guid("{00000000-0000-0000-0000-000000000000}"); // null
                // {dddddddd-dddd-dddd-dddd-dddddddddddd}
                nullGuid = Guid.Empty;
                int retVal = iAudioEndpoint.SetMasterVolumeLevelScalar(level, nullGuid);
                if (retVal != 0)
                {
                    throw new Exception("IAudioEndpointVolume.SetMasterVolumeLevelScalar()");
                }
            }
        }
        #endregion
        #region Public Methods
        /// 
        /// Increase the master volume
        /// 

        public void VolumeUp()
        {
            Guid nullGuid;
            //nullGuid = new Guid("{00000000-0000-0000-0000-000000000000}"); // null
            // {dddddddd-dddd-dddd-dddd-dddddddddddd}
            nullGuid = Guid.Empty;
            int retVal = iAudioEndpoint.VolumeStepUp(nullGuid);
            if (retVal != 0)
            {
                throw new Exception("IAudioEndpointVolume.SetMute()");
            }
        }
        /// 
        /// Decrease the master volume
        /// 

        public void VolumeDown()
        {
            Guid nullGuid;
            //nullGuid = new Guid("{00000000-0000-0000-0000-000000000000}"); // null
            // {dddddddd-dddd-dddd-dddd-dddddddddddd}
            nullGuid = Guid.Empty;
            int retVal = iAudioEndpoint.VolumeStepDown(nullGuid);
        }
        #endregion
    }
}