// MonoGame - Copyright (C) MonoGame Foundation, Inc // This file is subject to the terms and conditions defined in // file 'LICENSE.txt', which is part of this source code package. using System; namespace Microsoft.Xna.Framework.Media { /// /// Provides methods and properties to play, pause, resume, and stop songs. /// MediaPlayer also exposes shuffle, repeat, volume, play position, and visualization capabilities. /// public static partial class MediaPlayer { // Need to hold onto this to keep track of how many songs // have played when in shuffle mode private static int _numSongsInQueuePlayed = 0; private static MediaState _state = MediaState.Stopped; private static float _volume = 1.0f; private static bool _isMuted; private static bool _isRepeating; private static bool _isShuffled; private static readonly MediaQueue _queue = new MediaQueue(); /// /// Raised when the active song changes due to active playback or due to explicit calls to the /// or methods. /// public static event EventHandler ActiveSongChanged; /// /// Raised when the media player play state changes. /// public static event EventHandler MediaStateChanged; static MediaPlayer() { PlatformInitialize(); } #region Properties /// /// Gets the media playback queue, . /// public static MediaQueue Queue { get { return _queue; } } /// /// Gets or set the muted setting for the media player. /// public static bool IsMuted { get { return PlatformGetIsMuted(); } set { PlatformSetIsMuted(value); } } /// /// Gets or sets the repeat setting for the media player. /// /// /// When set to , the playback queue will begin /// playing again after all songs in the queue have been played. /// public static bool IsRepeating { get { return PlatformGetIsRepeating(); } set { PlatformSetIsRepeating(value); } } /// /// Gets or sets the shuffle setting for the media player. /// /// /// When set to , songs in the playback queue are /// played in random order rather than from first to last. /// public static bool IsShuffled { get { return PlatformGetIsShuffled(); } set { PlatformSetIsShuffled(value); } } /// /// Gets or sets the visualization enabled setting for the media player. /// /// /// Always returns /// public static bool IsVisualizationEnabled { get { return false; } } /// /// Gets the play position within the currently playing song. /// public static TimeSpan PlayPosition { get { return PlatformGetPlayPosition(); } #if (IOS && !TVOS) || ANDROID set { PlatformSetPlayPosition(value); } #endif } /// /// Gets the media playback state, /// public static MediaState State { get { return PlatformGetState(); } private set { if (_state != value) { _state = value; EventHelpers.Raise(null, MediaStateChanged, EventArgs.Empty); } } } /// /// Determines whether the game has control of the background music. /// /// /// If a gamer is currently playing their own music as the background to your game, /// calls to , , , /// , , and /// might have no effect, depending on the platform. /// public static bool GameHasControl { get { return PlatformGetGameHasControl(); } } /// /// Gets or sets the media player volume /// /// /// Media player volume, from 0.0f (silence) to 1.0f (full volume relative to the current device volume) /// /// /// /// Volume adjustment is based on a decibel, not multiplicative, scale. /// Setting to 0.0 subtracts 96 dB from the volume. /// Setting to 1.0 subtracts 0 dB from the volume. /// Values in between 0.0f and 1.0f subtract dB from the volume proportionally. /// /// public static float Volume { get { return PlatformGetVolume(); } set { var volume = MathHelper.Clamp(value, 0, 1); PlatformSetVolume(volume); } } #endregion /// /// Pauses the currently playing song. /// public static void Pause() { if (State != MediaState.Playing || _queue.ActiveSong == null) return; PlatformPause(); State = MediaState.Paused; } /// /// Play clears the current playback queue, and then queues up the specified song for playback. /// Playback starts immediately at the beginning of the song. /// public static void Play(Song song) { Play(song, null); } /// /// Play clears the current playback queue, and then queues up the specified song for playback. /// Playback starts immediately at the given position of the song. /// public static void Play(Song song, TimeSpan? startPosition) { if (song == null) throw new ArgumentNullException("song", "This method does not accept null for this parameter."); var previousSong = _queue.Count > 0 ? _queue[0] : null; _queue.Clear(); _numSongsInQueuePlayed = 0; _queue.Add(song); _queue.ActiveSongIndex = 0; PlaySong(song, startPosition); if (previousSong != song) EventHelpers.Raise(null, ActiveSongChanged, EventArgs.Empty); } /// /// Play clears the current playback queue, and then queues up the specified song collection for playback. /// Playback starts immediately at the beginning of the song, specified by song collection index. /// public static void Play(SongCollection collection, int index = 0) { if (collection == null) throw new ArgumentNullException("collection", "This method does not accept null for this parameter."); _queue.Clear(); _numSongsInQueuePlayed = 0; foreach(var song in collection) _queue.Add(song); _queue.ActiveSongIndex = index; PlaySong(_queue.ActiveSong, null); } private static void PlaySong(Song song, TimeSpan? startPosition) { if (song != null && song.IsDisposed) throw new ObjectDisposedException("song"); PlatformPlaySong(song, startPosition); State = MediaState.Playing; } internal static void OnSongFinishedPlaying(object sender, EventArgs args) { // TODO: Check args to see if song sucessfully played _numSongsInQueuePlayed++; if (_numSongsInQueuePlayed >= _queue.Count) { _numSongsInQueuePlayed = 0; if (!IsRepeating) { Stop(); EventHelpers.Raise(null, ActiveSongChanged, EventArgs.Empty); return; } } MoveNext(); } /// /// Resumes a paused song. /// public static void Resume() { if (State != MediaState.Paused) return; PlatformResume(); State = MediaState.Playing; } /// /// Stops playing a song. /// public static void Stop() { if (State == MediaState.Stopped) return; PlatformStop(); State = MediaState.Stopped; } /// /// Stops currently playing song, moves to the next song in the queue of playing songs and plays it. /// /// /// /// If the current song is the last song in the queue, will stay on current song /// /// public static void MoveNext() { NextSong(1); } /// /// Stops currently playing song, moves to the previous song in the queue of playing songs and plays it. /// /// /// /// If the current song is the first song in the queue, will stay on current song /// /// public static void MovePrevious() { NextSong(-1); } 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; } var nextSong = _queue.GetNextSong(direction, IsShuffled); if (nextSong != null) PlaySong(nextSong, null); EventHelpers.Raise(null, ActiveSongChanged, EventArgs.Empty); } } }