IT tutorials
 
Technology
 

Windows Phone 8 : Background Agents (part 3) - Audio Agent

8/17/2013 11:10:42 AM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

3. Audio Agent

Although you can write applications that play audio , you also might want that audio to continue regardless of whether your application is running. In addition to continuing to play the audio, you might want to integrate with the Universal Volume Control (which enables the user to move to pause audio as well as move to the previous and next tracks). You can see the Universal Volume Control (UVC) in Figure 5.

Image

FIGURE 5 The Universal Volume Control in action

To be able to have audio play in the background, you need to have an audio agent. There are two types of audio agents: audio playback agents and audio streaming agents. For this example, we’ll focus on adding an audio playback agent because that is a much more common case.

Background audio works by your application having an audio agent associated with it. This audio agent is solely responsible for handing requests to change the audio in any way (for example, move tracks, pause, and so on). Your application can have controls that request those changes, but it is ultimately the agent that does the work so that the same agent code is used when the user changes the audio (again, moving tracks, pausing, and so on) through the phone’s built-in controls (like the UVC).

To get started, you need to add a new Audio Playback Agent project to your project , as shown in Figure 6.

Image

FIGURE 6 Adding an audio agent to your project

This new project not only creates a project for the audio agent, but also modifies your WMAppManifest.xml file (similar to what the Scheduled Task Agent project did earlier):

<Deployment ...>
  <App ...>
    ...
    <Tasks>
      <DefaultTask Name="_default"
                   NavigationPage="MainPage.xaml" />
      <ExtendedTask Name="BackgroundTask">
        <BackgroundServiceAgent Specifier="AudioPlayerAgent"
                                Name="MyAudioAgent"
                                Source="MyAudioAgent"
                                Type="MyAudioAgent.AudioPlayer" />
      </ExtendedTask>
    </Tasks>
    ...
  </App>
</Deployment>

The BackgroundServiceAgent element added here specifies that the specifier of the agent is an AudioPlayerAgent, which tells the operating system that the AudioPlayer class is responsible for playing audio if the application decides to play background audio. Before you can use the new audio agent, you will need to add a reference to the audio agent project from your main phone application project.

Unlike the prior agent types, you do not need to register this type of agent with the ScheduledActionService. Instead, you will simply use the BackgroundAudioPlayer class. This class has a static property called Instance that returns the singleton background player object. You can use this class to tell the background audio to start, skip, or stop (or even seek). For instance, to implement a button in your application that can play or pause the current audio, you could do the following:

private void playButton_Click(object sender, RoutedEventArgs e)
{
  // Tell the agent to play or pause
  if (BackgroundAudioPlayer.Instance.PlayerState !=
        PlayState.Playing)
  {
    BackgroundAudioPlayer.Instance.Play();
  }
  else
  {
    BackgroundAudioPlayer.Instance.Pause();
  }
}

What is important to see here is that all your application’s code should do is send commands to the background audio player (which is controlled by the agent). When you tell the BackgroundAudioPlayer class to “play,” it then routes that command to the agent where all the hard work is done. So, let’s look at the skeleton of the audio agent the new project created for you:

public class AudioPlayer : AudioPlayerAgent
{
  protected override void OnPlayStateChanged(
    BackgroundAudioPlayer player,
    AudioTrack track,
    PlayState playState)
  {
    //TODO: Add code to handle play state changes

    NotifyComplete();
  }

  protected override void OnUserAction(
    BackgroundAudioPlayer player,
    AudioTrack track,
    UserAction action,
    object param)
  {
    //TODO: Add code to handle user actions
    // through the application and system-provided UI

    NotifyComplete();
  }

  protected override void OnError(
    BackgroundAudioPlayer player,
    AudioTrack track,
    Exception error,
    bool isFatal)
  {
    //TODO: Add code to handle error conditions

    NotifyComplete();
  }

  protected override void OnCancel()
  {
  }
}

The agent class contains four different methods that are called in different cases while background audio is playing. Let’s start with OnUserAction:

protected override void OnUserAction(
  BackgroundAudioPlayer player,
  AudioTrack track,
  UserAction action,
  object param)
{
  // May be initiated from the app or the UVC
  switch (action)
  {
    case UserAction.Play:
      {
        PlayCurrentTrack();
        break;
      }
    case UserAction.Pause:
      {
        player.Pause();
        break;
      }
    case UserAction.SkipNext:
      {
        MoveToNextSong();
        break;
      }
    case UserAction.SkipPrevious:
      {
        MoveToPreviousSong();
        break;
      }
  }

  NotifyComplete();
}

This method is called whenever the user makes a request to manipulate the currently playing music (including the first request to “play” a song). It passes in the current instance of the BackgroundAudioPlayer so you can manipulate the song to be played directly (as is shown when the UserAction is Pause). For playing and moving tracks, the list of tracks is owned by the audio agent (not the background audio player). This means you will typically need your audio agent to keep track of the list of tracks and the current position in that list. Any state you need to keep around needs to be static as the specific instance of the audio agent should be stateless (because it can be destroyed or garbage-collected if necessary). So, for this example you need to store both the list of tracks and the track counter as static data:

public class AudioPlayer : AudioPlayerAgent
{
  // Song Counter
  static int _currentSong = 0;

  // Songs
  static SongList _songList = new SongList();

  // ...
}

This enables you to keep the list of songs the user has requested (it can be stored in isolated storage or another mechanism for sharing between the audio agent and the application). Then the audio agent can implement the PlayCurrentTrack (which this example calls when a user requests to begin playing), like so:

