328 lines
7.2 KiB
C#
328 lines
7.2 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;
|
|
|
|
namespace Harmonia.WinUI.ViewModels;
|
|
|
|
public partial class PlayerViewModel : ViewModelBase
|
|
{
|
|
private readonly IAudioPlayer _audioPlayer;
|
|
private readonly IAudioBitmapImageCache _audioBitmapImageCache;
|
|
private readonly DispatcherTimer _timer;
|
|
|
|
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 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;
|
|
}
|
|
|
|
#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.GetForCurrentThread().TryEnqueue(() => SongImageSource = bitmapImage);
|
|
}
|
|
|
|
private void OnAudioPlayerPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
switch (e.PropertyName)
|
|
{
|
|
case nameof(_audioPlayer.State):
|
|
UpdateTimer();
|
|
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
|
|
} |