Files
harmonia/Harmonia.Core/Player/AudioPlayer.cs

283 lines
6.4 KiB
C#

using Harmonia.Core.Engine;
using Harmonia.Core.Playlists;
using System;
using System.ComponentModel;
using System.Reflection;
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;
}
private set
{
_playlist = value;
NotifyPropertyChanged(nameof(Playlist));
}
}
private PlaylistSong? _playingSong;
public PlaylistSong? PlayingSong
{
get
{
return _playingSong;
}
private set
{
_playingSong = value;
NotifyPropertyChanged(nameof(PlayingSong));
}
}
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 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)
{
// Alternative: Set the position to 0 and play again
if (PlayingSong != null)
{
await LoadAsync(PlayingSong, PlaybackMode.LoadAndPlay);
}
return;
}
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 || PlayingSong == 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(PlayingSong);
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 || PlayingSong == null)
return;
if (Position > PreviousSongSecondsThreshold)
{
Position = 0;
return;
}
int currentIndex = Playlist.Songs.IndexOf(PlayingSong);
int nextIndex = currentIndex - 1;
if (nextIndex < 0)
{
if (RepeatState == RepeatState.RepeatAll && Playlist.Songs.Count > 0)
{
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;
}
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));
}
}