Controlling Volume Mixer
Asked Answered
E

2

61

I want to control other application volume(firefox).

i can do it with Volume Mixer

enter image description here

What is the libraries of the Volume Mixer?

Engobe answered 13/1, 2013 at 17:20 Comment(3)
This is implemented in Windows by WASAPI, Windows Audio Session API. You'll need a wrapper for it, find google hits by searching for "c# wasapi"Erv
@Simon Mourier i think that this code control the system volume.i want to control firefox volume only.Engobe
I have been working on a CoreAudio library for .NET. The library is free and open source. A more robust implementation is available on the commercial MixerProNET LibraryEstimate
C
88

Here is a sample C# Console Application that does it. It's based on the Windows Core Audio Library. It works only on Windows 7 and higher.

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace SetAppVolumne
{
    class Program
    {
        static void Main(string[] args)
        {
            const string app = "Mozilla Firefox";

            foreach (string name in EnumerateApplications())
            {
                Console.WriteLine("name:" + name);
                if (name == app)
                {
                    // display mute state & volume level (% of master)
                    Console.WriteLine("Mute:" + GetApplicationMute(app));
                    Console.WriteLine("Volume:" + GetApplicationVolume(app));

                    // mute the application
                    SetApplicationMute(app, true);

                    // set the volume to half of master volume (50%)
                    SetApplicationVolume(app, 50);
                }
            }
        }

        public static float? GetApplicationVolume(string name)
        {
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return null;

            float level;
            volume.GetMasterVolume(out level);
            return level * 100;
        }

        public static bool? GetApplicationMute(string name)
        {
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return null;

            bool mute;
            volume.GetMute(out mute);
            return mute;
        }

        public static void SetApplicationVolume(string name, float level)
        {
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return;

            Guid guid = Guid.Empty;
            volume.SetMasterVolume(level / 100, ref guid);
        }

        public static void SetApplicationMute(string name, bool mute)
        {
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return;

            Guid guid = Guid.Empty;
            volume.SetMute(mute, ref guid);
        }

        public static IEnumerable<string> EnumerateApplications()
        {
            // get the speakers (1st render + multimedia) device
            IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers;
            deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);

            // activate the session manager. we need the enumerator
            Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
            object o;
            speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
            IAudioSessionManager2 mgr = (IAudioSessionManager2)o;

            // enumerate sessions for on this device
            IAudioSessionEnumerator sessionEnumerator;
            mgr.GetSessionEnumerator(out sessionEnumerator);
            int count;
            sessionEnumerator.GetCount(out count);

            for (int i = 0; i < count; i++)
            {
                IAudioSessionControl ctl;
                sessionEnumerator.GetSession(i, out ctl);
                string dn;
                ctl.GetDisplayName(out dn);
                yield return dn;
                Marshal.ReleaseComObject(ctl);
            }
            Marshal.ReleaseComObject(sessionEnumerator);
            Marshal.ReleaseComObject(mgr);
            Marshal.ReleaseComObject(speakers);
            Marshal.ReleaseComObject(deviceEnumerator);
        }

        private static ISimpleAudioVolume GetVolumeObject(string name)
        {
            // get the speakers (1st render + multimedia) device
            IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers;
            deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);

            // activate the session manager. we need the enumerator
            Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
            object o;
            speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
            IAudioSessionManager2 mgr = (IAudioSessionManager2)o;

            // enumerate sessions for on this device
            IAudioSessionEnumerator sessionEnumerator;
            mgr.GetSessionEnumerator(out sessionEnumerator);
            int count;
            sessionEnumerator.GetCount(out count);

            // search for an audio session with the required name
            // NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2)
            ISimpleAudioVolume volumeControl = null;
            for (int i = 0; i < count; i++)
            {
                IAudioSessionControl ctl;
                sessionEnumerator.GetSession(i, out ctl);
                string dn;
                ctl.GetDisplayName(out dn);
                if (string.Compare(name, dn, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    volumeControl = ctl as ISimpleAudioVolume;
                    break;
                }
                Marshal.ReleaseComObject(ctl);
            }
            Marshal.ReleaseComObject(sessionEnumerator);
            Marshal.ReleaseComObject(mgr);
            Marshal.ReleaseComObject(speakers);
            Marshal.ReleaseComObject(deviceEnumerator);
            return volumeControl;
        }
    }

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

    internal enum EDataFlow
    {
        eRender,
        eCapture,
        eAll,
        EDataFlow_enum_count
    }

    internal enum ERole
    {
        eConsole,
        eMultimedia,
        eCommunications,
        ERole_enum_count
    }

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IMMDeviceEnumerator
    {
        int NotImpl1();

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

        // the rest is not implemented
    }

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

        // the rest is not implemented
    }

    [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionManager2
    {
        int NotImpl1();
        int NotImpl2();

        [PreserveSig]
        int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);

        // the rest is not implemented
    }

    [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionEnumerator
    {
        [PreserveSig]
        int GetCount(out int SessionCount);

        [PreserveSig]
        int GetSession(int SessionCount, out IAudioSessionControl Session);
    }

    [Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionControl
    {
        int NotImpl1();

        [PreserveSig]
        int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);

        // the rest is not implemented
    }

    [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ISimpleAudioVolume
    {
        [PreserveSig]
        int SetMasterVolume(float fLevel, ref Guid EventContext);

        [PreserveSig]
        int GetMasterVolume(out float pfLevel);

        [PreserveSig]
        int SetMute(bool bMute, ref Guid EventContext);

        [PreserveSig]
        int GetMute(out bool pbMute);
    }
}

Note: I have not defined the interfaces completely, only what was needed for the code to work.

Coppins answered 14/1, 2013 at 16:43 Comment(16)
How can i change this code, so it changes the volume of Spotify?Equivocal
Maybe you can try with "Spotify" instead of "Mozilla Firefox"Coppins
Already tried, but the Volume mixer doesn't give back a DisplayName for Spotify, it only does for Firefox and some kind of .dllEquivocal
On Windows 7, there is an IAudioSessionControl2 interface that can get you the process id. From there you can determine if it's spotify or not. If you don't know how to do it, ask another question.Coppins
Simon, your simple & neat solution could be very useful for me as well, if audio-playing processes could have been enumerated by process-ID, rather than by Display-Name. Could you please post a modified version of the above, utilizing the IAudioSessionControl2 interface, to accomplish that, as you suggested. I tried, but... couldn't.Crisp
@bliss - this is another problem, so, like I said, you should ask another questionCoppins
Hello Simon. I've started another question. This link shall bring you to it. Thank you.Crisp
@Simon Mourier - i got this error in EnumerateApplications() function - here is screenshot: i.imgur.com/YzbgOKH.png Can you please help?Rockyrococo
@jstq - that's another problem. please create a new question.Coppins
@SimonMourier, But use this way how to set the system global volume?Zoila
While I appreciate the helpful answer that give us a hint, I also think doing a solution based on the window title is pure useless (what happens for processes that has an empty window title?, the solution returns empty strings because that), and the author has ignored the multiple requests to rework the solution to use a process id, because as it is is a very bad design and bad feedback/support that does not deserve to upvote his comments like some people did, sorry.Outroar
Anyway, adapting the code to find the process by a pid is easy, the steps: 1. implement the IAudioSessionControl2 interface which you can find here: github.com/xenolightning/AudioSwitcher/blob/… 2. in the EnumerateApplications function just convert the ctl object from IAudioSessionControl to IAudioSessionControl2, and finally do minor adaptions to call the ctl.GetProcessId function from that, and same in GetApplicationVolume, SetApplicationVolume, etc.Outroar
@Outroar - The display name is not the same as the window title and the solution by process id is available here since 2014: #20939434 but note it requires Windows 7.Coppins
I just tried it. In Windows 10, I have about a dozen apps running, but the loop prints out ``` name:"@%SystemRoot%\System32\AudioSrv.Dll,-202" name:"" name:"" ``` and that's all I get. What I really want is a background process that turns off one annoying app's sound. I would want it in a loop that sleeps and wakes up (ideally) only when that app is activated, but I will settle for just writing a loop that sleeps for a second and tries again. But in EnumerateApplications can't enumerate the applications, it isn't going to work terribly wellResultant
@SimonMourier, But use this way how to set the system global volume?Newmodel
@Newmodel - you should ask another questionCoppins
H
6

I know this is too late, but I needed this lately, I tried Simon's answer and couldn't get it to work, tried ElektroStudios's comment and Simon's other answer and still nothing, I don't know what I did wrong but I gave up on it.

I later found this asnwer and got it to work using CSCore

private static void Main(string[] args)
{
    using (var sessionManager = GetDefaultAudioSessionManager2(DataFlow.Render))
    {
        using (var sessionEnumerator = sessionManager.GetSessionEnumerator())
        {
            foreach (var session in sessionEnumerator)
            {
                using (var simpleVolume = session.QueryInterface<SimpleAudioVolume>())
                using (var sessionControl = session.QueryInterface<AudioSessionControl2>())
                {
                    if (sessionControl.ProcessID == 2436)
                        simpleVolume.MasterVolume = 0.5f;
                }
            }
        }
    }

    Console.ReadKey();
}

private static AudioSessionManager2 GetDefaultAudioSessionManager2(DataFlow dataFlow)
{
    using (var enumerator = new MMDeviceEnumerator())
    {
        using (var device = enumerator.GetDefaultAudioEndpoint(dataFlow, Role.Multimedia))
        {
            Debug.WriteLine("DefaultDevice: " + device.FriendlyName);
            var sessionManager = AudioSessionManager2.FromMMDevice(device);
            return sessionManager;
        }
    }
}
Helotism answered 9/10, 2020 at 1:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.