February 2012

Volume 27 Number 02

Touch and Go - Background Audio on Windows Phone 7.5

By Charles Petzold | February 2012

Charles PetzoldBack in the old days of MS-DOS, programmers could implement a crude form of task-switching with a technique called Terminate and Stay Resident (also known as TSR). TSR programs installed hooks into keyboard interrupts or other OS mechanisms and then terminated, leaving the program in memory ready to kick into action when the user pressed a particular key combination or something else of interest happened.

MS-DOS wasn’t equipped to handle even this level of rudimentary task-switching, so these TSRs created some serious problems. They would conflict with each other and frequently crash the whole OS. This was one of the primary reasons some of us welcomed Windows to supplement and later replace MS-DOS.

I mention this ancient history because I’m going to show you how to play music files in the background from a Windows Phone 7.5 application, and I know that developers have a tendency to think outside the box. Generally this is a good thing, but you shouldn’t use this technique for any purpose other than playing music files. Doing so might cause your application to be rejected by the Windows Phone Marketplace.

The technique I’ll show you is only for playing sound or music files from a Web location or isolated storage. If your application needs to play songs from the phone’s normal music library, you can do that using the MediaLibrary and MediaPlayer classes I discussed in the previous issue (msdn.microsoft.com/magazine/hh708760).

Application Plus DLL

In Windows Phone 7.5, an object that runs in the background to assist an application is known as an agent. To play music files in the background, you use a class that derives from AudioPlayerAgent. This class must be in a dynamic link library that’s referenced by the program. Program code does not itself run in the background; what runs in the background is this class that derives from AudioPlayerAgent.

Included with the downloadable code for this article is a Visual Studio solution named SimpleBackgroundAudio. For purposes of clarity, this program contains just about the minimum amount of code necessary to get background audio to work. I created the solution from the New Project dialog box by specifying Windows Phone Application, and then Windows Phone 7.1. (The 7.1 designation is used internally within the Windows Phone OS and Windows Phone applications, but it means the same thing as the more common 7.5 designation.)

To the MainPage class of the SimpleBackgroundAudio project I added a Button and a Click handler for that button:

void OnPlayButtonClick(object sender, RoutedEventArgs args)
{
  AudioTrack audioTrack = 
    new AudioTrack(new Uri("https://www.archive.org/.../Iv.Presto.mp3"), 
                   "Symphony No. 9: 4th Movement", 
                   "Felix Weingartner", 
                   "Beethoven: Symphony No. 9", 
                   null, 
                   null, 
                   EnabledPlayerControls.Pause);
  BackgroundAudioPlayer.Instance.Track = audioTrack;
}

The AudioTrack and BackgroundAudioPlayer classes are in the Microsoft.Phone.BackgroundAudio namespace. The URL (which I’ve shortened here to accommodate the magazine’s column width) references an MP3 file on the Internet Archive (archive.org) containing the last movement of Beethoven’s Symphony No. 9 as conducted by Felix Weingartner in a 1935 recording. (You can alternatively specify a URL that addresses a file in isolated storage; use the UriKind.Relative argument to the Uri constructor if that’s the case.) The next three arguments of the AudioTrack constructor provide album, artist and track information.

The BackgroundAudioPlayer class is somewhat similar to the MediaElement or MediaPlayer classes that play audio files in the foreground. BackgroundAudioPlayer has no constructor; instead you obtain the only instance with the static Instance property. In this code, the Track property is set to the AudioTrack object just created.

You can compile and run this program, but it won’t do anything. To make BackgroundAudioPlayer sing, you need a DLL containing a class that derives from AudioPlayerAgent. Visual Studio can create this class and library project for you. For my program, I right-clicked the SimpleBackgroundAudio solution name in Visual Studio, selected Add New Project from the menu and then chose Windows Phone Audio Playback Agent from the template list. I named this new project SimpleAudioPlaybackAgent.

Visual Studio creates a library project with a class named AudioPlayer that derives from AudioPlayerAgent, initialized with several skeleton methods. This is the class that runs in the background.

