É possível criar aplicativos para Windows Phone que tocam áudio em segundo plano, ou seja, mesmo com o usuário pressionando o botão voltar ou iniciar, saindo da aplicação, o aplicativo poderá continuar a reproduzir o áudio, e neste artigo você verá como os componentes de uma aplicação de áudio funcionam.

Será criado como exemplo, para que você veja o funcionamento do Background Audio, um projeto que irá acessar uma lista de músicas diferentes a partir do aplicativo e mesmo que você feche ele continuará reproduzindo a música.

Criação do projeto Music Application
Figura 2. Criação do projeto Music Application

Para isso, abra o Visual Studio e crie um novo projeto do tipo Windows Phone Application com o nome de MusicApplication, conforme a Figura 2.

Ao criar o projeto é exibida uma caixa com a opção de escolha da versão do Windows Phone, escolha a 7.1, pois assim o seu aplicativo funcionará em todas as versões de Windows Phone disponíveis.

Adicionando o projeto para a mesma solução
Figura 3. Adicionando o projeto para a mesma solução>

Agora clique com o botão direito na solução no Solution Explorer para adicionar mais um projeto para a solução conforme a Figura 3, e crie um projeto do tipo Windows Phone Audio Playback Agent com o nome de AudioPlaybackAgent.

Nota: Lembre-se de criar o novo projeto adicionado para a solução para a versão do Windows Phone 7, da mesma maneira que criou o projeto no início.

E então o seu projeto MusicApplication precisará referenciar e utilizar os recursos do AudioPlaybackAgent. Para isso clique com o botão direito do mouse no projeto MusicApplication e depois em Add Reference, e agora na janela que foi aberta, vá para Solutions, depois projects. Então selecione o AudioPlaybackAgent e clique em ok, conforme a Figura 4.

Adicionando a referência para o projeto MusicApplication
Figura 4. Adicionando a referência para o projeto MusicApplication

Agora vem a parte de criação do layout da página principal do nosso aplicativo. Abra a MainPage e no content panel você irá alterar o title panel e criar um Stack Panel que conterá três botões, um para pausar e reproduzir a música e outros dois para trocar entre a música anterior e a próxima. Também será colocado um Textblock que irá conter o nome da música sendo reproduzida. Verifique como ficará a MainPage.xaml na Listagem 1.


<phone:PhoneApplicationPage 
    x:Class="MusicApplication.MainPage"
    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:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION"
 Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0"
 Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel Width="450" Height="350" Background="Purple">
                <TextBlock Name="lblMusicaAtual" TextAlignment="Center"
Foreground="White" FontSize="25" Margin="10"/>
                <Button Name="btnAnterior" Content="Anterior" Width="400"
 Height="80" Click="btnAnterior_Click"/>
                <Button Name="btnReproduzir" Content="Play" Width="400" 
Height="80" Click="btnReproduzir_Click"/>
                <Button Name="btnProxima" Content="Próxima" Width="400" 
Height="80" Click="btnProxima_Click"/>
            </StackPanel>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>
Listagem 1. Criação do layout da página MainPage.xaml

Agora você deverá tratar os eventos de clicks nos botões criados no layout da página principal. Para isso, abra o arquivo MainPage.xaml.cs e então adicione no topo do código a referência para Microsoft.Phone.BackgroundAudio e crie os métodos de reprodução. O primeiro conterá a condição que irá verificar se a instância da música já está sendo reproduzida ou se está pausada, e os outros dois métodos alternarão para uma próxima música ou uma anterior, conforme a Listagem 2.

Note que no construtor é adicionada uma linha de código que irá indicar o método a ser executado quando o evento PlayStateChanged for acionado.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.BackgroundAudio;
using System.Windows.Navigation;

