2

I want to set the volume of an audio endpoint like in windows directly from 0 to 100

public override void SetVolume(int volume) {
    NativeInterfaces.IMMDeviceEnumerator enumerator = NativeClasses.ComObjectFactory.CreateInstance(new Guid(NativeGuids.MM_DEVICE_ENUMERATOR)) as NativeInterfaces.IMMDeviceEnumerator;
    
    enumerator.GetDefaultAudioEndpoint(
        NativeEnums.EDataFlow.eCapture | NativeEnums.EDataFlow.eRender,
        NativeEnums.ERole.eConsole,
        out NativeInterfaces.IMMDevice device
    );

    device.Activate(new Guid(NativeGuids.I_AUDIO_ENDPOINT_VOLUME), 0x1, IntPtr.Zero, out object volumeObj);
    NativeInterfaces.IAudioEndpointVolume audioEndpointVolume = volumeObj as NativeInterfaces.IAudioEndpointVolume;
    audioEndpointVolume.SetMasterVolumeLevelScalar(volume / 100.0f, new Guid());
}

When I run this code my volume is still on 50 although I passed 60 as a parameter. When I call GetMasterVolumeLevelScalar it outputs 0.6 so the code worked but it seems like I use the wrong method. So how to set between 0 and 100?

EDIT: Volume Mixer enter image description here

Kaskorian
  • 426
  • 3
  • 18
  • Please post a screenshot of [the Windows Volume Mixer window](https://i.stack.imgur.com/iKUwg.gif) after you run your program. I think you're only changing your process' volume level, not the systemwide volume level. – Dai Jan 12 '21 at 01:13
  • Added. There is a fifth one in the mixer which is OBS also at 50. – Kaskorian Jan 12 '21 at 01:28
  • Does this answer your question? [Controlling Application's Volume: By Process-ID](https://stackoverflow.com/questions/20938934/controlling-applications-volume-by-process-id) –  Jan 12 '21 at 03:20
  • Using EDataFlow.eCapture here is not sensible. – Hans Passant Jan 12 '21 at 11:14
  • @OlivierRogier Thanks I added it as a process-specific implementation. – Kaskorian Jan 12 '21 at 11:55
  • @HansPassant Okay wow. I removed it and now it works. Seems like I misunderstood the EDataFlow parameter as an OR so either render OR capture. But it is and AND. so the device readout was my headset. Thanks ;) – Kaskorian Jan 12 '21 at 11:57

1 Answers1

2

Solution 1: Master Volume

public override void SetMasterVolume(int volume) {
    NativeInterfaces.IMMDeviceEnumerator enumerator = NativeClasses.ComObjectFactory.CreateInstance(new Guid(NativeGuids.MM_DEVICE_ENUMERATOR)) as NativeInterfaces.IMMDeviceEnumerator;

    enumerator.GetDefaultAudioEndpoint(
        NativeEnums.EDataFlow.eRender,
        NativeEnums.ERole.eConsole,
        out NativeInterfaces.IMMDevice device
    );

    device.Activate(new Guid(NativeGuids.I_AUDIO_ENDPOINT_VOLUME), 0x1, IntPtr.Zero, out object volumeObj);
    NativeInterfaces.IAudioEndpointVolume audioEndpointVolume = volumeObj as NativeInterfaces.IAudioEndpointVolume;
    audioEndpointVolume.SetMasterVolumeLevelScalar(volume / 100.0f, new Guid());
}

Solution 2: Process specific volume

public override void SetVolume(List<int> pids, int volume) {
    NativeInterfaces.IMMDeviceEnumerator enumerator = NativeClasses.ComObjectFactory.CreateInstance(new Guid(NativeGuids.MM_DEVICE_ENUMERATOR)) as NativeInterfaces.IMMDeviceEnumerator;

    enumerator.EnumAudioEndpoints(
        NativeEnums.EDataFlow.eRender,
        0x00000001,
        out NativeInterfaces.IMMDeviceCollection deviceCollection
    );

    deviceCollection.GetCount(out uint collectionCount);

    for (uint i = 0; i < collectionCount; i++) {
        deviceCollection.Item(i, out NativeInterfaces.IMMDevice device);
        device.Activate(new Guid(NativeGuids.I_AUDIO_SESSION_MANAGER_2), 0, IntPtr.Zero, out object obj);
        NativeInterfaces.IAudioSessionManager2 audioSessionManager = obj as NativeInterfaces.IAudioSessionManager2;

        audioSessionManager.GetSessionEnumerator(out NativeInterfaces.IAudioSessionEnumerator sessionEnumerator);
        sessionEnumerator.GetCount(out int count);

        for (int j = 0; j < count; j++) {
            sessionEnumerator.GetSession(j, out NativeInterfaces.IAudioSessionControl control);
            NativeInterfaces.IAudioSessionControl2 control2 = control as NativeInterfaces.IAudioSessionControl2;
            control2.GetProcessId(out int pid);

            if (pids.Contains(pid)) {
                NativeInterfaces.ISimpleAudioVolume audioVolume = control as NativeInterfaces.ISimpleAudioVolume;
                audioVolume.SetMasterVolume(volume / 100.0f, Guid.Empty);
            }
        }
    }
}

Note: Solution 2 sets the volume relative to the current master volume. So if the current master volume is at 50 and you set the volume of an audio session to 50 it is set to 25.

Kaskorian
  • 426
  • 3
  • 18