Logo
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
EPL Standings
 
 
Windows Phone

Windows Phone 7 : Using Media Player to Shuffle Songs in Your Media Library

11/6/2011 11:23:20 AM

1. Problem

You need to retrieve songs from the media library of your Windows Phone 7 device and play them in shuffle mode.

2. Solution

You have to use the MediaLibrary class provided by the XNA Framework and use its property collections such as Songs and Albums. Moreover, you can play a song by using the Play static method defined in the MediaPlayer class.

3. How It Works

The XNA Framework provides the MediaLibrary class, in the Microsoft.XNA.Framework.dll assembly, which enables us to retrieve information from the media library of your phone. The MediaLibrary class provides a lot of useful media collections such as Songs, Pictures, and Albums. After you have a MediaLibrary object instance, you automatically have those collections filled with related information.

In this recipe, you are going to reproduce a song, so you are interested to the Songs collection. By using the Songs property from the MediaLibrary class, you can obtain a Song object pointing to a specified collection index.

The Song class contains everything concerning a song in the media library. Indeed, you can find the Album property to retrieve information on the album containing the song. The Artist property retrieves information on the song's author. The Duration property indicates the song's duration. The Genre property contains information on the song genre. The Name property is the song name. The TrackNumber property represents the song number within the album. Finally, the PlayCount and RatingFigure 1 shows the pertinent class diagrams. properties return the number of times the song has been played and the song's rating (if you have rated it), respectively.

Figure 1. The MediaLibrary, Song, and SongCollection class diagram

4. The Code

To demonstrate this recipe, we have created the ShuffleMe application by using the Silverlight Windows Phone 7 template from Visual Studio 2010.

Because the Silverlight application for Windows Phone 7 doesn't have support for accessing the media library, the first thing we did was to reference the Microsoft.Xna.Framework.dll assembly. Indeed, the XNA Framework has everything necessary to query the media library for pictures and songs.

After shuffling the songs and retrieving a random one, you are going to play the selected song by using the Play method provided by the MediaPlayer class. But because we have a Silverlight application calling the Play method, you need to do an extra step: you need to call the Update static method from the FrameworkDispatcher class. This call should be done periodically, so the Microsoft official documentation suggests the creation of a class implementing the IApplicationService interface. This interface has two method definitions, to start and to stop the service. In the related methods, you are going to start and stop a DispatcherTimer timer object. This timer has an interval set to 30 times per second in which it raises the Tick event. By defining the Tick event handler, you can call the Update static method periodically.

public class XNADispatcherService : IApplicationService
{
private DispatcherTimer frameworkDispatcherTimer;

public void StartService(ApplicationServiceContext context)
{
this.frameworkDispatcherTimer.Start();
}

public void StopService()
{
this.frameworkDispatcherTimer.Stop();
}

public XNADispatcherService()
{
this.frameworkDispatcherTimer = new DispatcherTimer();
this.frameworkDispatcherTimer.Interval = TimeSpan.FromTicks(333333);
this.frameworkDispatcherTimer.Tick += frameworkDispatcherTimer_Tick;
FrameworkDispatcher.Update();
}

void frameworkDispatcherTimer_Tick(object sender, EventArgs e) {
FrameworkDispatcher.Update(); }
}


In the App.xaml file, you can add the namespace that contains the XnaDispatcherService class and include a tag so that the application itself will start and stop the timer, automatically:

<Application
x:Class="ShuffleMe.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:local="clr-namespace:ShuffleMe">

<!--Application Resources-->
<Application.Resources>
</Application.Resources>

    <Application.ApplicationLifetimeObjects>
<!--Required object that handles lifetime events for the application-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
<local:XNADispatcherService />
</Application.ApplicationLifetimeObjects>

</Application>


