using EonaCat.VolumeMixer.Managers; using EonaCat.VolumeMixer.Models; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using System.Windows.Threading; namespace EonaCat.VolumeMixer.Tester.WPF { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public ObservableCollection Devices { get; } = new(); public ObservableCollection Sessions { get; } = new(); private VolumeMixerManager _manager; private AudioDevice _currentDevice; private readonly DispatcherTimer _pollTimer = new() { Interval = TimeSpan.FromMilliseconds(250) }; private readonly DispatcherTimer _refreshTimer = new() { Interval = TimeSpan.FromSeconds(10) }; public MainWindow() { InitializeComponent(); DeviceSelector.ItemsSource = Devices; SessionList.ItemsSource = Sessions; _refreshTimer.Tick += (s, e) => RefreshSessions().ConfigureAwait(false); _pollTimer.Tick += PollTimer_Tick; LoadDevices(); } private async void PollTimer_Tick(object sender, EventArgs e) { foreach (var sessionVm in Sessions) { await sessionVm.PollRefreshAsync(); } } private async void LoadDevices() { _manager = new VolumeMixerManager(); var devices = await _manager.GetAudioDevicesAsync(DataFlow.All); Devices.Clear(); foreach (var device in devices) { Devices.Add(new AudioDeviceViewModel(device)); } var defaultDevice = await _manager.GetDefaultAudioDeviceAsync(); DeviceSelector.SelectedItem = Devices.FirstOrDefault(d => d.Id == defaultDevice.Id); } private async void DeviceSelector_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (DeviceSelector.SelectedItem is not AudioDeviceViewModel selectedVm) { _refreshTimer.Stop(); _pollTimer.Stop(); return; } _currentDevice = selectedVm.Device; await RefreshSessions(); _refreshTimer.Start(); _pollTimer.Start(); } private async Task RefreshSessions() { if (_currentDevice == null) { return; } var sessions = await _currentDevice.GetAudioSessionsAsync(); Sessions.Clear(); foreach (var session in sessions) { var vm = new AudioSessionViewModel(session); await vm.RefreshAsync(); Sessions.Add(vm); } } private void Slider_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (((FrameworkElement)sender).DataContext is AudioSessionViewModel vm) { vm.IsUserChangingVolume = true; } } private void Slider_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (((FrameworkElement)sender).DataContext is AudioSessionViewModel vm) { vm.IsUserChangingVolume = false; _ = vm.RefreshAsync(); } } private void Slider_PreviewTouchDown(object sender, System.Windows.Input.TouchEventArgs e) { if (((FrameworkElement)sender).DataContext is AudioSessionViewModel vm) { vm.IsUserChangingVolume = true; } } private void Slider_PreviewTouchUp(object sender, System.Windows.Input.TouchEventArgs e) { if (((FrameworkElement)sender).DataContext is AudioSessionViewModel vm) { vm.IsUserChangingVolume = false; _ = vm.RefreshAsync(); } } } public class AudioDeviceViewModel { public AudioDevice Device { get; } public string Display => Device.Name; public string Id => Device.Id; public BitmapImage Icon => new BitmapImage(new Uri("pack://application:,,,/Resources/" + (Device.DeviceType == DeviceType.Microphone ? "mic.png" : "speaker.png"))); public AudioDeviceViewModel(AudioDevice device) { Device = device; } } public class AudioSessionViewModel : INotifyPropertyChanged { private readonly AudioSession _session; private float _volume; private bool _isMuted; public string DisplayName => _session.DisplayName; private bool _isUserChangingVolume; public bool IsUserChangingVolume { get => _isUserChangingVolume; set { if (_isUserChangingVolume != value) { _isUserChangingVolume = value; OnPropertyChanged(); } } } public float Volume { get => _volume; set { if (Math.Abs(_volume - value) > 0.01) { value = Math.Clamp(value, 0, 1); if (_volume == value) { return; } _volume = value; OnPropertyChanged(); _ = SetVolumeSafeAsync(value); } } } private async Task SetVolumeSafeAsync(float value) { try { await _session.SetVolumeAsync(value); } catch { // Do nothing } } public bool IsMuted { get => _isMuted; set { if (_isMuted != value) { _isMuted = value; _ = _session.SetMuteAsync(value); OnPropertyChanged(); } } } public async Task PollRefreshAsync() { if (!IsUserChangingVolume) { float currentVolume = await _session.GetVolumeAsync(); if (Math.Abs(currentVolume - _volume) > 0.01) { _volume = currentVolume; OnPropertyChanged(nameof(Volume)); } bool currentMute = await _session.GetMuteAsync(); if (currentMute != _isMuted) { _isMuted = currentMute; OnPropertyChanged(nameof(IsMuted)); } } } public AudioSessionViewModel(AudioSession session) { _session = session; } public async Task RefreshAsync() { Volume = await _session.GetVolumeAsync(); IsMuted = await _session.GetMuteAsync(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }