The story so far
In the first part of this series on Background Audio the architecture of the Background Audio feature of the phone was introduced. The Marketplace Certification guidelines that are relevant to Background Audio were highlighted. Then a code sample explaining how to integrate with the Music and Videos Hub was presented.
Where we are now
This part of the series will cover the following topics:
- How to specify a playlist
- Playing your audio: what events occur
- Background audio and user feedback in the app
How to specify a playlist and other information
As we will see in more detail later in this blog post audio track information has to be provided from the agent side, not the app UI side of your code. However of course in most cases the app UI will gather information from the user to specify which audio to play. It is therefore inevitable that there needs to be some communication between the app UI and the agent code.
Why is data sharing different for background agents compared with normal apps?
The architecture for background audio application means that the app code and agent code run in different processes and don’t have any common memory where they can place shared C#/VB objects.
The advantage of this approach is that this allows the Windows Phone operating system to treat the background audio app UI part as any other app, with the same app lifecycle.
The OS can then call the background agent when required without needing to start the main app: usually when changing tracks or dealing with input from the Universal Volume Control (UVC). That also means that the OS is free to aggressively shutdown agents once the agent’s piece work is done - or if the agent is taking too long or using too much RAM or CPU for the OS’s liking.
Because of this it is also not possible to predict when the app code and the agent code will run simultaneously so care must be taken when sharing data between the two.
How to pass data between application code and agent
There are several ways to pass or share data between the app code and the agent:
- You can read properties of the BackgroundAudioPlayer (BAP) object to determine the state of the audio agent and current track position and duration.
- If the BAP is active for your application (you are playing or paused), then the AudioTrack.Tag property can be used to pass information between app and agent. This is the recommended approach in the Background Agent Best Practices for Windows Phone article on MSDN, however this only works if the background audio is active for your app.
- Use Isolated Storage.
- Use Local Database.
Challenges for data sharing
While choosing the right approach, we will face following challenges:
Challenge 1: Detecting when to read updated information sent from agent to the application:
(As discussed above the OS controls when the agent code runs so the reverse problem is not something you can do anything about)
For this there are two possibilities:
- Use state change events raised in the BackgroundAudioPlayer to read information.
- Poll the data you’re interested in.
Challenge 2: Avoiding accessing the data store at the same time from app code and agent code
As mentioned above it is possible for app and agent code to run simultaneously and care must be taken to guard against data corruption.
- Use a Mutex to guard access to Isolated Storage.
- Local Database is more robust to simultaneous access like this, but make sure to use a different DataContext every time your agent is launched.
How to share code between application code and agent
If you use isolated storage or local database APIs to communicate between app code and agent code you will typically need to share code between the app and the agent.
However care must be taken when doing this:
The agent code must not reference a class library which contains banned APIs (even if these are not called from the agent).
To check whether agents are using banned APIs, you can use the Marketplace Test Kit which checks for common faults in Windows Phone applications.
Playing your audio: what events occur?
Let’s explore how the background audio events are raised – to do this we will go back to the architecture diagram of background audio introduced in the previous blog post.
Remember, the BackgroundAudioPlayer (BAP) object appears to be the same from both the application side and agent side, however we will see there are some important differences in practice when we look at the events that flow from some common usage scenarios. Let’s look at these now:
App startup – check what state BAP is in
In this case no background audio is playing – the first surprise is that the app-side BAP returns “Unknown” state. Clearly there is no need to involve the agent side BAP to check the state of background audio.
Playing a new track
Understanding what is going on in this diagram is critical to understanding how the Background Audio player works. It seems quite complex but it is worth bearing in mind that all of this is implemented for you in the template background audio project. All that remains for the developer is to add their own logic for finding tracks for the agent to play.
Firstly, the app calls the BackgroundAudioPlayer.Instance.Play() method. This will cause the audio agent to start up (and if necessary to load into memory which can clearly be seen if you are using the debugger).
Once loaded the agent code will receive the Play user action – and it should respond by calling the Play method on the agent side BAP.
In response to a Play() request with no track set, the agent side BAP will raise the TrackEnded event on the agent. At this point your agent code should find the next track to play (if it is available). The agent then needs to set the Track property on the agent-side BAP.
Once the BAP has made the track ready to play the agent receives the TrackReady event. In response to this the agent should call Play() to start playback of the new track.
At this point, the app code should receive the Playing event (if it is registered to receive BAP events).
And the audio should start playing – there are a couple of further events on the agent side, BufferingStopped and Playing which are only significant for streaming audio scenarios which will be expanded upon in the next blog post.
Play/Pause during playback
These actions are fairly straightforward as expected.
Skip to next track
Track comes to an end: find new track (no user action)
These diagrams is similar to the initial playback case – when the agent receives the SkipNext user action or TrackEnded event it should find the new track to play, and set the Track property on the agent-side BAP.
However note that there is an Unknown state reported to the app when there is a change of tracks. This creates a potential challenge for the app UI to display the correct currently playing track information.
Background audio and user feedback in the app
The MSDN docs which state that the audio agent gives no guarantee as to how long it will take for a command like Play() to be processed. It could take a few seconds to get the Playing event after the play command (even for non-streaming audio).
This is a challenge in the context of Marketplace Certification requirement 5.1.3
5.1.3 Application Responsiveness
If an application performs an operation that causes the device to appear to be unresponsive for more than three seconds, such as downloading data over a network connection, the application must display a visual progress or busy indicator.
In this case if no steps are taken the user will likely think that the app has ignored the tap which they were expecting to start or pause the audio. In our Depth Partner Support program we have seen a few such applications which use the Background Audio feature which suffer from this.
For good user experience once a play/pause control has been pressed:
- the app should display a ProgressBar in the Indeterminate state immediately to indicate to the user that the app has registered your command. It should turn off the ProgressBar once the Playing/Pausing event has been received.
- the app should disable or “grey out” any controls that will lead to commands to BackgroundAudioPlayer object – if multiple commands are sent these will be queued and this can become tricky to deal with given the delay in response from the BAP, leading to the app code getting in the wrong state or even crashing.
Telling the user the track position
If you are in the “Now Playing” experience of your app you may want to have a control on your page which tells the user the current track location. This can be simply achieved by calling BackgroundAudioPlayer.Instance.Position method in the callback of a DispatcherTimer.
The next part of this blog series will deal with challenges with streaming audio.
Written by Paul Annetts