The ShuffleMe application has some requirements, which can be summarized as follows:

  • It must continue to play and shuffle songs even when the screen is locked.

  • When one song is over, a new one must be played, and it shouldn't be the same.

  • When the application is tombstoned, it must save the song's properties such as its title and album cover.

  • When the application is reactivated from a tombstone, it must not stop the current played song and must again display the song's properties.

  • When the hardware Back button is pressed, the application must stop playing the song.

Let's examine the code and the solutions found to address those points.

You can set the ApplicationIdleDetectionMode property to IdleDetectionMode.Disabled so that your application doesn't stop working when the screen is locked:

// The shuffle routine has to work even when the screen is locked
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;


Sadly, the MediaLibrary doesn't provide either a property or a method to retrieve when the song is over. Our solution has been to create a DispatcherTimer object set to 1 second more than the song duration. Indeed, when the song is over (plus 1 second), the timer raises the Tick event that is hooked by the related event handler, and the private PlaySong method is called:

public partial class MainPage : PhoneApplicationPage
{
. . .
DispatcherTimer timer = null;

// Constructor
public MainPage()
{
. . .

timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);

// The shuffle routine has to work even when the screen is locked
PhoneApplicationService.Current.ApplicationIdleDetectionMode =
IdleDetectionMode.Disabled;
}

void timer_Tick(object sender, EventArgs e)
{
PlaySong();
}
. . .


When the application is tombstoned—for example, by the user pressing the hardware Start button—the application must store important data such the album cover and the song's author and title. Sadly, the Song class is not serializable, and the same is true for the BitmapImage class for the album cover. You need to create the AppSettings serializable class with specific information such as the Title property to store the application's title and the AlbumImage bytes array to store the image album. The latter is the way we found to serialize an image. In the IsTombstoned property, you set when the application is tombstoned. Finally, in the SongNumber property, you store the song's number generated by the shuffle routine.

public class AppSettings
{
public bool IsTombstoned { get; set; }
public string Title { get; set; }
public byte[] AlbumImage { get; set; }
public int SongNumber { get; set; }
}

NOTE

You could store only the SongNumber value during the tombstoning and use it to retrieve the song when the application is reactivated. However, in this way you can learn different techniques to manage images' serialization.

An object from the AppSettings class is stored in the App application class, and you can retrieve it by using the related settings property:

public partial class App : Application
{
/// <summary>
/// Provides easy access to the root frame of the Phone Application.
/// </summary>
/// <returns>The root frame of the Phone Application.</returns>
public PhoneApplicationFrame RootFrame { get; private set; }

public AppSettings settings { get; set; }
. . .

Moreover, in the App class, you define the event handlers to manage the tombstoning:

// Code to execute when the application is launching (e.g., from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
settings = new AppSettings();

settings.IsTombstoned = false;
}

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("settings"))
{
settings = PhoneApplicationService.Current.State["settings"] as AppSettings;
settings.IsTombstoned = true;

}
}

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
PhoneApplicationService.Current.State["settings"] = settings;
}


The other properties provided by the AppSettings class are set in the PlaySong method. The first operation in this method calls the DoShuffle method, which returns a Song object. This object is compared to a previous Song object stored as _lastSong variable. If the objects are equal, the PlaySong method is called again until a new Song object is retrieved. If the user has only one song in her library, the song is repeated each time until the application is closed.

NOTE

You could use the SongNumber property to check whether the DoShuffle method picked the same song, but we used a Song object to demonstrate that this class supports object comparison.

The PlaySong method contains a couple of interesting code snippets. The HasArt property is checked to see whether the song has an associated album cover. If it does, the GetAlbumArt method is used to retrieve the stream data of the image. One way to use the Stream object with the Image control—and specifically its Source property—is to create a WriteableBitmap object with the static DecodeJpeg method from the PictureDecoder class defined in the Microsoft.Phone namespace. If the song does not have an associated album cover, a No Cover image is retrieved from the image folder within the ShuffleMe project.

The AlbumImage bytes array defined in the settings is filled with the GetAlbumArt data thanks to the BinaryReader class and its ReadBytes method.

