Files
harmonia/Harmonia.WinUI/ViewModels/PlayerViewModel.cs
2025-03-30 23:18:40 -04:00

350 lines
7.8 KiB
C#

using CommunityToolkit.Mvvm.Input;
using Harmonia.Core.Engine;
using Harmonia.Core.Models;
using Harmonia.Core.Player;
using Harmonia.WinUI.Caching;
using Harmonia.WinUI.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.System;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
namespace Harmonia.WinUI.ViewModels;
public partial class PlayerViewModel : ViewModelBase
{
private readonly IAudioPlayer _audioPlayer;
private readonly IAudioBitmapImageCache _audioBitmapImageCache;
private readonly DispatcherTimer _timer;
private readonly DispatcherQueue _dispatcherQueue;
private CancellationTokenSource? _songImageCancellationTokenSource;
private Song? _song;
public Song? Song
{
get
{
return _song;
}
private set
{
SetProperty(ref _song, value);
CurrentPosition = 0; // What if player is being loaded and returning to save state?
Position = 0;
MaxPosition = value?.Length.TotalSeconds ?? 0;
}
}
private BitmapImage? _songImageSource;
public BitmapImage? SongImageSource
{
get
{
return _songImageSource;
}
private set
{
SetProperty(ref _songImageSource, value);
}
}
private double _currentPosition;
public double CurrentPosition
{
get
{
return _currentPosition;
}
set
{
SetProperty(ref _currentPosition, value);
if (_isPositionChangeInProgress)
_audioPlayer.Position = value;
}
}
private double _position;
public double Position
{
get
{
return _position;
}
private set
{
SetProperty(ref _position, value);
}
}
private double _maxPosition;
public double MaxPosition
{
get
{
return _maxPosition;
}
set
{
SetProperty(ref _maxPosition, value);
}
}
private bool _isPositionChangeInProgress;
public bool IsPositionChangeInProgress
{
get
{
return _isPositionChangeInProgress;
}
set
{
SetProperty(ref _isPositionChangeInProgress, value);
}
}
public double Volume
{
get
{
return _audioPlayer.Volume;
}
set
{
if (IsMuted)
IsMuted = false;
_audioPlayer.Volume = value;
OnPropertyChanged();
OnPropertyChanged(nameof(VolumeState));
}
}
public bool IsMuted
{
get
{
return _audioPlayer.IsMuted;
}
set
{
_audioPlayer.IsMuted = value;
OnPropertyChanged();
OnPropertyChanged(nameof(VolumeState));
}
}
public VolumeState VolumeState
{
get
{
if (IsMuted)
return VolumeState.Muted;
if (Volume == 0)
return VolumeState.Off;
if (Volume < .33)
return VolumeState.Low;
if (Volume < .66)
return VolumeState.Medium;
return VolumeState.High;
}
}
public bool IsRandom
{
get
{
return _audioPlayer.IsRandom;
}
private set
{
_audioPlayer.IsRandom = value;
OnPropertyChanged();
}
}
public RepeatState RepeatState
{
get
{
return _audioPlayer.RepeatState;
}
private set
{
_audioPlayer.RepeatState = value;
OnPropertyChanged();
}
}
public bool CanUpdatePosition
{
get
{
return _audioPlayer.State != AudioPlaybackState.Stopped;
}
private set
{
OnPropertyChanged();
}
}
public ICommand PlaySongCommand => new RelayCommand(Play);
public ICommand PauseSongCommand => new RelayCommand(Pause);
public ICommand StopSongCommand => new RelayCommand(Stop);
public ICommand PreviousSongCommand => new RelayCommand(Previous);
public ICommand NextSongCommand => new RelayCommand(Next);
public ICommand ToggleMuteCommand => new RelayCommand(ToggleMute);
public ICommand ToggleRandomizerCommand => new RelayCommand(ToggleRandomizer);
public ICommand ToggleRepeatCommand => new RelayCommand(ToggleRepeat);
public PlayerViewModel(IAudioPlayer audioPlayer, IAudioBitmapImageCache audioBitmapCache)
{
_audioPlayer = audioPlayer;
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
_audioPlayer.PropertyChanged += OnAudioPlayerPropertyChanged;
_audioBitmapImageCache = audioBitmapCache;
_timer = new()
{
Interval = TimeSpan.FromMilliseconds(100)
};
_timer.Tick += TickTock;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
}
#region Event Handlers
private void OnPlayingSongChanged(object? sender, EventArgs e)
{
Song = _audioPlayer.PlayingSong?.Song;
Task.Run(UpdateImage);
}
private async Task UpdateImage()
{
// TODO: Show default picture
if (Song == null)
return;
if (_songImageCancellationTokenSource != null)
await _songImageCancellationTokenSource.CancelAsync();
_songImageCancellationTokenSource = new();
CancellationToken cancellationToken = _songImageCancellationTokenSource.Token;
//BitmapImage? bitmapImage = await _audioBitmapImageCache.GetAsync(Song, cancellationToken);
_dispatcherQueue.TryEnqueue(async () =>
{
BitmapImage? bitmapImage = await _audioBitmapImageCache.GetAsync(Song, cancellationToken);
SongImageSource = bitmapImage;
});
}
private void OnAudioPlayerPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(_audioPlayer.State):
UpdateTimer();
OnPropertyChanged(nameof(CanUpdatePosition));
break;
}
}
private void UpdateTimer()
{
if (_audioPlayer.State == AudioPlaybackState.Playing)
{
_timer.Start();
}
else
{
_timer.Stop();
}
}
private void TickTock(object? sender, object e)
{
Position = _audioPlayer.Position;
if (IsPositionChangeInProgress)
return;
CurrentPosition = _audioPlayer.Position;
}
#endregion
#region Commands
public void Play()
{
_audioPlayer.Play();
}
public void Pause()
{
_audioPlayer.Pause();
}
public void Stop()
{
_audioPlayer.Stop();
CurrentPosition = 0;
Position = 0;
}
public void Previous()
{
_audioPlayer.PreviousAsync();
}
public void Next()
{
_audioPlayer.NextAsync();
}
public void ToggleMute()
{
IsMuted = !IsMuted;
}
public void ToggleRandomizer()
{
IsRandom = !IsRandom;
}
public void ToggleRepeat()
{
RepeatState = GetNextRepeatState();
}
private RepeatState GetNextRepeatState()
{
return _audioPlayer.RepeatState switch
{
RepeatState.Off => RepeatState.RepeatAll,
RepeatState.RepeatAll => RepeatState.RepeatOne,
RepeatState.RepeatOne => RepeatState.Off,
_ => _audioPlayer.RepeatState,
};
}
#endregion
}