284 lines
6.5 KiB
C#
284 lines
6.5 KiB
C#
using Harmonia.Core.Engine;
|
|
using Harmonia.Core.Playlists;
|
|
using System.ComponentModel;
|
|
|
|
namespace Harmonia.Core.Player;
|
|
|
|
public class AudioPlayer : IAudioPlayer
|
|
{
|
|
private readonly IAudioEngine _audioEngine;
|
|
private readonly IPlaylistRepository _playlistRepository;
|
|
|
|
private Playlist? _playlist;
|
|
public Playlist? Playlist
|
|
{
|
|
get
|
|
{
|
|
return _playlist;
|
|
}
|
|
protected set
|
|
{
|
|
_playlist = value;
|
|
NotifyPropertyChanged(nameof(Playlist));
|
|
}
|
|
}
|
|
|
|
protected PlaylistSong? CurrentPlaylistSong { get; set; }
|
|
|
|
private PlaylistSong? _playingSong;
|
|
public PlaylistSong? PlayingSong
|
|
{
|
|
get
|
|
{
|
|
return _playingSong;
|
|
}
|
|
protected set
|
|
{
|
|
_playingSong = value;
|
|
NotifyPropertyChanged(nameof(PlayingSong));
|
|
PlayingSongChanged?.Invoke(this, new());
|
|
}
|
|
}
|
|
|
|
public double Position
|
|
{
|
|
get
|
|
{
|
|
return _audioEngine.Position.TotalSeconds;
|
|
}
|
|
set
|
|
{
|
|
_audioEngine.Position = TimeSpan.FromSeconds(value);
|
|
NotifyPropertyChanged(nameof(Position));
|
|
}
|
|
}
|
|
|
|
private RepeatState _repeatState;
|
|
public RepeatState RepeatState
|
|
{
|
|
get
|
|
{
|
|
return _repeatState;
|
|
}
|
|
set
|
|
{
|
|
_repeatState = value;
|
|
NotifyPropertyChanged(nameof(RepeatState));
|
|
}
|
|
}
|
|
|
|
public double Volume
|
|
{
|
|
get
|
|
{
|
|
return _audioEngine.Volume;
|
|
}
|
|
set
|
|
{
|
|
// Should the mute logic be here instead of the in view model?
|
|
_audioEngine.Volume = Convert.ToSingle(value);
|
|
NotifyPropertyChanged(nameof(Volume));
|
|
}
|
|
}
|
|
|
|
private bool _isRandom;
|
|
public bool IsRandom
|
|
{
|
|
get
|
|
{
|
|
return _isRandom;
|
|
}
|
|
set
|
|
{
|
|
_isRandom = value;
|
|
NotifyPropertyChanged(nameof(IsRandom));
|
|
}
|
|
}
|
|
|
|
public bool IsMuted
|
|
{
|
|
get
|
|
{
|
|
return _audioEngine.IsMuted;
|
|
}
|
|
set
|
|
{
|
|
_audioEngine.IsMuted = value;
|
|
NotifyPropertyChanged(nameof(IsMuted));
|
|
}
|
|
}
|
|
|
|
public AudioPlaybackState State => _audioEngine.State;
|
|
|
|
protected virtual int PreviousSongSecondsThreshold => 5;
|
|
|
|
public event EventHandler? PlayingSongChanged;
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
public AudioPlayer(IAudioEngine audioEngine, IPlaylistRepository playlistRepository)
|
|
{
|
|
_audioEngine = audioEngine;
|
|
_audioEngine.StreamFinished += OnAudioEngineStreamFinished;
|
|
_audioEngine.StateChanged += OnMusicEngineStateChanged;
|
|
|
|
_playlistRepository = playlistRepository;
|
|
}
|
|
|
|
private async void OnAudioEngineStreamFinished(object? sender, EventArgs e)
|
|
{
|
|
if (RepeatState == RepeatState.RepeatOne)
|
|
{
|
|
Position = 0;
|
|
Play();
|
|
}
|
|
else
|
|
{
|
|
await NextAsync();
|
|
}
|
|
}
|
|
|
|
private void OnMusicEngineStateChanged(object? sender, PlaybackStateChangedEventArgs e)
|
|
{
|
|
NotifyPropertyChanged(nameof(State));
|
|
}
|
|
|
|
public void Play()
|
|
{
|
|
_audioEngine.Play();
|
|
}
|
|
|
|
public void Pause()
|
|
{
|
|
_audioEngine.Pause();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_audioEngine.Stop();
|
|
}
|
|
|
|
public async Task NextAsync()
|
|
{
|
|
if (Playlist == null || CurrentPlaylistSong == null || Playlist.Songs.Count == 0)
|
|
return;
|
|
|
|
if (IsRandom)
|
|
{
|
|
int randomIndex = new Random().Next(0, Playlist.Songs.Count - 1);
|
|
await LoadAsync(randomIndex);
|
|
return;
|
|
}
|
|
|
|
int currentIndex = Playlist.Songs.IndexOf(CurrentPlaylistSong);
|
|
int nextIndex = currentIndex + 1;
|
|
|
|
if (nextIndex > Playlist.Songs.Count - 1)
|
|
{
|
|
PlaybackMode playbackMode = RepeatState == RepeatState.RepeatAll
|
|
? PlaybackMode.LoadAndPlay
|
|
: PlaybackMode.LoadOnly;
|
|
|
|
await LoadAsync(0, playbackMode);
|
|
return;
|
|
}
|
|
|
|
await LoadAsync(nextIndex);
|
|
}
|
|
|
|
public async Task PreviousAsync()
|
|
{
|
|
if (Playlist == null || CurrentPlaylistSong == null || Playlist.Songs.Count == 0)
|
|
return;
|
|
|
|
if (Position > PreviousSongSecondsThreshold)
|
|
{
|
|
Position = 0;
|
|
return;
|
|
}
|
|
|
|
int currentIndex = Playlist.Songs.IndexOf(CurrentPlaylistSong);
|
|
int nextIndex = currentIndex - 1;
|
|
|
|
if (nextIndex < 0)
|
|
{
|
|
if (RepeatState == RepeatState.RepeatAll)
|
|
{
|
|
await LoadAsync(Playlist.Songs.Count - 1);
|
|
return;
|
|
}
|
|
|
|
Stop();
|
|
return;
|
|
}
|
|
|
|
await LoadAsync(nextIndex);
|
|
}
|
|
|
|
public async Task<bool> LoadAsync(int index, PlaybackMode mode = PlaybackMode.LoadAndPlay)
|
|
{
|
|
if (Playlist == null)
|
|
return false;
|
|
|
|
if (index < 0 || index > Playlist.Songs.Count - 1)
|
|
return false;
|
|
|
|
return await LoadAsync(Playlist.Songs[index], mode);
|
|
}
|
|
|
|
public async Task<bool> LoadAsync(PlaylistSong song, PlaybackMode mode = PlaybackMode.LoadAndPlay)
|
|
{
|
|
if (Playlist == null || Playlist.Songs.Contains(song) == false)
|
|
{
|
|
//Playlist? newPlaylist = _playlistRepository.GetPlaylist(song);
|
|
|
|
Playlist? newPlaylist = _playlistRepository.Get().FirstOrDefault(playlist =>
|
|
playlist.Songs.Contains(song));
|
|
|
|
if (newPlaylist == null)
|
|
return false;
|
|
|
|
Playlist = newPlaylist;
|
|
}
|
|
|
|
CurrentPlaylistSong = song;
|
|
|
|
bool isLoaded = await TryLoadAsync(song);
|
|
|
|
if (isLoaded == false)
|
|
{
|
|
if (mode == PlaybackMode.LoadAndPlay)
|
|
{
|
|
await NextAsync();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
PlayingSong = song;
|
|
Position = 0;
|
|
|
|
if (mode == PlaybackMode.LoadAndPlay)
|
|
{
|
|
Play();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private async Task<bool> TryLoadAsync(PlaylistSong song)
|
|
{
|
|
try
|
|
{
|
|
return await _audioEngine.LoadAsync(song.Song.FileName);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected void NotifyPropertyChanged(string propertyName)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
} |