Finally, the Duration property from the Song class is used to set the Interval property of the timer, and then the timer is started.

private void PlaySong()
{
Song s = DoShuffle();
if ((s != null && s != _lastSong)||(library.Songs.Count == 1))
{
App app = Application.Current as App;

_lastSong = s;

tbAuthor.Text = s.Artist.Name + ": " + s.Name;
app.settings.Title = tbAuthor.Text;

if (s.Album.HasArt)
{
WriteableBitmap wbimg =
PictureDecoder.DecodeJpeg(s.Album.GetAlbumArt());
imgCover.Source = wbimg;
using (var br = new BinaryReader(s.Album.GetAlbumArt()))
app.settings.AlbumImage =
br.ReadBytes((int)s.Album.GetAlbumArt().Length);
}
else
{
app.settings.AlbumImage = null;
imgCover.Source = new BitmapImage(new Uri("/images/nocover.jpg",
UriKind.Relative));
}

MediaPlayer.Play(s);
timer.Interval = s.Duration + TimeSpan.FromSeconds(1);
timer.Start();
}
else
PlaySong();
}


When the application either starts or is tombstoned, the Loaded event for the MainPage page is raised and the private PlaySong method is called. But before calling this method, you check whether the application has been tombstoned or has been launched for the first time. Only in the latter case do you call the PlaySong method, because you want to avoid having a new song played when the application is reactivated by tombstoning. Indeed, in the tombstoning case, you simply rewrite the title and reset the album cover.

In the PhoneApplicationPage_Loaded event handler, there is an interesting thing that is worth noting. After tombstoning occurs, the timer is not working anymore, and so you need to set its interval again. But this time you can't use the song duration because some time has passed. So the PlayPosition property from the MediaPlayerTimeSpan object representing the song reproduction time. By subtracting this value from the song's duration, you obtain the new Interval class is used to retrieve a value of the timer.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
App app = Application.Current as App;

if (!app.settings.IsTombstoned)
PlaySong();
else
{
tbAuthor.Text = app.settings.Title;
if (app.settings.AlbumImage != null)
{
MemoryStream ms = new MemoryStream(app.settings.AlbumImage);
WriteableBitmap wbimg = PictureDecoder.DecodeJpeg(ms);
imgCover.Source = wbimg;
}
else
imgCover.Source = new BitmapImage(new Uri("/images/nocover.jpg",
UriKind.Relative));

TimeSpan remainTime = library.Songs[app.settings.SongNumber].Duration –
MediaPlayer.PlayPosition;
timer.Interval = remainTime + TimeSpan.FromSeconds(1);
timer.Start();
}
}


In the DoShuffle method code, you use the Random class to generate a number ranging from zero to the value of the Count property of the Songs collection, which returns the number of songs in the media library. Finally, this number is saved in the SongNumber property of the settings object, and the song at songIndex is returned from the Songs collection.

private Song DoShuffle()
{
App app = Application.Current as App;

int count = library.Songs.Count;

Random rand = new Random();
int songIndex = rand.Next(0, count);

app.settings.SongNumber = songIndex;
return library.Songs[songIndex];
}

The last requirement we have imposed on ourselves is that the application must end when the hardware Back button is pressed. In the PhoneApplicationPage class, you define the OnBackKeyPress method, which you can override so as to add your code before the back functionality is accomplished.

In this case, you stop the song that is playing and you stop the timer.

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
MediaPlayer.Stop();
timer.Stop();

base.OnBackKeyPress(e);
}


NOTE

Actually, at writing time, the MediaLibrary has an initialization bug that can be resolved with a workaround. In the MainPage class constructor, you can add the MediaPlayer.Queue.ToString(); call so that you force the library initialization.

5. Usage

From Visual Studio 2010, select the output target as Windows Phone 7 Emulator and press Ctrl+F5. The emulator starts, briefly showing the application, as in Figure 2. The song you play could be different from the one shown in Figure 7-8, because of the shuffle mode. The emulator provides three songs with no covers, so if you want to see the application with album covers as well, you should run it on a physical device.