Very important: Create a reference from the application to this DLL! To do this, I right-clicked the References section under the SimpleBackgroundAudio project, selected Add Reference, and then in the dialog box I selected the Projects tab and then the SimpleAudioPlaybackAgent project.

In the AudioPlayerAgent derivative, you’ll want to modify at least two methods: OnPlayStateChanged and OnUserAction. The complete AudioPlayer class from this project (minus using directives and comments) is shown in Figure 1.

Figure 1 The AudioPlayer Class in SimpleBackgroundAudio

namespace SimpleAudioPlaybackAgent
{
  public class AudioPlayer : AudioPlayerAgent
  {
    protected override void OnPlayStateChanged(BackgroundAudioPlayer player,
                                      AudioTrack track, PlayState playState)
    {
      switch (playState)
      {
        case PlayState.TrackReady:
          player.Play();
          break;

        case PlayState.TrackEnded:
          player.Track = null;
          break;
      }
      NotifyComplete();
    }
    protected override void OnUserAction(BackgroundAudioPlayer player, 
                                      AudioTrack track, UserAction action, 
                                      object param)
    {
      switch (action)
      {
        case UserAction.Pause:
          player.Pause();
          break;

        case UserAction.Play:
          player.Play();
          break;
      }
      NotifyComplete();
    }

    protected override void OnError(BackgroundAudioPlayer player, 
                                      AudioTrack track, Exception error, 
                                      bool isFatal)
    {
        base.OnError(player, track, error, isFatal);
        NotifyComplete();
    }
    protected override void OnCancel()
    {
        base.OnCancel();
        NotifyComplete();
    }
  }
}

Look at the OnPlayStateChanged override first. This method is called when the PlayState property of the BackgroundAudioPlayer changes. The first argument is the same BackgroundAudioPlayer referenced in the program code; the last argument is a member of the PlayState enumeration.

When the program sets the Track property of the BackgroundAudioPlayer in the Click event handler, the BackgroundAudioPlayer accesses the music file over the Internet; the SimpleAudioPlaybackAgent DLL is loaded and the OnPlayStateChanged method eventually gets a call with the playState argument set to PlayState.TrackReady. It is the responsibility of OnPlayStateChanged to call the Play method of the BackgroundAudioPlayer object to start playing that track.

If the phone’s regular music player happens to be playing something at the time, it will be stopped. At this point, you can navigate away from the program or even terminate it by pressing the phone’s Back button, and the music will continue playing.

You can run this program in the Windows Phone emulator, but you’ll want to try it on an actual phone so you can press the phone’s volume control. This invokes a little drop-down known as the Universal Volume Control (UVC), which is an important part of background audio. The UVC displays the name of the track being played and the artist, based on the arguments that the application supplied to the AudioTrack constructor. The UVC also displays buttons for Previous Track, Pause and Next Track. In this program, only the Pause button is enabled because that’s what I specified in the last argument to the AudioTrack constructor. When you press the Pause button, it toggles between Pause and Play.

The AudioPlayer class handles these Pause and Play commands in the OnUserAction override shown in Figure 1. The UserAction argument indicates the particular button pressed by the user. OnUserAction responds by calling the appropriate method in the BackgroundAudioPlayer object.

When the track finishes playing, OnPlayStateChanged gets a call with PlayState.TrackEnded. The method responds by setting the Track property of the BackgroundAudioPlayer to null, which removes the item from the UVC. If you want, you can go back into the application and start the music playing again.

Notice that both OnPlayStateChanged and OnUserAction conclude with calls to NotifyComplete: This is required. Also notice that neither method includes a call to the base class method. These base class calls are part of the template that Visual Studio creates for you, but I had a problem with the OnUserAction override when the base method was called. The background audio sample code from Microsoft (available online from the Windows Phone 7.5 documentation area) also doesn’t include calls to the base methods.

A Very Strange DLL

When experimenting with background audio, you’ll want to keep a watch on the Output window in Visual Studio. When you run SimpleBackgroundAudio from the debugger, the Output window lists all the system libraries that are loaded on the phone to run the program, as well as the program itself, which is SimpleBackgroundAudio.dll. The lines in the Output window that list these libraries begin with the words “UI Task,” indicating the program.

