I'm trying to control the volume of currently running applications in Windows. The problem is, that applications are sometimes "assigned" to a wrong output interface/device.
For example: My primary audio device is the aux port on the back of my pc. But I also have a second monitor that is connected via HDMI that allows me to use audio over HDMI. In other words, I have 2 active output devices, the primary audio device and HDMI monitor which is listed in the device manager as "NVIDIA output - NVIDIA high definition audio".
When I run the following code, it seems to pick the wrong output device for some applications (like Foobar2000) which means it's not changing the volume on the primary device, but on the NVIDIA high definition audio instead. I can hear the audio of those applications just fine, I'm just not able to change the volume. Unplugging the HDMI cable fixes this and I'm able to change the volume of the application on the primary audio device.
The questions is: How can I select the primary audio device and ignore all other active output devices?
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);
}
}
}