Improve sound browser to make it have more stuff in it
improve sound browser debuggability by making it capture the window earlier so you can set a breakpoint and not have it just get VS info put in code to try to handle helldivers 2
This commit is contained in:
@ -162,9 +162,9 @@ public class AudioHelper
|
||||
/// I also experimented with grabbing the parent process and enumerating through the windows to see if that would help, but any time the parent process was an unexpected process (explorer) it could blow up. so i decided not to bother for now
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<Process> GetPossibleProcesses()
|
||||
public List<Process> GetPossibleProcesses(IntPtr? handleOverride = null)
|
||||
{
|
||||
var handle = Native.GetForegroundWindow();
|
||||
var handle = handleOverride ?? Native.GetForegroundWindow();
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
@ -174,7 +174,25 @@ public class AudioHelper
|
||||
var ids = Native.GetProcessesOfChildWindows(handle);
|
||||
|
||||
Native.GetWindowThreadProcessId(handle, out var pid);
|
||||
ids.Insert(0, pid);
|
||||
|
||||
if(ids.Count == 0 && pid == 0)
|
||||
{
|
||||
foreach(var p in Process.GetProcesses())
|
||||
{
|
||||
if(p.MainWindowHandle == handle)
|
||||
{
|
||||
if(p.MainWindowTitle == "HELLDIVERS™ 2")
|
||||
{
|
||||
ids = FindAudioSessionByProcessName("helldivers2");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ids.Insert(0, pid);
|
||||
}
|
||||
|
||||
var processes = ids.Distinct()
|
||||
.Select(x => Process.GetProcessById(x))
|
||||
@ -287,4 +305,79 @@ public class AudioHelper
|
||||
return new SystemVolumeAudioSession(endpointVolume);
|
||||
}
|
||||
|
||||
|
||||
static Dictionary<string, List<int>> _audioSessionNameCache = new Dictionary<string, List<int>>();
|
||||
private List<int> GetFromNameCacheIfPossible(string processName)
|
||||
{
|
||||
if(_audioSessionNameCache.TryGetValue(processName, out var result))
|
||||
{
|
||||
if(result.Count!= 0)
|
||||
{
|
||||
foreach(var pid in result)
|
||||
{
|
||||
var p = GetProcessById(pid);
|
||||
if(p == null || p.ProcessName != processName)
|
||||
{
|
||||
_audioSessionNameCache.Remove(processName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private List<int> FindAudioSessionByProcessName(string processName)
|
||||
{
|
||||
var cached = GetFromNameCacheIfPossible(processName);
|
||||
if(cached != null)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
var results = new List<int>();
|
||||
Process bestProcessMatch = null;
|
||||
|
||||
var deviceEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
|
||||
|
||||
deviceEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active, out var deviceCollection);
|
||||
deviceCollection.GetCount(out var numDevices);
|
||||
for (int d = 0; d < numDevices; d++)
|
||||
{
|
||||
deviceCollection.Item(d, out var device);
|
||||
|
||||
Guid iid = typeof(IAudioSessionManager2).GUID;
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
|
||||
var manager = (IAudioSessionManager2)m;
|
||||
|
||||
device.GetId(out var currentDeviceId);
|
||||
|
||||
manager.GetSessionEnumerator(out var sessionEnumerator);
|
||||
|
||||
var currentIndex = int.MaxValue;
|
||||
|
||||
sessionEnumerator.GetCount(out var count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
sessionEnumerator.GetSession(i, out var session);
|
||||
session.GetDisplayName(out var displayName);
|
||||
session.GetProcessId(out var sessionProcessId);
|
||||
|
||||
var audioProcess = GetProcessById(sessionProcessId);
|
||||
|
||||
if(audioProcess != null && audioProcess.ProcessName == processName)
|
||||
{
|
||||
results.Add(sessionProcessId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results = results.Distinct().ToList();
|
||||
|
||||
_audioSessionNameCache[processName] = results;
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,10 @@ public sealed class ActiveAudioSessionWrapper : IAudioSession
|
||||
private IEnumerable<ISimpleAudioVolume> Volume => Sessions.Cast<ISimpleAudioVolume>();
|
||||
|
||||
public IconWrapper? IconWrapper { get; set; }
|
||||
public IEnumerable<int> Pids => Sessions.Select(x =>
|
||||
{
|
||||
x.GetProcessId(out var pid); return pid;
|
||||
});
|
||||
|
||||
public string GetIcon()
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Runtime.InteropServices;
|
||||
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
|
||||
|
||||
namespace FocusVolumeControl.AudioSessions;
|
||||
|
||||
@ -91,6 +92,91 @@ public interface IMMDeviceEnumerator
|
||||
int GetDefaultAudioEndpoint(DataFlow dataFlow, Role role, out IMMDevice device);
|
||||
}
|
||||
|
||||
public enum EStgmAccess
|
||||
{
|
||||
STGM_READ = 0x0
|
||||
}
|
||||
|
||||
[Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"),
|
||||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IPropertyStore
|
||||
{
|
||||
[PreserveSig]
|
||||
int GetCount(out int count);
|
||||
[PreserveSig]
|
||||
int GetAt(int iProp, out PropertyKey pkey);
|
||||
[PreserveSig]
|
||||
int GetValue(ref PropertyKey key, out PropVariant pv);
|
||||
[PreserveSig]
|
||||
int SetValue(ref PropertyKey key, ref PropVariant propvar);
|
||||
[PreserveSig]
|
||||
int Commit();
|
||||
}
|
||||
|
||||
public struct PropertyKey
|
||||
{
|
||||
public Guid fmtId;
|
||||
public int PId;
|
||||
|
||||
public PropertyKey(Guid fmtId, int pId)
|
||||
{
|
||||
this.fmtId = fmtId;
|
||||
this.PId = pId;
|
||||
}
|
||||
}
|
||||
public struct Blob
|
||||
{
|
||||
public int Length;
|
||||
public IntPtr Data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct PropVariant
|
||||
{
|
||||
[FieldOffset(0)] short vt;
|
||||
[FieldOffset(2)] short wReserved1;
|
||||
[FieldOffset(4)] short wReserved2;
|
||||
[FieldOffset(6)] short wReserved3;
|
||||
[FieldOffset(8)] sbyte cVal;
|
||||
[FieldOffset(8)] byte bVal;
|
||||
[FieldOffset(8)] short iVal;
|
||||
[FieldOffset(8)] ushort uiVal;
|
||||
[FieldOffset(8)] int lVal;
|
||||
[FieldOffset(8)] uint ulVal;
|
||||
[FieldOffset(8)] long hVal;
|
||||
[FieldOffset(8)] ulong uhVal;
|
||||
[FieldOffset(8)] float fltVal;
|
||||
[FieldOffset(8)] double dblVal;
|
||||
[FieldOffset(8)] Blob blobVal;
|
||||
[FieldOffset(8)] DateTime date;
|
||||
[FieldOffset(8)] bool boolVal;
|
||||
[FieldOffset(8)] int scode;
|
||||
[FieldOffset(8)] FILETIME filetime;
|
||||
[FieldOffset(8)] IntPtr everything_else;
|
||||
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var ve = (VarEnum)vt;
|
||||
|
||||
if((VarEnum)vt == VarEnum.VT_LPWSTR)
|
||||
{
|
||||
return Marshal.PtrToStringUni(everything_else);
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PKey
|
||||
{
|
||||
public static readonly PropertyKey DeviceFriendlyName = new(new(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 14);
|
||||
public static readonly PropertyKey DeviceFriendlyNameAttributes = new(new(0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b), 3);
|
||||
public static readonly PropertyKey DeviceInterfaceFriendlyName = new(new(0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22), 2);
|
||||
}
|
||||
|
||||
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IMMDevice
|
||||
{
|
||||
@ -98,7 +184,7 @@ public interface IMMDevice
|
||||
int Activate(ref Guid iid, CLSCTX dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
|
||||
|
||||
[PreserveSig]
|
||||
int NotImpl1();
|
||||
int OpenPropertyStore(EStgmAccess stgmAccess, out IPropertyStore propertyStore);
|
||||
[PreserveSig]
|
||||
int GetId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FocusVolumeControl.AudioSessions;
|
||||
|
||||
@ -15,4 +17,6 @@ public interface IAudioSession
|
||||
public void IncrementVolumeLevel(int step, int ticks);
|
||||
|
||||
public int GetVolumeLevel();
|
||||
|
||||
public IEnumerable<int> Pids { get; }
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FocusVolumeControl.AudioSessions;
|
||||
@ -15,6 +16,9 @@ internal sealed class SystemSoundsAudioSession : IAudioSession
|
||||
ISimpleAudioVolume _volumeControl;
|
||||
|
||||
public string DisplayName => "System sounds";
|
||||
|
||||
public IEnumerable<int> Pids => new int[0];
|
||||
|
||||
public string GetIcon() => "Images/systemSounds";
|
||||
|
||||
public void ToggleMute()
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FocusVolumeControl.AudioSessions;
|
||||
@ -15,6 +16,8 @@ internal sealed class SystemVolumeAudioSession : IAudioSession
|
||||
public string DisplayName => "System Volume";
|
||||
public string GetIcon() => "Images/encoderIcon";
|
||||
|
||||
public IEnumerable<int> Pids => new int[0];
|
||||
|
||||
public void ToggleMute()
|
||||
{
|
||||
_volumeControl.SetMute(!IsMuted(), Guid.Empty);
|
||||
|
Reference in New Issue
Block a user