void PlayCurrentTrack()
{
  // Retrieve a Song object that contains the song to play
  var song = _songList.Songs.ElementAt(_currentSong);

  // Build Audio Track
  var track = new AudioTrack(song.Path,
    song.Name,
    song.Artist,
    song.Album,
    song.Art);

  // Instruct the player that this is the current track to play
  BackgroundAudioPlayer.Instance.Track = track;
}

This simply returns the current song in the list as a custom type (called Song in this example) and builds a new AudioTrack object from the Song object (by setting a URI to the path to the song as well as descriptive properties for the song name, artist name, album name, and URI to an image for the album art).


Audio Paths

The first parameter to the AudioTrack class’s constructor is a Uri that points at the song. This could be a song in isolated storage (as a relative URI):

var path = new Uri("/audio/SomeTrack.mp3", UriKind.Relative);

Or the first parameter could be a URI for a song out across a network connection:

var path = new Uri("http://shawntwain.com/01-anne.mp3");

Only network and isolated storage URIs are supported. If you want to include audio in your .xap file, you’ll need to copy it to isolated storage manually.


After a track has been set, the audio will not automatically start playing. Setting the track simply tells the background audio to attempt to find the audio to play. When it finds the audio, it will change its play state. Luckily, one of the audio agent’s generated methods is called when the play state changes:

protected override void OnPlayStateChanged(
  BackgroundAudioPlayer player,
  AudioTrack track,
  PlayState playState)
{
  switch (playState)
  {
    // Track has been selected and is ready to be played
    // (includes starting to download if audio is remote)
    case PlayState.TrackReady:
      {
        player.Play();
        break;
      }
    case PlayState.TrackEnded:
      {
        MoveToNextSong();
        break;
      }
  }

  NotifyComplete();
}

The PlayState enumeration has a number of values, but in your case you most want to pay attention to two states:

TrackReady: This indicates that the BackgroundAudioPlayer has located and loaded the audio and is ready to actually play.

TrackEnded: This indicates that a track just ended.

When this method is called with TrackReady, that’s your cue to go ahead and play the audio that has been readied. This is what actually causes the audio to start. You can also handle other states (for instance, TrackEnded) to determine what to do next. In this example the code simply moves the track to the next item, eventually calling PlayCurrentTrack to load the next track. No matter what happens at this point, you should call NotifyComplete or Abort (like the earlier scheduled task agent example explained).

So far, you’ve seen how to play the first track, but what about changing tracks? Earlier in this section, when the user requested to go to the next or previous track, you called methods that would do that work, but what do those methods actually do? They do the simple work of changing the current track number and telling the audio agent to play the current track after the counter has changed:

private void MoveToPreviousSong()
{
  if (--_currentSong < 0)
  {
    _currentSong = _songList.Songs.Count() - 1;
  }

  PlayCurrentTrack();
}

private void MoveToNextSong()
{
  if (++_currentSong >= _songList.Songs.Count())
  {
    _currentSong = 0;
  }

  PlayCurrentTrack();
}

The work of determining what to set the _currentSong value to is trivial code after that track number has changed. It calls PlayCurrentTrack, which you saw earlier as simply setting the BackgroundAudioPlayer’s Track property to the new track.

Back in the main application, you can use the BackgroundAudioPlayer class to retrieve the current state of background audio. For example, to show the currently playing song, you might check the current Track:

public partial class MainPage : PhoneApplicationPage
{

  protected override void OnNavigatedTo(NavigationEventArgs e)
  {
    base.OnNavigatedTo(e);

    if (BackgroundAudioPlayer.Instance.Track != null)
    {
      currentSong.Text = BackgroundAudioPlayer.Instance.Track.Title;
    }
  }

  // ...
}

When the page is launched, it simply tests whether there is a current track, and if so, it sets the title in the user interface to indicate to the user what the current song is. But because the audio agent is controlling the state of the background audio, you might need to know about the change to the PlayState as well. This enables your application to show the current track and enable/disable buttons as necessary:

public partial class MainPage : PhoneApplicationPage
{
  // Constructor
  public MainPage()
  {
    InitializeComponent();

    BackgroundAudioPlayer.Instance.PlayStateChanged +=
      Instance_PlayStateChanged;
  }

  void Instance_PlayStateChanged(object sender, EventArgs e)
  {
    if (BackgroundAudioPlayer.Instance.Track != null)
    {
      currentSong.Text = BackgroundAudioPlayer.Instance.Track.Title;
    }
  }

  // ...
}

When the event is fired, the play state has changed and (as this example shows) you can change the current audio track if it is valid.

This should give you a general feel for the nature of how audio agents work. The specifics of sharing data between the agent and the application will greatly vary depending on how you want to use background audio, but this way you can allow the user to control the audio without it necessarily being part of the phone’s media library.

 
Others
 
- Windows Phone 8 : Background Agents (part 2) - Resource-Intensive Agent
- Windows Phone 8 : Background Agents (part 1) - Periodic Agent
- Active Directory 2008 : Configuring Computer Accounts - Supporting Computer Objects and Accounts
- Active Directory 2008 : Automating the Creation of Computer Objects
- Administration of Microsoft Lync Server 2010 : Troubleshooting (part 2) - Lync Server Logging Tool
- Administration of Microsoft Lync Server 2010 : Troubleshooting (part 1)
- Administration of Microsoft Lync Server 2010 : Configuring Quality of Service
- Administration of Microsoft Lync Server 2010 : Management Tasks
- Windows 8 : Maintaining Data Access and Availability - Managing Offline Files (part 3) - Configuring Disk Usage Limits for Offline Files
- Windows 8 : Maintaining Data Access and Availability - Managing Offline Files (part 2) - Managing Offline File Synchronization
 
 
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
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us