Logo
programming4us
programming4us
programming4us
programming4us
Home
programming4us
XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
Windows Phone

Programming Windows Phone 7 : Pivot and Panorama - The XNA Music Classes: MediaPlayer

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
5/29/2011 11:39:00 AM
To display music from the music library you use the XNA MediaLibrary and related classes. To actually play that music you use the static XNA MediaPlayer class.

The MediaPlayer class plays either a Song object, or all the songs in a SongCollection, or all the songs in a SongCollection beginning at a particular index. Those are the three variations of the static MediaPlayer.Play method.

You cannot create a SongCollection object yourself. You must always obtain an immutable SongCollection from one of the other classes (such as Album). This means that it’s not a simple matter to let the user select a particular subset of an album, or to rearrange the tracks in some way. That would require the program to maintain its own list of Song objects, and to play them sequentially. I chose not to implement anything like that for this relatively simple demonstration program.

Besides Play, MediaPlayer also defines Pause, Resume, and Stop methods, as well as MovePrevious and MoveNext to move to the previous or next item in a SongCollection.

The crucial properties of MediaPlayer are all get-only:

  • State, which returns a member of the MediaState enumeration: Playing, Paused, or Stopped.

  • PlayPosition, a TimeSpan object indicating the position within the currently playing song.

  • Queue, a MediaQueue object that contains a collection of the Song objects in the currently-playing collection as well as an ActiveSong property.

From the ActiveSong property, you can obtain the Album object and other information associated with that song.

MediaPlayer also defines two events:

  • MediaStateChanged

  • ActiveSongChanged

The code-behind file for AlbumPage is responsible for actually playing the album. But first take a look at the parts of the class that perform what might be considered the “housekeeping” chores:

