56

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?

Cœur
  • 32,421
  • 21
  • 173
  • 232
user1974519
  • 569
  • 1
  • 5
  • 3
  • A bit of google gives back this: http://www.dreamincode.net/forums/topic/45693-controlling-sound-volume-in-c%23/ – Simon Mourier Jan 13 '13 at 17:24
  • 4
    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" – Hans Passant Jan 13 '13 at 17:32
  • @Simon Mourier i think that this code control the system volume.i want to control firefox volume only. – user1974519 Jan 13 '13 at 17:35
  • 2
    I have been working on a [CoreAudio library for .NET](http://whenimbored.xfx.net/2011/01/core-audio-for-net/). The library is free and open source. A more robust implementation is available on the commercial [MixerProNET Library](http://software.xfx.net/netcl/mxp/index.htm) – xfx Jan 14 '13 at 13:47

2 Answers2

83

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.

Simon Mourier
  • 117,251
  • 17
  • 221
  • 269
  • How can i change this code, so it changes the volume of Spotify? – joostmakaay Dec 19 '13 at 12:10
  • 6
    Maybe you can try with "Spotify" instead of "Mozilla Firefox" – Simon Mourier Dec 19 '13 at 13:16
  • 3
    Already tried, but the Volume mixer doesn't give back a `DisplayName `for Spotify, it only does for Firefox and some kind of .dll – joostmakaay Dec 19 '13 at 13:19
  • 2
    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. – Simon Mourier Dec 19 '13 at 17:26
  • 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. – Bliss Jan 05 '14 at 15:50
  • 2
    @bliss - this is another problem, so, like I said, you should ask another question – Simon Mourier Jan 05 '14 at 19:23
  • Hello Simon. I've started another question. [This link](http://stackoverflow.com/questions/20938934/controlling-applications-volume-by-process-id) shall bring you to it. Thank you. – Bliss Jan 05 '14 at 20:45
  • 1
    @Simon Mourier - i got this error in EnumerateApplications() function - here is screenshot: http://i.imgur.com/YzbgOKH.png Can you please help? – csharp newbie Feb 26 '14 at 19:57
  • @jstq - that's another problem. please create a new question. – Simon Mourier Feb 27 '14 at 09:22
  • @SimonMourier, But use this way how to set the system global volume? – qakmak Apr 01 '15 at 06:14
  • 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. – ElektroStudios Aug 24 '18 at 02:48
  • 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: https://github.com/xenolightning/AudioSwitcher/blob/f1d7b75f13e108b0c40c73c85177abcfa8068b24/AudioSwitcher.AudioApi.CoreAudio/Internal/Interfaces/IAudioSessionControl2.cs 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. – ElektroStudios Aug 24 '18 at 02:53
  • 1
    @ElektroStudios - The display name is not the same as the window title and the solution by process id is available here since 2014: https://stackoverflow.com/questions/20938934/controlling-applications-volume-by-process-id but note it requires Windows 7. – Simon Mourier Aug 24 '18 at 06:29
3

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;
        }
    }
}
Elfor
  • 414
  • 2
  • 7