Rewrite how picking a matching audio session works
rewrite the UI layer to make it only send updates to the stream deck if needed
This commit is contained in:
@ -5,11 +5,15 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SoundBrowser"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="150" Width="800">
|
||||
Title="MainWindow" Height="800" Width="800">
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="_tf">blah</TextBlock>
|
||||
</StackPanel>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock x:Name="_tf" Grid.Row="0">current</TextBlock>
|
||||
<TextBlock x:Name="_tf2" Grid.Row="1">list</TextBlock>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@ -1,9 +1,11 @@
|
||||
using CoreAudio;
|
||||
using FocusVolumeControl;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -19,98 +21,115 @@ using System.Windows.Shapes;
|
||||
|
||||
namespace SoundBrowser
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
dele = new WinEventDelegate(WinEventProc);
|
||||
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
|
||||
}
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
|
||||
AudioHelper _audioHelper;
|
||||
Native.WinEventDelegate _delegate;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_audioHelper = new AudioHelper();
|
||||
|
||||
//normally you can just pass a lambda, but for some reason, that seems to get garbage collected
|
||||
_delegate = new Native.WinEventDelegate(WinEventProc);
|
||||
Native.RegisterForForegroundWindowChangedEvent(_delegate);
|
||||
}
|
||||
|
||||
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||
{
|
||||
SetupCurrentAppFields();
|
||||
SetupAllSessionFields();
|
||||
}
|
||||
|
||||
private void SetupCurrentAppFields()
|
||||
{
|
||||
var handle = Native.GetForegroundWindow();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
//use this in debug to help there be less events
|
||||
|
||||
/*
|
||||
Native.GetWindowThreadProcessId(handle, out var fpid);
|
||||
var fp = Process.GetProcessById(fpid);
|
||||
|
||||
if(!fp.ProcessName.Contains("FSD"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
var processes = _audioHelper.GetPossibleProcesses();
|
||||
var session = _audioHelper.FindSession(processes);
|
||||
|
||||
foreach (var p in processes)
|
||||
{
|
||||
|
||||
sb.AppendLine($"pid: {p.Id}");
|
||||
sb.AppendLine($"\tprocessName: {p.ProcessName}");
|
||||
try
|
||||
{
|
||||
sb.AppendLine($"\tFileDescription: {p!.MainModule!.FileVersionInfo.FileDescription}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
sb.AppendLine("\tFileDescription: ##ERROR##");
|
||||
}
|
||||
|
||||
|
||||
WinEventDelegate dele = null;
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
if (session != null)
|
||||
{
|
||||
sb.AppendLine("picked the following best match");
|
||||
sb.AppendLine($"\tsession: {session.DisplayName}");
|
||||
sb.AppendLine($"\tvolume: {session.GetVolumeLevel()}");
|
||||
sb.AppendLine($"\tcount: {session.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine("No Match");
|
||||
}
|
||||
}
|
||||
|
||||
_tf.Text = sb.ToString();
|
||||
}
|
||||
|
||||
private void SetupAllSessionFields()
|
||||
{
|
||||
_tf2.Text = "";
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("-------------------------------------------------------------------------------");
|
||||
|
||||
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||
|
||||
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
|
||||
using var manager = device.AudioSessionManager2;
|
||||
|
||||
var sessions = manager!.Sessions;
|
||||
|
||||
foreach (var session in sessions!)
|
||||
{
|
||||
var audioProcess = Process.GetProcessById((int)session.ProcessID);
|
||||
|
||||
var displayName = audioProcess!.MainModule!.FileVersionInfo.FileDescription;
|
||||
|
||||
sb.AppendLine($"pid: {audioProcess.Id}");
|
||||
sb.AppendLine($"\tprocessName: {audioProcess.ProcessName}");
|
||||
sb.AppendLine($"\tsession: {displayName}");
|
||||
}
|
||||
|
||||
_tf2.Text = sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
|
||||
|
||||
private const uint WINEVENT_OUTOFCONTEXT = 0;
|
||||
private const uint EVENT_SYSTEM_FOREGROUND = 3;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int processId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
||||
|
||||
private static SimpleAudioVolume GetVolumeObject(Process process)
|
||||
{
|
||||
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||
|
||||
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
|
||||
using var manager = device.AudioSessionManager2;
|
||||
|
||||
var sessions = manager.Sessions;
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
if (session.ProcessID == process.Id)
|
||||
{
|
||||
return session.SimpleAudioVolume;
|
||||
}
|
||||
|
||||
var audioProcess = Process.GetProcessById((int)session.ProcessID);
|
||||
if(audioProcess?.ProcessName == process.ProcessName)
|
||||
{
|
||||
Console.WriteLine(process.MainModule.FileVersionInfo.FileDescription);
|
||||
return session.SimpleAudioVolume;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
SimpleAudioVolume _current;
|
||||
|
||||
private string GetActiveWindowTitle()
|
||||
{
|
||||
const int nChars = 256;
|
||||
IntPtr handle = IntPtr.Zero;
|
||||
StringBuilder Buff = new StringBuilder(nChars);
|
||||
handle = GetForegroundWindow();
|
||||
|
||||
if (handle <= 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var tid = GetWindowThreadProcessId(handle, out var pid);
|
||||
var process = Process.GetProcessById(pid);
|
||||
|
||||
var vol = GetVolumeObject(process);
|
||||
_current = vol;
|
||||
if(vol != null)
|
||||
{
|
||||
vol.Mute = true;
|
||||
}
|
||||
|
||||
return $"{pid} vol:{vol?.MasterVolume ?? -1} - {process.ProcessName}";
|
||||
}
|
||||
|
||||
|
||||
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||
{
|
||||
_tf.Text = GetActiveWindowTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
65
src/SoundBrowser/ParentProcessUtilities.cs
Normal file
65
src/SoundBrowser/ParentProcessUtilities.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SoundBrowser
|
||||
{
|
||||
/// <summary>
|
||||
/// A utility class to determine a process parent.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ParentProcessUtilities
|
||||
{
|
||||
// These members must match PROCESS_BASIC_INFORMATION
|
||||
internal IntPtr Reserved1;
|
||||
internal IntPtr PebBaseAddress;
|
||||
internal IntPtr Reserved2_0;
|
||||
internal IntPtr Reserved2_1;
|
||||
internal IntPtr UniqueProcessId;
|
||||
internal IntPtr InheritedFromUniqueProcessId;
|
||||
|
||||
[DllImport("ntdll.dll")]
|
||||
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent process of specified process.
|
||||
/// </summary>
|
||||
/// <param name="id">The process id.</param>
|
||||
/// <returns>An instance of the Process class.</returns>
|
||||
public static Process? GetParentProcess(int id)
|
||||
{
|
||||
Process process = Process.GetProcessById(id);
|
||||
return GetParentProcess(process.Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent process of a specified process.
|
||||
/// </summary>
|
||||
/// <param name="handle">The process handle.</param>
|
||||
/// <returns>An instance of the Process class.</returns>
|
||||
public static Process? GetParentProcess(IntPtr handle)
|
||||
{
|
||||
ParentProcessUtilities pbi = new ParentProcessUtilities();
|
||||
int returnLength;
|
||||
int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
|
||||
if (status != 0)
|
||||
throw new Win32Exception(status);
|
||||
|
||||
try
|
||||
{
|
||||
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -11,4 +11,8 @@
|
||||
<PackageReference Include="CoreAudio" Version="1.27.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FocusVolumeControl\FocusVolumeControl.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user