Windows Core Audio API - Изменение громкости приложения, для некоторых приложений выбран неправильный аудиоинтерфейс - PullRequest
2 голосов
/ 10 июля 2020

Я пытаюсь контролировать громкость запущенных в данный момент приложений в Windows. Проблема в том, что приложения иногда «назначаются» неправильному интерфейсу / устройству вывода.

Например: Мое основное аудиоустройство - это порт aux на задней панели моего p c. Но у меня также есть второй монитор, подключенный через HDMI, что позволяет мне использовать звук через HDMI. Другими словами, у меня есть 2 активных устройства вывода, основное аудиоустройство и монитор HDMI, который указан в диспетчере устройств как «Вывод NVIDIA - звук высокого разрешения NVIDIA».

Когда я запускаю следующий код, он кажется, выбирает неправильное устройство вывода для некоторых приложений (например, Foobar2000), что означает, что оно меняет громкость не на основном устройстве, а на аудио высокого разрешения NVIDIA. Я прекрасно слышу звук этих приложений, просто не могу изменить громкость. Это устраняет отключение кабеля HDMI, и я могу изменить громкость приложения на основном аудиоустройстве.

Вопрос: как выбрать основное аудиоустройство и игнорировать все остальные активные устройства вывода?

using System;
using System.Windows;
using System.IO.Ports;
using System.Threading;
using System.Windows.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
using WpfApp1.Properties;

namespace WpfApp1
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string strClassName, string strWindowName);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);



        int pID = 0;
        int pID1 = 0;
        int test;
        int testpos;
        string t;
        string a;

        public MainWindow()
        {
            InitializeComponent();

            rtbTest.AppendText(Settings.Default.appVolume01 + " + " + Settings.Default.appVolume02);

            foreach (var process in Process.GetProcesses())
            {
                if (process.ProcessName == "foobar2000" && !String.IsNullOrEmpty(process.MainWindowTitle))
                {
                    pID = process.Id;
                    tbPort.Text = pID.ToString();
                    if(Settings.Default.appVolume01 == 0)
                    {
                        sliderVolume.Value = Convert.ToInt32(VolumeMixer.GetApplicationVolume(pID));
                    }
                    else
                    {
                        sliderVolume.Value = Settings.Default.appVolume01;
                    }
                  
                    
                }
                if (process.ProcessName == "firefox" && !String.IsNullOrEmpty(process.MainWindowTitle))
                {
                    pID1 = process.Id;
                    tbReadDataBox.Text = pID1.ToString();
                    if (Settings.Default.appVolume02 == 0)
                    {
                        sliderVolume2.Value = Convert.ToInt32(VolumeMixer.GetApplicationVolume(pID1));
                    }
                    else
                    {
                        sliderVolume2.Value = Settings.Default.appVolume02;
                    }
                }
            }

            if (pID == 0)
            {
                return;
            }

        }
    
        private void Window_Closed(object sender, EventArgs e)
        {
            try
            {
                Settings.Default.appVolume01 = Convert.ToInt32(VolumeMixer.GetApplicationVolume(pID));
                Settings.Default.Save();
                
            }
            catch
            {
                MessageBox.Show("Error");
            }
            
        }

        private void sliderVolume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double test = sliderVolume.Value;
            VolumeMixer.SetApplicationVolume(pID, Convert.ToInt32(test));
        }

        private void sliderVolume2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double test2 = sliderVolume2.Value;
            VolumeMixer.SetApplicationVolume(pID1, Convert.ToInt32(test2));
            
        }

        public class VolumeMixer
        {

            
            public static float? GetApplicationVolume(int pid)
            {
                ISimpleAudioVolume volume = GetVolumeObject(pid);
                if (volume == null)
                {
                    return null;
                }
                float level;
                volume.GetMasterVolume(out level);
                Marshal.ReleaseComObject(volume);
                return level * 100;

            }

            public static bool? GetApplicationMute(int pid)
            {
                ISimpleAudioVolume volume = GetVolumeObject(pid);
                if (volume == null)
                {
                    return null;
                }

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

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

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

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

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

            private static ISimpleAudioVolume GetVolumeObject(int pid)
            {
                IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
                IMMDevice speakers;
                deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);

                Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
                object o;
                speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
                IAudioSessionManager2 mgr = (IAudioSessionManager2)o;

                IAudioSessionEnumerator sessionEnumerator;
                mgr.GetSessionEnumerator(out sessionEnumerator);
                int count;
                sessionEnumerator.GetCount(out count);

                ISimpleAudioVolume volumeControl = null;
                for (int i = 0; i < count; i++)
                {
                    IAudioSessionControl2 ctl;
                    sessionEnumerator.GetSession(i, out ctl);
                    int cpid;
                    ctl.GetProcessId(out cpid);

                    if (cpid == pid)
                    {
                        
                        volumeControl = ctl as ISimpleAudioVolume; 
                        
                        //Debug.WriteLine("CPID: " + cpid + " --- " + "PID: " + pid);

                        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);


        }

        [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);
        }

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

            [PreserveSig]
            int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);

        }

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

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

        [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);
        }

        [Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IAudioSessionControl2
        {
            [PreserveSig]
            int NotImpl0();

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

            [PreserveSig]
            int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);

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

            [PreserveSig]
            int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);

            [PreserveSig]
            int GetGroupingParam(out Guid pRetVal);

            [PreserveSig]
            int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);

            [PreserveSig]
            int NotImpl1();

            [PreserveSig]
            int NotImpl2();


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

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

            [PreserveSig]
            int GetProcessId(out int pRetVal);

            [PreserveSig]
            int IsSystemSoundsSession();

            [PreserveSig]
            int SetDuckingPreference(bool optOut);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...