When you tap the button in SimpleBackgroundAudio, you’ll see many of the same DLLs being loaded—but now with each line prefaced by the words “Background Task.” These DLLs include the application DLL as well as SimpleAudioPlaybackAgent.dll. The loading of these DLLs is one reason it takes a little bit of time for the music to begin playing after you tap the button.

As you experiment with programs that play background audio, you’ll probably want to insert Debug.WriteLine statements in all method overrides in the AudioPlayer class, and then study their real-time behavior in the Output window.

You also might want to create a constructor for the AudioPlayer class with another Debug.WriteLine statement. You’ll discover that a new instance of AudioPlayer is instantiated whenever a call needs to be made to OnPlayStateChanged or OnUserAction. Every call gets a new instance!

This simple fact has a profound implication: If you need this AudioPlayer class to maintain information between calls to OnPlayStateChanged and OnUserAction, you must keep that infor­mation in static fields or properties.

What if you need to pass information from a class in the SimpleBackgroundAudio program to the AudioPlayer class in the SimpleAudioPlaybackAgent library? It seems reasonable to define a public static method in the AudioPlayer class, and then call that method from MainPage or another class in the program. You can indeed do this, but it won’t do what you want: Anything saved from this method will not be available during calls to OnPlayStateChanged and OnUserAction.

Why won’t it be available? Recall the UI Task and Background Task designations in the Visual Studio Output window. These are two separate processes. The instance of the DLL referenced by your program is not the same as the instance that runs in the background, and therefore not even static data in a class in this DLL will be shared between the UI Task and the Background Task.

When testing a background audio application from the debugger in Visual Studio, you’ll experience some additional oddities. When you exit a program that has initiated some background audio, the audio keeps playing and Visual Studio indicates that code is still running. To resume editing your program, you’ll want to stop debugging directly from Visual Studio, and even then the music will often keep on playing. During development of a background audio program, you’ll probably find yourself frequently uninstalling the program from the phone.

Enhancing the Application

The SimpleBackgroundAudio program has a big problem. Although it has a button to start the music playing, it has no way to pause it or shut it off. It doesn’t even know when the music concludes or if anything else is happening. Yes, a user can always invoke the UVC to control background audio, but any program that starts music playing should also include its own controls to shut it off.

These enhancements are included in the project named Playlist­Player. As the name implies, this program plays a series of consecutive tracks—in this case, the 12 little piano pieces from Claude Debussy’s Preludes, Book 1, as performed by Alfred Cortot in a 1949 recording available from the Internet Archive.

I originally wanted to create all the AudioTrack objects within the program and then hand them off to the background audio DLL. That seemed the more generalized program structure, but I discovered it didn’t work because the application is accessing a different instance of the DLL than the one running in the background. Instead, I wrote the AudioPlayer class to create all the AudioTrack objects itself and store them in a generic List called playlist.

To make the program somewhat more challenging, I decided that the playlist wouldn’t be circular: I didn’t want to skip from the last track to the first track, or from the first to the last. For that reason, the first of the AudioTrack constructors has a last argument that combines the EnabledPlayerControls.Pause and EnabledPlayerControls.SkipNext flags; the last AudioTrack combines the Pause and SkipPrevious flags. All the others have all three flags. This is how the three buttons are enabled in the UVC for the various tracks.

Figure 2shows the OnPlayStateChanged and OnUserAction overrides. In OnPlayStateChanged, when one track ends, the next track is set to the Track property of the BackgroundAudioPlayer. The OnUserAction override handles the Previous Track and Next Track commands from the UVC by setting the Track property to the previous or next AudioTrack in the playlist.

Figure 2 The AudioPlayer Overrides in PlaylistPlayer

static List<AudioTrack> playlist = new List<AudioTrack>();
static int currentTrack = 0;

...