Example 1. Silverlight Project: MusicByComposer File: AlbumPage.xaml.cs (excerpt)
public partial class AlbumPage : PhoneApplicationPage
{
// Used for switching play and pause icons
static Uri playButtonIconUri =
new Uri("/Images/appbar.transport.play.rest.png", UriKind.Relative);
static Uri pauseButtonIconUri =
new Uri("/Images/appbar.transport.pause.rest.png", UriKind.Relative);

int composerInfoIndex;
int albumInfoIndex;

public AlbumPage()
{
InitializeComponent();
appbarPlayPauseButton = this.ApplicationBar.Buttons[1] as
ApplicationBarIconButton;
}

protected override void OnNavigatedFrom(NavigationEventArgs args)
{
PhoneApplicationService.Current.State["ComposerInfoIndex"] = composerInfoIndex;
PhoneApplicationService.Current.State["AlbumInfoIndex"] = albumInfoIndex;

base.OnNavigatedFrom(args);
}

protected override void OnNavigatedTo(NavigationEventArgs args)
{
// Navigating from MainPage
if (this.NavigationContext.QueryString.ContainsKey("ComposerInfoIndex"))
{
composerInfoIndex =
Int32.Parse(this.NavigationContext.QueryString["ComposerInfoIndex"]);
albumInfoIndex =
Int32.Parse(this.NavigationContext.QueryString["AlbumInfoIndex"]);
}

// Reactivating from tombstoning
else if (PhoneApplicationService.Current.State.ContainsKey("ComposerInfoInd
ex"))
{
composerInfoIndex =
(int)PhoneApplicationService.Current.State["ComposerInfoIndex"];
albumInfoIndex =
(int)PhoneApplicationService.Current.State["AlbumInfoIndex"];
}

ComposerInfo composerInfo = MusicPresenter.Current.
Composers[composerInfoIndex];
AlbumInfo albumInfo = composerInfo.Albums[albumInfoIndex];

// Set page title and DataContext
PageTitle.Text = composerInfo.Composer;
this.DataContext = albumInfo;

// Get the media state when it changes and also right now
MediaPlayer.MediaStateChanged += OnMediaPlayerMediaStateChanged;
OnMediaPlayerMediaStateChanged(null, EventArgs.Empty);

base.OnNavigatedTo(args);
}
. . .
}


When being tombstoned, the OnNavigatedFrom method saves the two fields named composerInfoIndex and albumInfoIndex. These are the same two values that MainPage passes to AlbumPage in the navigation query string. The OnNavigatedTo method obtains those values either from the query string or the State property of the PhoneApplicationService to set the text of the PageTitle element (to display the name of the composer) and the DataContext of the page (so the bindings in AlbumPage.xaml work).

The OnNavigatedTo method also sets a handler for the MediaPlayer.MediaStateChanged event to maintain the correct icon image for the button that combines the functions of Play and Pause.

The event handler for that button turned out to be one of the trickier aspects of this class:

Example 2. Silverlight Project: MusicByComposer File: AlbumPage.xaml.cs (excerpt)
void OnAppbarPlayButtonClick(object sender, EventArgs args)
{
Album thisPagesAlbum = (this.DataContext as AlbumInfo).Album;

switch (MediaPlayer.State)
{
// The MediaPlayer is currently playing so pause it.
case MediaState.Playing:
MediaPlayer.Pause();
break;

// The MediaPlayer is currently paused. . .
case MediaState.Paused:
MediaQueue queue = MediaPlayer.Queue;

// so if we're on the same page as the paused song, resume it.
if (queue.ActiveSong != null &&
queue.ActiveSong.Album == thisPagesAlbum)
{
MediaPlayer.Resume();
}
// Otherwise, start playing this page's album.
else
{
goto case MediaState.Stopped;
}
break;

// The MediaPlayer is stopped, so play this page's album.
case MediaState.Stopped:
MediaPlayer.Play(thisPagesAlbum.Songs);
break;
}
}

void OnAppbarPreviousButtonClick(object sender, EventArgs args)
{
MediaPlayer.MovePrevious();
}

void OnAppbarNextButtonClick(object sender, EventArgs args)
{
MediaPlayer.MoveNext();
}


Once a program calls MediaPlayer.Play on a Song or SongCollection object, the music keeps going even if the user exits that program or the phone shuts off the screen and locks the display. This is how it should be. The user wants to listen to the music regardless—even to the point where the battery completely runs down.

For that reason, a program should be very cautious about calling MediaPlayer.Stop, because calling that method will stop the music without allowing it to be resumed. I found no reason to call MediaPlayer.Stop at all in my program.

The user can also exit a program such as MusicByComposer and then return to it, and the user should also be allowed to navigate to different album pages without interfering with the playing music. Yet, the user should also have the option of switching from the music currently playing to the album currently in view. It seemed to me that these choices implied four different cases when the user presses the play/pause button:

  • If music is currently playing, then the play/pause button displays the pause icon, and the currently playing music should be paused.

  • If the player is stopped, then the play/pause button displays the play icon, and the album in view should be played.

  • If the music is paused, then the play/pause button also displays the play icon. If the user is on the album page that’s currently active, then the play button should just resume whatever was playing.

  • However, if the music is paused but the user is on a different album page, then the play button should start playing the album on the current page.

In actual use, that logic seems to work well.

The only class you haven’t seen yet is SongTitleControl, an instance of which is used to display each individual song on the album. SongTitleControl is also responsible for highlighting the currently playing song and displaying the elapsed time and total duration of that song.

SongTitleControl just derives from UserControl and has a simple visual tree:

Example 3. Silverlight Project: MusicByComposer File: SongTitleControl.xaml (excerpt)
<Grid x:Name="LayoutRoot">
<StackPanel Margin="0 3">
<TextBlock Name="txtblkTitle"
Text="{Binding Name}"
TextWrapping="Wrap" />

<TextBlock Name="txtblkTime"
Margin="24 6"
Visibility="Collapsed" />
</StackPanel>
</Grid>

In AlbumPage.xaml, the SongTitleControl contains a binding on its Song property, which means that SongTitleControl must define a dependency property named Song of the XNA type Song. Here’s the definition of the Song property and the property-changed handlers:

Example 4. Silverlight Project: MusicByComposer File: SongTitleControl.xaml.cs (excerpt)
public static readonly DependencyProperty SongProperty =
DependencyProperty.Register("Song",
typeof(Song),
typeof(SongTitleControl),
new PropertyMetadata(OnSongChanged));

. . .

public Song Song
{
set { SetValue(SongProperty, value); }
get { return (Song)GetValue(SongProperty); }
}

static void OnSongChanged(DependencyObject obj, DependencyPropertyChangedEventArgs
args)
{
(obj as SongTitleControl).OnSongChanged(args);
}

void OnSongChanged(DependencyPropertyChangedEventArgs args)
{
if (Song != null)
MediaPlayer.ActiveSongChanged += OnMediaPlayerActiveSongChanged;
else
MediaPlayer.ActiveSongChanged -= OnMediaPlayerActiveSongChanged;

OnMediaPlayerActiveSongChanged(null, EventArgs.Empty);
}


If Song is set to a non-null value, then an event handler is set for the MediaPlayer.ActiveSongChanged event. That event is handled here:

Example 5. Silverlight Project: MusicByComposer File: SongTitleControl.xaml.cs (excerpt)
void OnMediaPlayerActiveSongChanged(object sender, EventArgs args)
{
if (this.Song == MediaPlayer.Queue.ActiveSong)
{
txtblkTitle.FontWeight = FontWeights.Bold;
txtblkTitle.Foreground = this.Resources["PhoneAccentBrush"] as Brush;
txtblkTime.Visibility = Visibility.Visible;
timer.Start();
}
else
{
txtblkTitle.FontWeight = FontWeights.Normal;
txtblkTitle.Foreground = this.Resources["PhoneForegroundBrush"] as Brush;
txtblkTime.Visibility = Visibility.Collapsed;
timer.Stop();
}
}


The Text property of txtblkTitle is handled with a binding in the XAML file. If the active song is the Song associated with this instance of SongTitleControl, then this TextBlock is highlighted with the accent color, the other TextBlock with the time information is made visible, and a DispatcherTimer is started:

Example 6. Silverlight Project: MusicByComposer File: SongTitleControl.xaml.cs (excerpt)
public partial class SongTitleControl : UserControl
{
DispatcherTimer timer = new DispatcherTimer();
. . .
public SongTitleControl()
{
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(0.25);
timer.Tick += OnTimerTick;
}
. . .
void OnTimerTick(object sender, EventArgs args)
{
TimeSpan dur = this.Song.Duration;
TimeSpan pos = MediaPlayer.PlayPosition;

txtblkTime.Text = String.Format("{0}:{1:D2} / {2}:{3:D2}",
(int)pos.TotalMinutes, pos.Seconds,
(int)dur.TotalMinutes, dur.Seconds);
}
}


That Tick handler simply formats the duration of the song and the current position for display purposes.

I thought about shifting some of this code to XAML, which would require defining a property for the elapsed time, as well as using the Visual State Manager for ActiveSong and NotActiveSong states, and then bringing in the StringFormatterConverter for formatting the two TimeSpan objects. But for this particular application the code file seemed the simpler of the two approaches.

Although you’ve seen many ways in which XAML is very powerful, sometimes code is really the right solution.
Other -----------------
- Programming Windows Phone 7 : Pivot and Panorama - Displaying the Albums
- Programming Windows Phone 7 : Pivot and Panorama - The XNA Music Classes: MediaLibrary
- Programming Windows Phone 7 : Pivot and Panorama - The XNA Connection
- Programming Windows Phone 7 : Pivot and Panorama - Music by Composer
- Programming Windows Phone 7 : Pivot and Panorama - Compare and Contrast
- Programming Windows Phone 7 : Elements and Properties - Modes of Opacity
- Programming Windows Phone 7 : Elements and Properties - Non-Tiled Tile Brushes & Playing Movies
- Programming Windows Phone 7 : Items Controls - A Card File Metaphor
- Programming Windows Phone 7 : Items Controls - The DataTemplate Bar Chart
- Programming Windows Phone 7 : Items Controls - Changing the Panel
 
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 windows Phone 7 windows Phone 8
programming4us programming4us
 
programming4us
Natural Miscarriage
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Game Trailer