Skip to content

Commit

Permalink
Enhancement: Added hotkeys, removed refresh button, made list fully a…
Browse files Browse the repository at this point in the history
…uto-update
  • Loading branch information
Ceiridge committed Sep 22, 2020
1 parent bed6b24 commit d3f1c16
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 25 deletions.
8 changes: 1 addition & 7 deletions App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows;

namespace WindowsAudioVolumeManager {
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion AudioSessionElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public bool Saved {
MainWindow.SavedSessions.Remove(SavedSession);
}

if(MainWindow.Parser != null) {
if (MainWindow.Parser != null) {
MainWindow.Parser.Dirty = true;
}
});
Expand Down
2 changes: 1 addition & 1 deletion ConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void LoadConfig() {
}

public void SaveConfig() {
if(SavedConfigObject == null) {
if (SavedConfigObject == null) {
SavedConfigObject = new ConfigObject();
SavedConfigObject.DefaultVolumeScalar = Window.DefaultSessionElement.ScalarVolume;
SavedConfigObject.SavedSessions = Window.SavedSessions;
Expand Down
51 changes: 51 additions & 0 deletions HotkeyRegistrar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace WindowsAudioVolumeManager {
public class HotkeyRegistrar {
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

public event EventHandler OnHotkeyPressed;
private IntPtr Handle;
private int HotKeyId;
private HwndSource hwndSource;

public HotkeyRegistrar(IntPtr handle, int hotKeyId) {
Handle = handle;
HotKeyId = hotKeyId;
hwndSource = HwndSource.FromHwnd(Handle);
}

public void RegisterHotkey(uint modifier, uint vk) {
RegisterHotKey(Handle, HotKeyId, modifier, vk);
hwndSource.AddHook(HwndHook);
}

public void UnregisterHotkey() {
UnregisterHotKey(Handle, HotKeyId);
hwndSource.RemoveHook(HwndHook);
}

private IntPtr HwndHook(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (message == 0x312 && wParam.ToInt32() == HotKeyId) { // WM_HOTKEY
OnHotkeyPressed?.Invoke(this, null);
handled = true;
}

return IntPtr.Zero;
}


public enum HotkeyModifier : uint {
MOD_ALT = 0x1,
MOD_CONTROL = 0x2,
MOD_NOREPEAT = 0x4000,
MOD_SHIFT = 0x4,
MOD_WIN = 0x8
}
}
}
7 changes: 4 additions & 3 deletions MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WindowsAudioVolumeManager"
mc:Ignorable="d"
Title="Ceiridge's Windows Audio Volume Manager" Height="460" Width="800" WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize">
Title="Ceiridge's Windows Audio Volume Manager" Height="460" Width="800" WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" StateChanged="Window_StateChanged">
<Grid>
<ComboBox x:Name="AudioOutputCombo" HorizontalAlignment="Left" Height="37" Margin="10,10,0,0" VerticalAlignment="Top" Width="600" FontSize="22"/>
<Button x:Name="RefreshAppsButton" Content="Refresh Apps" HorizontalAlignment="Right" Height="37" Margin="0,10,10,0" VerticalAlignment="Top" FontSize="22" Click="RefreshAppsButton_Click" IsEnabled="False"/>
<ComboBox x:Name="AudioOutputCombo" HorizontalAlignment="Center" Height="37" Margin="0,10,0,0" VerticalAlignment="Top" Width="780" FontSize="22"/>
<ListView x:Name="SessionListView" ItemsSource="{Binding}" HorizontalAlignment="Center" Height="324" Margin="0,100,0,0" VerticalAlignment="Top" Width="780">
<ListView.ItemTemplate>
<DataTemplate>
Expand All @@ -26,10 +27,10 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="RefreshAppsButton" Content="Refresh Apps" HorizontalAlignment="Right" Height="37" Margin="0,10,10,0" VerticalAlignment="Top" FontSize="22" Click="RefreshAppsButton_Click"/>
<Slider x:Name="MasterSlider" HorizontalAlignment="Left" Height="23" Margin="10,70,0,0" VerticalAlignment="Top" Width="372" Maximum="100" SmallChange="1" AutoToolTipPlacement="TopLeft" ValueChanged="MasterSlider_ValueChanged"/>
<Slider x:Name="DefaultSessionSlider" HorizontalAlignment="Left" Height="23" Margin="387,70,0,0" VerticalAlignment="Top" Width="389" Maximum="100" SmallChange="1" AutoToolTipPlacement="TopLeft" Value="{Binding Volume, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock HorizontalAlignment="Left" Margin="10,52,0,0" Text="Master Volume" TextWrapping="Wrap" VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" Margin="387,52,0,0" Text="Default Application Volume" TextWrapping="Wrap" VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" Margin="10,424,0,0" Text="Hotkey to show this window: CTRL+SHIFT+V" TextWrapping="Wrap" VerticalAlignment="Top"/>
</Grid>
</Window>
53 changes: 45 additions & 8 deletions MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace WindowsAudioVolumeManager {
public partial class MainWindow : Window {
public const string DEFAULT_APP_NAME = "_Default_App";

private readonly List<MMDevice> Devices = new List<MMDevice>();
private readonly ObservableCollection<AudioSessionElement> SessionControls = new ObservableCollection<AudioSessionElement>();
private readonly ObservableCollection<AudioSessionElement> SessionElements = new ObservableCollection<AudioSessionElement>();
private HotkeyRegistrar HotkeyRegistrar;

public readonly ConfigParser Parser;
public List<SavedAudioSession> SavedSessions = new List<SavedAudioSession>();
Expand All @@ -22,7 +24,7 @@ public partial class MainWindow : Window {

public MainWindow() {
InitializeComponent();
SessionListView.DataContext = SessionControls;
SessionListView.DataContext = SessionElements;
DefaultSessionElement = new AudioSessionElement(DEFAULT_APP_NAME, 1f, this);
DefaultSessionSlider.DataContext = DefaultSessionElement;

Expand Down Expand Up @@ -59,6 +61,15 @@ public MainWindow() {

Task.Run(VolumeManagerThread);
});

Task.Run(() => {
Thread.Sleep(5000); // Delay the hotkey registration to make sure the window exists
Invoke(() => {
HotkeyRegistrar = new HotkeyRegistrar(new WindowInteropHelper(this).Handle, unchecked((int)0xCE161D6E));
HotkeyRegistrar.OnHotkeyPressed += OnHotkeyPressed;
HotkeyRegistrar.RegisterHotkey((uint)HotkeyRegistrar.HotkeyModifier.MOD_CONTROL | (uint)HotkeyRegistrar.HotkeyModifier.MOD_SHIFT, 0x56); // VK_V
});
});
}

/// Has to run in an MTA thread
Expand All @@ -69,17 +80,17 @@ private void RefreshUIData() {
using AudioSessionManager2 sessionManager = AudioSessionManager2.FromMMDevice(device);
using AudioSessionEnumerator sessionEnumerator = sessionManager.GetSessionEnumerator();

Invoke(SessionControls.Clear);
Invoke(SessionElements.Clear);
foreach (AudioSessionControl session in sessionEnumerator) {
using SimpleAudioVolume volume = session.QueryInterface<SimpleAudioVolume>();
string name = GetSessionName(session);
SavedAudioSession savedSession = Invoke(() => SavedSessions.FirstOrDefault((ss) => ss.Name.Equals(name)));

if(savedSession == null) {
if (savedSession == null) {
volume.MasterVolume = DefaultSessionElement.ScalarVolume;
}

Invoke(() => SessionControls.Add(new AudioSessionElement(name, volume.MasterVolume, this, session, savedSession)));
Invoke(() => AddSessionElement(name, volume.MasterVolume, session, savedSession));
}

Invoke(() => {
Expand All @@ -106,13 +117,13 @@ private void VolumeManagerThread() {
foreach (AudioSessionControl session in sessionEnumerator) {
using SimpleAudioVolume volume = session.QueryInterface<SimpleAudioVolume>();
string name = GetSessionName(session);
AudioSessionElement element = Invoke(() => SessionControls.FirstOrDefault((element) => element.Name.Equals(name)));
AudioSessionElement element = Invoke(() => SessionElements.FirstOrDefault((element) => element.Name.Equals(name)));

if (element == null) {
SavedAudioSession savedSession = Invoke(() => SavedSessions.FirstOrDefault((ss) => ss.Name.Equals(name)));
volume.MasterVolume = savedSession != null ? savedSession.ScalarVolume : DefaultSessionElement.ScalarVolume;

Invoke(() => SessionControls.Add(new AudioSessionElement(name, volume.MasterVolume, this, session, savedSession)));
Invoke(() => AddSessionElement(name, volume.MasterVolume, session, savedSession));
} else {
float masterVol = volume.MasterVolume;

Expand All @@ -122,6 +133,13 @@ private void VolumeManagerThread() {
}
}
}

for (int i = SessionElements.Count - 1; i >= 0; i--) { // Reverse iteration to allow removal
AudioSessionElement element = SessionElements[i];
if (!sessionEnumerator.Any((session) => element.Name.Equals(GetSessionName(session)))) {
Invoke(() => SessionElements.RemoveAt(i));
}
}
} catch (Exception e) {
Console.WriteLine(e);
}
Expand All @@ -148,6 +166,13 @@ private string GetSessionName(AudioSessionControl sessionControl) {

return name.Equals(@"@%SystemRoot%\System32\AudioSrv.Dll,-202") ? "_System Sounds" : name;
}
private void AddSessionElement(string name, float scalarVolume, AudioSessionControl session, SavedAudioSession savedSession) {
if (!SessionElements.Any((element) => element.Name.Equals(name))) {
SessionElements.Add(new AudioSessionElement(name, scalarVolume, this, session, savedSession));
}
}



private void RefreshAppsButton_Click(object sender, RoutedEventArgs e) {
Task.Run(RefreshUIData);
Expand All @@ -158,9 +183,21 @@ private void MasterSlider_ValueChanged(object sender, RoutedPropertyChangedEvent
using AudioEndpointVolume masterVolume = AudioEndpointVolume.FromDevice(device);
masterVolume.MasterVolumeLevelScalar = (float)(MasterSlider.Value / 100f);

foreach (AudioSessionElement element in SessionControls) {
foreach (AudioSessionElement element in SessionElements) {
element.NotifyVolumeUpdate();
}
}

private void Window_StateChanged(object sender, EventArgs e) {
if (WindowState == WindowState.Minimized) {
Hide();
}
}

private void OnHotkeyPressed(object sender, EventArgs e) {
Show();
Focus();
WindowState = WindowState.Normal;
}
}
}
6 changes: 1 addition & 5 deletions SavedAudioSession.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace WindowsAudioVolumeManager {
namespace WindowsAudioVolumeManager {
public class SavedAudioSession {
public string Name { set; get; }
public float ScalarVolume { set; get; }
Expand Down

0 comments on commit d3f1c16

Please sign in to comment.