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)); PlaylistChanged?.Invoke(this, new()); } } 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? PlaylistChanged; 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 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 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 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)); } }