Figure 2. The ShuffleMe application running in the emulator

Press the hardware Back button to terminate the application and its song. Or press the hardware Start button and then the hardware Back button to simulate the application tombstoning. You should hear the song continuing playing without any interruption and see the title, as before the tombstoning.

Now you can wait until the song ends in order to see that another (and different) song is played.

Top Search -----------------
- Windows Server 2008 R2 : Work with RAID Volumes - Understand RAID Levels & Implement RAID
- Windows Server 2008 R2 Administration : Managing Printers with the Print Management Console
- Configuring Email Settings in Windows Small Business Server 2011
- Windows Server 2008 R2 : Configuring Folder Security, Access, and Replication - Implement Permissions
- Monitoring Exchange Server 2010 : Monitoring Mail Flow
- Windows Server 2008 R2 :Task Scheduler
- Windows Server 2008 R2 : File Server Resource Manager
- Windows Server 2008 R2 : Installing DFS
- Exchange Server 2010 : Managing Anti-Spam and Antivirus Countermeasures
- Windows Server 2008 R2 : Configuring Folder Security, Access, and Replication - Share Folders
Other -----------------
- Windows Phone 7 : Picking a Photo from Your Media Library
- The Model-View-ViewModel Architecture (part 2) - GalaSoft MVVM Light Toolkit
- The Model-View-ViewModel Architecture (part 1) - MVVM Overview
- Developing for Windows Phone and Xbox Live : Graphics Performance
- Developing for Windows Phone and Xbox Live : General Performance
- Windows Phone 7 : Media Management - Taking a Photo from Your Phone Camera
- Windows Phone 7 : Sensors - Indicating the User's Position via Coordinates
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 3)
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 2) - Creating the Content Processor
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 1) - Building the Custom Animation Type
 
 
Most view of day
- Planning and Designing a Public Key Infrastructure : Identifying PKI Requirements
- Advanced Windows 7 Programming : Working in the Background - DEVELOPING TRIGGER-START SERVICES (part 3)
- Managing Windows Small Business Server 2011 : Adding a Terminal Server (part 2) - Installing the Remote Desktop Services Role
- Zero Touch Installations : Creating and Capturing a Reference Image (part 3) - Advertise the Reference Image Task Sequence, Run the Reference Image Task Sequence
- Microsoft Systems Management Server 2003 : Analysis and Troubleshooting Tools - Using SMS Trace (part 1) - Obtaining SMS Trace
- Adobe Dreamweaver CS5 : Using Library Items and Server-side Includes (part 1) - Using the Library Assets Panel - Adding a Library item
- Microsoft Dynamic AX 2009 : Report Customization (part 2) - Adding Promotional Materials to an Invoice Report
Top 10
- Windows Server 2012 : Managing networking using Windows PowerShell (part 2) - Examples of network-administration tasks
- Windows Server 2012 : Managing networking using Windows PowerShell (part 1) - Identifying networking cmdlets
- Sharepoint 2013 : Managing Site Security - Create Permission Levels for a Site
- Sharepoint 2013 : Managing Site Security - Edit a SharePoint Group’s Settings
- Sharepoint 2013 : Managing Site Security - Create a SharePoint Group for a Site
- Sharepoint 2013 : Assign Users’ Permissions on a Site
- Sharepoint 2013 : Get to a Site’s Permission Management Page (part 2) - Check What Permissions a User or a Group Has on a Site
- Sharepoint 2013 : Get to a Site’s Permission Management Page (part 1)
- Microsoft Exchange Server 2013 : Creating new mailboxes (part 4) - Automating mailbox settings,Ready-to-go custom attributes
- Microsoft Exchange Server 2013 : Creating new mailboxes (part 3) - Default folders, Manipulating mailbox settings
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
Cars Review