Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/src/Media/MediaPlayer.cs
398 lines | 6.8 KiB | text/x-csharp | CSharpLexer
398 lines | 6.8 KiB | text/x-csharp | CSharpLexer
r0 | #region License | |||
/* FNA - XNA4 Reimplementation for Desktop Platforms | ||||
* Copyright 2009-2020 Ethan Lee and the MonoGame Team | ||||
* | ||||
* Released under the Microsoft Public License. | ||||
* See LICENSE for details. | ||||
*/ | ||||
#endregion | ||||
#region Using Statements | ||||
using System; | ||||
using System.Diagnostics; | ||||
#endregion | ||||
namespace Microsoft.Xna.Framework.Media | ||||
{ | ||||
public static class MediaPlayer | ||||
{ | ||||
#region Public Static Properties | ||||
public static bool GameHasControl | ||||
{ | ||||
get | ||||
{ | ||||
/* This is based on whether or not the player is playing custom | ||||
* music, rather than yours. | ||||
* -flibit | ||||
*/ | ||||
return true; | ||||
} | ||||
} | ||||
public static bool IsMuted | ||||
{ | ||||
get | ||||
{ | ||||
return INTERNAL_isMuted; | ||||
} | ||||
set | ||||
{ | ||||
INTERNAL_isMuted = value; | ||||
FAudio.XNA_SetSongVolume( | ||||
INTERNAL_isMuted ? | ||||
0.0f : | ||||
INTERNAL_volume | ||||
); | ||||
} | ||||
} | ||||
public static bool IsRepeating | ||||
{ | ||||
get; | ||||
set; | ||||
} | ||||
public static bool IsShuffled | ||||
{ | ||||
get; | ||||
set; | ||||
} | ||||
public static TimeSpan PlayPosition | ||||
{ | ||||
get | ||||
{ | ||||
return timer.Elapsed; | ||||
} | ||||
} | ||||
public static MediaQueue Queue | ||||
{ | ||||
get; | ||||
private set; | ||||
} | ||||
public static MediaState State | ||||
{ | ||||
get | ||||
{ | ||||
return INTERNAL_state; | ||||
} | ||||
private set | ||||
{ | ||||
if (INTERNAL_state != value) | ||||
{ | ||||
INTERNAL_state = value; | ||||
FrameworkDispatcher.MediaStateChanged = true; | ||||
} | ||||
} | ||||
} | ||||
public static float Volume | ||||
{ | ||||
get | ||||
{ | ||||
return INTERNAL_volume; | ||||
} | ||||
set | ||||
{ | ||||
INTERNAL_volume = MathHelper.Clamp( | ||||
value, | ||||
0.0f, | ||||
1.0f | ||||
); | ||||
FAudio.XNA_SetSongVolume( | ||||
IsMuted ? 0.0f : INTERNAL_volume | ||||
); | ||||
} | ||||
} | ||||
public static bool IsVisualizationEnabled | ||||
{ | ||||
get | ||||
{ | ||||
return FAudio.XNA_VisualizationEnabled() == 1; | ||||
} | ||||
set | ||||
{ | ||||
FAudio.XNA_EnableVisualization((uint) (value ? 1 : 0)); | ||||
} | ||||
} | ||||
#endregion | ||||
#region Public Static Variables | ||||
public static event EventHandler<EventArgs> ActiveSongChanged; | ||||
public static event EventHandler<EventArgs> MediaStateChanged; | ||||
#endregion | ||||
#region Private Static Variables | ||||
private static bool INTERNAL_isMuted = false; | ||||
private static MediaState INTERNAL_state = MediaState.Stopped; | ||||
private static float INTERNAL_volume = 1.0f; | ||||
private static bool initialized = false; | ||||
/* Need to hold onto this to keep track of how many songs | ||||
* have played when in shuffle mode. | ||||
*/ | ||||
private static int numSongsInQueuePlayed = 0; | ||||
/* FIXME: Ideally we'd be using the stream offset to track position, | ||||
* but usually you end up with a bit of stairstepping... | ||||
* | ||||
* For now, just use a timer. It's not 100% accurate, but it'll at | ||||
* least be consistent. | ||||
* -flibit | ||||
*/ | ||||
private static Stopwatch timer = new Stopwatch(); | ||||
private static readonly Random random = new Random(); | ||||
#endregion | ||||
#region Static Constructor | ||||
static MediaPlayer() | ||||
{ | ||||
Queue = new MediaQueue(); | ||||
} | ||||
#endregion | ||||
#region Public Static Methods | ||||
public static void MoveNext() | ||||
{ | ||||
NextSong(1); | ||||
} | ||||
public static void MovePrevious() | ||||
{ | ||||
NextSong(-1); | ||||
} | ||||
public static void Pause() | ||||
{ | ||||
if (State != MediaState.Playing || Queue.ActiveSong == null) | ||||
{ | ||||
return; | ||||
} | ||||
FAudio.XNA_PauseSong(); | ||||
timer.Stop(); | ||||
State = MediaState.Paused; | ||||
} | ||||
/// <summary> | ||||
/// The Play method clears the current playback queue and queues the specified song | ||||
/// for playback. Playback starts immediately at the beginning of the song. | ||||
/// </summary> | ||||
public static void Play(Song song) | ||||
{ | ||||
Song previousSong = Queue.Count > 0 ? Queue[0] : null; | ||||
Queue.Clear(); | ||||
numSongsInQueuePlayed = 0; | ||||
LoadSong(song); | ||||
Queue.ActiveSongIndex = 0; | ||||
PlaySong(song); | ||||
if (previousSong != song) | ||||
{ | ||||
FrameworkDispatcher.ActiveSongChanged = true; | ||||
} | ||||
} | ||||
public static void Play(SongCollection songs) | ||||
{ | ||||
Play(songs, 0); | ||||
} | ||||
public static void Play(SongCollection songs, int index) | ||||
{ | ||||
Queue.Clear(); | ||||
numSongsInQueuePlayed = 0; | ||||
foreach (Song song in songs) | ||||
{ | ||||
LoadSong(song); | ||||
} | ||||
Queue.ActiveSongIndex = index; | ||||
PlaySong(Queue.ActiveSong); | ||||
} | ||||
public static void Resume() | ||||
{ | ||||
if (State != MediaState.Paused) | ||||
{ | ||||
return; | ||||
} | ||||
FAudio.XNA_ResumeSong(); | ||||
timer.Start(); | ||||
State = MediaState.Playing; | ||||
} | ||||
public static void Stop() | ||||
{ | ||||
if (State == MediaState.Stopped) | ||||
{ | ||||
return; | ||||
} | ||||
FAudio.XNA_StopSong(); | ||||
timer.Stop(); | ||||
timer.Reset(); | ||||
for (int i = 0; i < Queue.Count; i += 1) | ||||
{ | ||||
Queue[i].PlayCount = 0; | ||||
} | ||||
State = MediaState.Stopped; | ||||
} | ||||
public static void GetVisualizationData(VisualizationData data) | ||||
{ | ||||
FAudio.XNA_GetSongVisualizationData( | ||||
data.freq, | ||||
data.samp, | ||||
VisualizationData.Size | ||||
); | ||||
} | ||||
#endregion | ||||
#region Internal Static Methods | ||||
internal static void Update() | ||||
{ | ||||
if ( Queue == null || | ||||
Queue.ActiveSong == null || | ||||
State != MediaState.Playing || | ||||
FAudio.XNA_GetSongEnded() == 0 ) | ||||
{ | ||||
// Nothing to do... yet... | ||||
return; | ||||
} | ||||
numSongsInQueuePlayed += 1; | ||||
if (numSongsInQueuePlayed >= Queue.Count) | ||||
{ | ||||
numSongsInQueuePlayed = 0; | ||||
if (!IsRepeating) | ||||
{ | ||||
Stop(); | ||||
FrameworkDispatcher.ActiveSongChanged = true; | ||||
return; | ||||
} | ||||
} | ||||
MoveNext(); | ||||
} | ||||
internal static void DisposeIfNecessary() | ||||
{ | ||||
if (initialized) | ||||
{ | ||||
FAudio.XNA_SongQuit(); | ||||
initialized = false; | ||||
} | ||||
} | ||||
internal static void OnActiveSongChanged() | ||||
{ | ||||
if (ActiveSongChanged != null) | ||||
{ | ||||
ActiveSongChanged(null, EventArgs.Empty); | ||||
} | ||||
} | ||||
internal static void OnMediaStateChanged() | ||||
{ | ||||
if (MediaStateChanged != null) | ||||
{ | ||||
MediaStateChanged(null, EventArgs.Empty); | ||||
} | ||||
} | ||||
#endregion | ||||
#region Private Static Methods | ||||
private static void LoadSong(Song song) | ||||
{ | ||||
/* Believe it or not, XNA duplicates the Song object | ||||
* and then assigns a bunch of stuff to it at Play time. | ||||
* -flibit | ||||
*/ | ||||
Queue.Add(new Song(song.handle, song.Name)); | ||||
} | ||||
private static void NextSong(int direction) | ||||
{ | ||||
Stop(); | ||||
if (IsRepeating && Queue.ActiveSongIndex >= Queue.Count - 1) | ||||
{ | ||||
Queue.ActiveSongIndex = 0; | ||||
/* Setting direction to 0 will force the first song | ||||
* in the queue to be played. | ||||
* if we're on "shuffle", then it'll pick a random one | ||||
* anyway, regardless of the "direction". | ||||
*/ | ||||
direction = 0; | ||||
} | ||||
if (IsShuffled) | ||||
{ | ||||
Queue.ActiveSongIndex = random.Next(Queue.Count); | ||||
} | ||||
else | ||||
{ | ||||
Queue.ActiveSongIndex = (int) MathHelper.Clamp( | ||||
Queue.ActiveSongIndex + direction, | ||||
0, | ||||
Queue.Count - 1 | ||||
); | ||||
} | ||||
Song nextSong = Queue[Queue.ActiveSongIndex]; | ||||
if (nextSong != null) | ||||
{ | ||||
PlaySong(nextSong); | ||||
} | ||||
FrameworkDispatcher.ActiveSongChanged = true; | ||||
} | ||||
private static void PlaySong(Song song) | ||||
{ | ||||
if (!initialized) | ||||
{ | ||||
FAudio.XNA_SongInit(); | ||||
initialized = true; | ||||
} | ||||
song.Duration = TimeSpan.FromSeconds(FAudio.XNA_PlaySong(song.handle)); | ||||
timer.Start(); | ||||
State = MediaState.Playing; | ||||
} | ||||
#endregion | ||||
} | ||||
} | ||||