It’s that time again… We’ve reached the last part of this article series on background processing in Windows 8. So almost time to say goodbye for now!
But we’re not there yet. In the previous module, we’ve covered background downloads. We’ve seen that to enable these, we need to use a separate service that keeps transferrin our files when we’re not actively using the application (in other words, have it open as the main app on screen). But what if we want to have an app that plays audio such as Media Player? We probably want to create such an app so that it plays audio when we’re using the app but also so that it continues playing our content when we’re doing something else. Otherwise, we’d simply be changing our tablet in a media player which doesn’t allow me to do any other task while I’m playing music in the background. Again, that’s not a good experience, that’s not what a user wants.
In this article, we’ll explore the way we want play audio while the audio player app isn’t in the foreground.
The good old MediaElement
In Silverlight and other XAML-based technologies, we’ve used the MediaElement control for most of our media needs. The control is smart enough to know that if you’re giving it an audio (for example an MP3 file), it’s being used for audio and if you pass it a movie file (like a WMV file), it’ll need to visibly show that movie.
The control behaves pretty much the same in Windows 8. It can handle most media types. But there’s one thing that bothers us: if we have a MediaElement in our app, we might want to have at least the option that the media continues playing even if the app is not in the foreground any more. By default, the behaviour is such that as soon as the app gets suspended (which is 5 seconds after not being the main app on screen any more), the MediaElement will stop playing the content. If you think about this, this is pretty logical, as no code can execute once the app gets suspended (including code that needs to run to play the media). So indeed, building a Spotify client or basically any media app is pretty much impossible, as the user will need to keep the app open (at least in snap view).
Another scenario that’s going to be problematic is background audio communications. Think of a Skype-like application that can only allow us to talk with someone when we have the app open. This wouldn’t be a good experience at all! It would be for example impossible to search some info on the web while we’re having a call with someone. Impossible to imagine in 2013, isn’t it?!
It turns out though that an extra property was added on the MediaElement class in Windows 8 namely the AudioCategory property. This property is of type AudioCategory (they weren’t being very original with the names that day!) and that is an enumeration that can have a couple of values. This enumeration is shown in the code below.
1: public enum AudioCategory
2: {
3:
4: Other = 0,
5: ForegroundOnlyMedia = 1,
6: BackgroundCapableMedia = 2,
7: Communications = 3,
8: Alerts = 4,
9: SoundEffects = 5,
10: GameEffects = 6,
11: GameMedia = 7,
12: }
There are 2 important values in this enumeration that will enable us to use the MediaElement in the two scenarios described above, namely the BackgroundCapableMedia and Communications. The first one is useful for media player while the second one will be useful in the communication-scenario such as Skype.
Let’s now look at creating a background-capable media player app.
Creating a background-enabled audio player
To create this type of media player app, we have to specify in the app manifest this type of background task. As can be seen below, the app has a background task declaration and the supported type is set to Audio. There’s no real background code required here (as we’ve seen with the background tasks), so we don’t have to specify the Entry Point.
Next, as mentioned, we need to use a MediaElement in which we are specifying the value of the AudioCategory property to BackgroundCapableMedia.
1: <MediaElement Name="BackgroundEnabledMediaElement" AudioCategory="BackgroundCapableMedia" AutoPlay="False"></MediaElement>
If we run the app now with just these 2 options set, things won’t work yet though. The audio will only play when the app is the main app on screen. It’s required that we add some more code: we need to register for a couple of MediaControl events. The MediaControl is the little thing in Windows 8 that allows you to change the volume and the playing song as shown below.
We need to register for the following events:
1: MediaControl.PlayPressed += MediaControl_PlayPressed;
2:
3: MediaControl.StopPressed += MediaControl_StopPressed;
4:
5: MediaControl.PausePressed += MediaControl_PausePressed;
6:
7: MediaControl.PlayPauseTogglePressed += MediaControl_PlayPauseTogglePressed;
We can then handle these events as follows:
1: async void MediaControl_PlayPressed(object sender, object e)
2: {
3: await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
4: {
5: BackgroundEnabledMediaElement.Play();
6: MediaControl.IsPlaying = true;
7: });
8: }
In the code above, we handle that the Play button in the on-screen controls was clicked. We call on to the MediaElement of our app to start playing and change the state of the MediaControl itself (so it too can update its state).
This code is different from the actual, typical PlayButton Click code, which is shown below.
1: private void PlayButton_Click_1(object sender, RoutedEventArgs e)
2: {
3: MediaControl.IsPlaying = true;
4: BackgroundEnabledMediaElement.Play();
5: MediaControl.TrackName = selectedFile.Name;
6: }
In this code, we do more or less the same, however, this code simple executes on the main UI thread, whereas we had to use the Dispatcher before to get access to the UI element (hence the code ran on a background thread).
In the PlayPauseTogglePressed, we add the following code:
1: async void MediaControl_PlayPauseTogglePressed(object sender, object e)
2: {
3: await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
4: {
5: try
6: {
7: if (BackgroundEnabledMediaElement.CurrentState == MediaElementState.Paused)
8: {
9: BackgroundEnabledMediaElement.Play();
10: MediaControl.IsPlaying = true;
11: }
12: else
13: {
14: BackgroundEnabledMediaElement.Pause();
15: MediaControl.IsPlaying = false;
16: }
17: }
18: catch
19: {
20: }
21: });
22: }
Here we first check if the current state is already playing and if so, we pause, otherwise we start the playback. This code too doesn’t run on the UI thread, since it will be called from the MediaControl (and it’s possible that the app has no UI visible at this time).
The net result is the following:
As you can see, the media player app is not being suspended when it’s playing audio on my system. We have successfully created a background-enabled audio player.
There’s one more thing to add!
Adding support for the SoundLevelChanged event
The SoundLevelChanged event is an event that will fire when your application is currently playing audio and another app wants to play audio as well. It turns out that of course only having one app that plays audio at the same time is beneficial, so the event will fire and allow us to react to what we want to do when some other app wants to playback audio. For example, if you’re getting an incoming phone call on Skype and your app is still playing audio, this event will fire.
Registering for this event is done on the MediaControl:
1: MediaControl.SoundLevelChanged += MediaControl_SoundLevelChanged;
The code below handles this event:
1: async void MediaControl_SoundLevelChanged(object sender, object e)
2: {
3: var currentSoundLevel = MediaControl.SoundLevel;
4:
5: if (currentSoundLevel == SoundLevel.Muted)
6: {
7: await coreWindow.Dispatcher.RunAsync(
8: Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
9: {
10: if (BackgroundEnabledMediaElement.CurrentState !=
11: MediaElementState.Paused)
12: {
13: BackgroundEnabledMediaElement.Pause();
14: MediaControl.IsPlaying = false;
15: }
16: });
17: }
18: else
19: {
20: await coreWindow.Dispatcher.RunAsync(
21: Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
22: {
23: if (BackgroundEnabledMediaElement.CurrentState != MediaElementState.Playing)
24: {
25: BackgroundEnabledMediaElement.Play();
26: MediaControl.IsPlaying = true;
27: }
28: });
29: }
30: }
If the “incoming” sound level is muted (meaning another app needs to use the sound channel, so our app is being muted), we can pause the playback so that when the user is finished making the phone call, he can resume the song afterwards.
With this, we have finished our media player app!
Summary
In this 6th and last part of this series, we have looked at how we can create apps that play audio in the background. We’ve seen that by using the AudioCategory property on the MediaElement, we can change the app so that it’s not being suspended and can thus continue playing audio.
Series Summary
With this 6th part finished, this series is finished as well. We have covered a lot of ground and hopefully you’ve understood now how processes in the background in Windows 8 work. While being very constrained, a good deal of options exists to perform work in the background in Windows 8. It’s going to be interesting to see if Microsoft will add any changes to all this in the upcoming preview of Windows 8.1. We’ll surely cover the changes here at SilverlightShow as well.
If you want to learn even more on background processing, you can take a look at my Pluralsight course on this topic. It of course digs a lot deeper in many of the topics we’ve seen in this series. You can take a look at this course here: http://pluralsight.com/training/Courses/TableOfContents/win8-bgproc.
About the author
Gill Cleeren is Microsoft Regional Director, Silverlight MVP, Pluralsight trainer and Telerik MVP. He lives in Belgium where he works as .NET architect at Ordina. Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF at conferences including TechEd, TechDays, DevDays, NDC Oslo, SQL Server Saturday Switserland, Silverlight Roadshow in Sweden, Telerik RoadShow UK… Gill has written 2 books: “Silverlight 4 Data and Services Cookbook” and Silverlight 5 Data and Services Cookbook and is author of many articles for magazines and websites. You can find his blog at www.snowball.be. Twitter: @gillcleeren