247 lines
7.6 KiB
C#
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));
|
|
}
|
|
} |