EonaCat.VolumeMixer/EonaCat.VolumeMixer.Tester.WPF/MainWindow.xaml.cs

247 lines
7.6 KiB
C#

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
{
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<AudioDeviceViewModel> Devices { get; } = new();
public ObservableCollection<AudioSessionViewModel> 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));
}
}