namespace MusicApplication
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();
            BackgroundAudioPlayer.Instance.PlayStateChanged +=
 Instance_PlayStateChanged;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
            {
                btnReproduzir.Content = "Parar";
                lblMusicaAtual.Text = BackgroundAudioPlayer.Instance.Track.Title +
" - " + BackgroundAudioPlayer.Instance.Track.Artist;

            }
            else
            {
                btnReproduzir.Content = "Reproduzir";
                lblMusicaAtual.Text = "";
            }
        }

        void Instance_PlayStateChanged(object sender, EventArgs e)
        {
            switch (BackgroundAudioPlayer.Instance.PlayerState)
            {
                case PlayState.Playing:
                    btnReproduzir.Content = "Parar";
                    break;

                case PlayState.Paused:
                case PlayState.Stopped:
                    btnReproduzir.Content = "Reproduzir";
                    break;
            }
            if (null != BackgroundAudioPlayer.Instance.Track)
            {
                lblMusicaAtual.Text = BackgroundAudioPlayer.Instance.Track.Title
 + " - " +
BackgroundAudioPlayer.Instance.Track.Artist;
            }
        }

        private void btnAnterior_Click(object sender, RoutedEventArgs e)
        {
            BackgroundAudioPlayer.Instance.SkipPrevious();
        }

        private void btnReproduzir_Click(object sender, RoutedEventArgs e)
        {
            if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
            {
                BackgroundAudioPlayer.Instance.Pause();
            }
            else
            {
                BackgroundAudioPlayer.Instance.Play();
            }
        }

        private void btnProxima_Click(object sender, RoutedEventArgs e)
        {
            BackgroundAudioPlayer.Instance.SkipNext();
        }
    }
} 
Listagem 2. Arquivo MainPage.xaml.cs

Clique com o botão direito do mouse sobre o projeto MusicApplication e então em Add Existing Item. Procure e selecione as músicas para adicionar ao projeto.

Então agora você deverá abrir o arquivo App.xaml.cs e adicionar a referência no topo do projeto para o Isolated Storage e Resources, você também irá criar um método chamado CopyToIsolatedStorage, que ficará responsável por armazenar na memória do aplicativo as músicas. Esse método será chamado no construtor da classe. Verifique como ficará o arquivo App.xaml.cs na Listagem 3.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO.IsolatedStorage;
using System.Windows.Resources;

namespace MusicApplication
{
    public partial class App : Application
    {
        public PhoneApplicationFrame RootFrame { get; private set; }

        public App()
        {
            UnhandledException += Application_UnhandledException;
            InitializeComponent();
            InitializePhoneApplication();

            if (System.Diagnostics.Debugger.IsAttached)
            {
                Application.Current.Host.Settings.EnableFrameRateCounter = true;
                PhoneApplicationService.Current.UserIdleDetectionMode =
 IdleDetectionMode.Disabled;
            }
            CopyToIsolatedStorage();
        }

        private void CopyToIsolatedStorage()
        {
            using (IsolatedStorageFile storage = 
IsolatedStorageFile.GetUserStoreForApplication())
            {
                string[] files = new string[] { "Somewhere I Belong.wma",
"Won't Go Home Without You.mp3", "Go let it out.mp3" };

                foreach (var _fileName in files)
                {
                    if (!storage.FileExists(_fileName))
                    {
                        string _filePath = "Musicas/" + _fileName;
                        StreamResourceInfo resource = 
Application.GetResourceStream(new Uri(_filePath, UriKind.Relative));

                        using (IsolatedStorageFileStream file = 
storage.CreateFile(_fileName))
                        {
                            int chunkSize = 4096;
                            byte[] bytes = new byte[chunkSize];
                            int byteCount;

                            while ((byteCount = resource.Stream.Read(bytes, 0,
 chunkSize)) > 0)
                            {
                                file.Write(bytes, 0, byteCount);
                            }
                        }
                    }
                }
            }
        }

        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
        }

        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
        }

        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
        }

        private void Application_Closing(object sender, ClosingEventArgs e)
        {
        }

        private void RootFrame_NavigationFailed(object sender,
 NavigationFailedEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                System.Diagnostics.Debugger.Break();
            }
        }

        private void Application_UnhandledException(object sender,
 ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                System.Diagnostics.Debugger.Break();
            }
        }

        #region Phone application initialization

        private bool phoneApplicationInitialized = false;

        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;
            phoneApplicationInitialized = true;
        }

        private void CompleteInitializePhoneApplication(object sender,
 NavigationEventArgs e)
        {
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        #endregion
    }
}
Listagem 3. Arquivo App.xaml.cs