protected override void OnPlayStateChanged(BackgroundAudioPlayer player, 
  AudioTrack track, PlayState playState)
{
  switch (playState)
  {
    case PlayState.TrackReady:
      player.Play();
      break;

    case PlayState.TrackEnded:
      if (currentTrack < playlist.Count - 1)
      {
        currentTrack += 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;
  }
  NotifyComplete();
}

protected override void OnUserAction(BackgroundAudioPlayer player, 
  AudioTrack track, UserAction action, object param)
{
  switch (action)
  {
    case UserAction.Play:
      if (player.Track == null)
      {
        currentTrack = 0;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Play();
      }
      break;

    case UserAction.Pause:
      player.Pause();
      break;

    case UserAction.SkipNext:
      if (currentTrack < playlist.Count - 1)
      {
        currentTrack += 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;

    case UserAction.SkipPrevious:
      if (currentTrack > 0)
      {
        currentTrack -= 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;

    case UserAction.Seek:
      player.Position = (TimeSpan)param;
      break;
  }
  NotifyComplete();
}

The program’s MainPage class has a series of four standard application bar buttons to perform the same functions as the UVC. It also sets an event handler for the PlayStateChanged event of BackgroundAudioPlayer to update the screen with current track information, and a CompositionTarget.Rendering handler to update a Slider with current track progress, as shown in Figure 3.

The PlaylistPlayer Program
Figure 3 The PlaylistPlayer Program

The logic to enable and disable the application bar buttons is fairly simple: the Previous Track and Next Track buttons are enabled based on the PlayerControls property of the current AudioTrack; thus they should be consistent with the UVC. The Pause button is enabled if the player is playing; the Play button is enabled if the player is paused. If the current track is null, Play is enabled and all the other buttons are disabled.

The Click handlers of the four application bar buttons make calls to the BackgroundAudioPlayer methods SkipPrevious, Play, Pause and SkipNext, respectively. It’s important to understand that these method calls don’t directly control the operation of music playback. Instead, these method calls from the program trigger OnUserAction calls in the AudioPlayer class, and it’s the code in AudioPlayer that actually starts and stops the music.

This is somewhat peculiar, because it means that calls to the Play and Pause methods of BackgroundAudioPlayer behave differently depending whether they’re called from a program or from the OnUserAction override.

I also added a ValueChanged handler for the Slider to move to a particular location in the track. The handler calculates a new position for the track and sets an appropriate TimeSpan object to the Position property of the BackgroundAudioPlayer. Similar to the case with Play and Pause calls, setting this property does not change the position of the track. Instead, it generates a call to the OnUserAction override in AudioPlayer with an action argument of UserAction.Seek and the TimeSpan encoded in the param argument. The OnUserAction override then sets this TimeSpan object to the Position property of the BackgroundAudioPlayer, and that’s what actually causes the jump.

In practice, this Slider works fine when you just tap it to move ahead or back in the track by 10 percent. If you try to slide it, multiple Seek calls seem to build up, and the result is an audible mess. I would much prefer to use a regular ScrollBar rather than a Slider because then I could wait for an EndScroll event, which occurs when the user stops manipulating the control. Unfortunately, I’ve never been able to persuade the Windows Phone ScrollBar to work at all.

The Limitations

It’s been interesting to see how Windows Phone 7.5 has given programmers more direct access to the phone’s hardware (such as the video feed) as well as the ability to perform some background processing. But I can’t help thinking there’s a piece missing from this implementation of background audio.

Suppose a program wants to let the user pick from a list of music files to play in sequence. The program can’t hand off the entire list to the DLL, so it needs to take responsibility to setting the Track property of the BackgroundAudioPlayer when each track ends. But this can happen only when the program is running in the foreground.

It’s possible to pass some information between the application and the background DLL through the string Tag property of the AudioTrack class. But don’t bother deriving a new class from AudioTrack in hopes of passing additional information to the DLL: A copy is made of the AudioTrack object created by the application for passing to the DLL overrides.

Fortunately, the DLL has access to the application’s area of isolated storage, so the program can save a small file describing the playlist and then the DLL can access the playlist from the file. The bonus program for this column is a project named PlaylistFilePlayer, which demonstrates a simple approach to this technique.         


Charles Petzold is a longtime contributing editor to MSDN Magazine. His recent book, “Programming Windows Phone 7” (Microsoft Press, 2010), is available as a free download at bit.ly/cpebookpdf.

Thanks to the following technical expert for reviewing this article: Mark Hopkins