Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set Windows volume with C#

Tags:

c#

windows

volume

I've been searching around the web for a way to control the audio of my computer from C#. I've found this code which is working fine.

    public static float GetMasterVolume()
    {
        // get the speakers (1st render + multimedia) device
        IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
        IMMDevice speakers;
        const int eRender = 0;
        const int eMultimedia = 1;
        deviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, out speakers);

        object o;
        speakers.Activate(typeof(IAudioEndpointVolume).GUID, 0, IntPtr.Zero, out o);
        IAudioEndpointVolume aepv = (IAudioEndpointVolume)o;
        float volume = aepv.GetMasterVolumeLevelScalar();
        Marshal.ReleaseComObject(aepv);
        Marshal.ReleaseComObject(speakers);
        Marshal.ReleaseComObject(deviceEnumerator);
        return volume;
    }

    [ComImport]
    [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
    private class MMDeviceEnumerator
    {
    }

    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IAudioEndpointVolume
    {
        void _VtblGap1_6();
        float GetMasterVolumeLevelScalar();
    }

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDeviceEnumerator
    {
        void _VtblGap1_1();

        [PreserveSig]
        int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppDevice);
    }

    [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDevice
    {
        [PreserveSig]
        int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
    }

That's the getVolume function. I think there must be a similar way to set the volume. I came across this method:

SetMasterVolumeLevelScalar()

HRESULT SetMasterVolumeLevelScalar(
  [in] float   fLevel,
  [in] LPCGUID pguidEventContext
);

But I can't understand the second parameter. Any help would be appreciated.

like image 322
Aldridge1991 Avatar asked Oct 31 '22 05:10

Aldridge1991


2 Answers

I see you've already resolved the issue but wanted to give a formal answer for anyone else curious or in need of help.

When you call SetMasterVolumeLevelScalar you pass in the volume in percentage from 0.0 to 1.0 and you pass in a GUID. You can pass in Null as well; however. The reason to pass in the GUID is so that anyone other applications changing can inspect the change and see if it was them or another app. For example:

If you register to the OnNotify for volume change you'll receive a struct each time the volume changes. The first field in the struct is the GUID. If you're passing in a constant GUID to change the volume then this struct will show you you're own GUID; you can compare it and ignore it or whatever. If the struct passes in NULL or a different GUID then you know another app has changed the volume and maybe you want to do something different at this point.

Hope this helps :)

like image 79
Michael Puckett II Avatar answered Nov 08 '22 04:11

Michael Puckett II


Define your interface...

[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAudioEndpointVolume
{
    // ...
    Int32 GetMasterVolumeLevelScalar(ref Single pfLevel);
    Int32 SetMasterVolumeLevelScalar(Single fLevel, Guid pguidEventContext);
    // ...
}

Then, in your class, for example:

public class EndpointVolume
{
    private readonly Guid m_GuidEventContext;
    private IAudioEndpointVolume m_AudioEndpoint;

    public EndpointVolume()
    {
        m_GuidEventContext = Guid.NewGuid();
        m_AudioEndpoint = ...
    }

    public Single MasterVolume
    {
        get
        {
            Single level = 0.0f;
            Int32 res = m_AudioEndpoint.GetMasterVolumeLevelScalar(ref level);

            if (retVal != 0)
                throw new Exception("AudioEndpointVolume.GetMasterVolumeLevelScalar()");

            return level;
        }

        set
        {
            Single level = value;

            if (level < 0.0f)
                level = 0.0f;
            else if (level > 1.0f)
                level = 0.0f;

            Int32 res = m_AudioEndpoint.SetMasterVolumeLevelScalar(level, m_GuidEventContext);

            if (res!= 0)
                throw new Exception("AudioEndpointVolume.SetMasterVolumeLevelScalar()");
        }
    }

    // ...
}

The pguidEventContext parameter is being used to define the current event context, so that the changes being made to the master volume are reflected to all other clients. From official documentation:

Context value for the IAudioEndpointVolumeCallback::OnNotify method. This parameter points to an event-context GUID. If the SetMasterVolumeLevelScalar call changes the volume level of the endpoint, all clients that have registered IAudioEndpointVolumeCallback interfaces with that endpoint will receive notifications. In its implementation of the OnNotify method, a client can inspect the event-context GUID to discover whether it or another client is the source of the volume-change event. If the caller supplies a NULL pointer for this parameter, the notification routine receives the context GUID value GUID_NULL.

like image 35
Tommaso Belluzzo Avatar answered Nov 08 '22 05:11

Tommaso Belluzzo