Abra o arquivo AudioPlayer.cs que fica no ProjetoAudioApplication e adicione ao topo da classe a referência para a DLL Collections.Generic. Também será necessário uma variável para usar como indicador de que música da lista está tocando.

Será necessário criar uma lista estática que conterá as músicas e os dados conforme a listagem, e também métodos para passagem da música e play.

Os métodos OnPlayStateChanged e OnUserAction serão trocados para utilizar os métodos de play novos criados na classe AudioPlayer, verifique a Listagem 4.


using System;
using System.Windows;
using System.Collections.Generic;
using Microsoft.Phone.BackgroundAudio;

namespace AudioPlaybackAgent
{
    public class AudioPlayer : AudioPlayerAgent
    {
        private static volatile bool _classInitialized;
        static int numMusica = 0;

        public AudioPlayer()
        {
            if (!_classInitialized)
            {
                _classInitialized = true;
                Deployment.Current.Dispatcher.BeginInvoke(delegate
                {
                    Application.Current.UnhandledException +=
 AudioPlayer_UnhandledException;
                });
            }
        }

        private static List<AudioTrack> _playList = new List<AudioTrack>
{
    new AudioTrack(new Uri("Go let it out.mp3", UriKind.Relative), 
                    "Go let it out", 
                    "Oasis", 
                    "Oasis", 
                    null),
    new AudioTrack(new Uri("Won't Go home Without You.mp3",
UriKind.Relative), 
                    "Won't Go home Without You", 
                    "Maroon 5", 
                    "Maroon 5", 
                    null),    
};
        private void AudioPlayer_UnhandledException(object sender,
 ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                System.Diagnostics.Debugger.Break();
            }
        }

        protected override void OnPlayStateChanged(BackgroundAudioPlayer player,
 AudioTrack track, PlayState playState)
        {
            switch (playState)
            {
                case PlayState.TrackReady:
                    // The track to play is set in the PlayTrack method.
                    player.Play();
                    break;

                case PlayState.TrackEnded:
                    PlayNextTrack(player);
                    break;
            }

            NotifyComplete();
        }


        protected override void OnUserAction(BackgroundAudioPlayer player,
 AudioTrack track, UserAction action, object param)
        {
            switch (action)
            {
                case UserAction.Play:
                    PlayTrack(player);
                    break;

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

                case UserAction.SkipPrevious:
                    PlayPreviousTrack(player);
                    break;

                case UserAction.SkipNext:
                    PlayNextTrack(player);
                    break;
            }

            NotifyComplete();
        }

        private AudioTrack GetNextTrack()
        {
            AudioTrack track = null;
            return track;
        }

        private AudioTrack GetPreviousTrack()
        {
            AudioTrack track = null;
            return track;
        }

        protected override void OnError(BackgroundAudioPlayer player,
AudioTrack track, Exception error, bool isFatal)
        {
            if (isFatal)
            {
                Abort();
            }
            else
            {
                NotifyComplete();
            }

        }

        private void PlayNextTrack(BackgroundAudioPlayer player)
        {
            if (++numMusica >= _playList.Count)
            {
                numMusica = 0;
            }

            PlayTrack(player);
        }

        private void PlayPreviousTrack(BackgroundAudioPlayer player)
        {
            if (--numMusica < 0)
            {
                numMusica = _playList.Count - 1;
            }

            PlayTrack(player);
        }

        private void PlayTrack(BackgroundAudioPlayer player)
        {
            // Sets the track to play. When the TrackReady state is received, 
            // playback begins from the OnPlayStateChanged handler.
            player.Track = _playList[numMusica];
        }

        protected override void OnCancel()
        {

        }
    }
}
Listagem 4. Classe AudioPlayer.cs

Agora pressione F5 e teste a aplicação.

Um abraço e até o próximo artigo.