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:
2023-08-06 21:51:04 -06:00
parent ab769bf7d2
commit a429a435bc
18 changed files with 888 additions and 448 deletions

View File

@ -9,55 +9,113 @@ using System.Threading.Tasks;
namespace FocusVolumeControl
{
internal class AudioHelper
{
ActiveAudioSessionWrapper GetSessionForProcess(Process process)
{
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
public class AudioHelper
{
ActiveAudioSessionWrapper _current;
List<Process> _currentProcesses;
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
using var manager = device.AudioSessionManager2;
public ActiveAudioSessionWrapper FindSession(List<Process> processes)
{
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
var sessions = manager.Sessions;
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
using var manager = device.AudioSessionManager2;
foreach (var session in sessions)
var sessions = manager.Sessions;
var matchingSession = new ActiveAudioSessionWrapper();
foreach (var session in sessions)
{
var audioProcess = Process.GetProcessById((int)session.ProcessID);
if (processes.Any(x => x.Id == session.ProcessID || x.ProcessName == audioProcess?.ProcessName))
{
try
{
var displayName = audioProcess.MainModule.FileVersionInfo.FileDescription;
if(string.IsNullOrEmpty(displayName))
{
displayName = audioProcess.ProcessName;
}
matchingSession.DisplayName = displayName;
}
catch
{
matchingSession.DisplayName ??= audioProcess.ProcessName;
}
matchingSession.ExecutablePath ??= audioProcess.MainModule.FileName;
//some apps like discord have multiple volume processes.
matchingSession.AddVolume(session.SimpleAudioVolume);
}
}
return matchingSession.Any() ? matchingSession : null;
}
public ActiveAudioSessionWrapper GetActiveSession()
{
var processes = GetPossibleProcesses();
if (_currentProcesses == null || !_currentProcesses.SequenceEqual(processes))
{
var audioProcess = Process.GetProcessById((int)session.ProcessID);
if (session.ProcessID == process.Id || audioProcess?.ProcessName == process.ProcessName)
{
var displayName = audioProcess.MainModule.FileVersionInfo.FileDescription;
var path = audioProcess.MainModule.FileName;
return new ActiveAudioSessionWrapper()
{
DisplayName = displayName,
ExecutablePath = path,
Volume = session.SimpleAudioVolume
};
}
}
return null;
}
internal ActiveAudioSessionWrapper GetActiveSession()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = Native.GetForegroundWindow();
if (handle == IntPtr.Zero)
{
//todo: return system or something like that?
return null;
_current = FindSession(processes);
}
var tid = Native.GetWindowThreadProcessId(handle, out var pid);
var process = Process.GetProcessById(pid);
_currentProcesses = processes;
return _current;
}
return GetSessionForProcess(process);
}
/// <summary>
/// Get the list of processes that might be currently selected
/// This includes getting the child window's processes
///
/// This helps to find the audo process for windows store apps whose process is "ApplicationFrameHost.exe"
///
/// The list may optionally include a parent process, because that helps thing steam to be more reliable because the steamwebhelper (ui) is a child of steam.exe
///
/// According to deej, getting the ForegroundWindow and enumerating steam windows should work, but it doesn't seem to work for me without including the parent process
/// https://github.com/omriharel/deej/blob/master/pkg/deej/util/util_windows.go#L22
///
/// but the parent process is sometimes useless (explorer, svchost, etc) so i filter some of them out because i felt like it when i wrote the code
///
/// 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()
{
var handle = Native.GetForegroundWindow();
if (handle == IntPtr.Zero)
{
return null;
}
}
var ids = Native.GetProcessesOfChildWindows(handle);
Native.GetWindowThreadProcessId(handle, out var pid);
ids.Insert(0, pid);
var processes = ids.Distinct()
.Select(x => Process.GetProcessById(x))
.ToList();
try
{
var blah = ParentProcessUtilities.GetParentProcess(pid);
if (blah != null && blah.ProcessName != "explorer" && blah.ProcessName != "svchost")
{
processes.Add(blah);
}
}
catch
{
}
return processes;
}
}
}