Este artigo foi traduzido por máquina.

Toque e ouça

Reproduzindo arquivos de áudio no Windows Phone

Charles Petzold

Baixar o exemplo de código

Charles Petzold
Quando comecei a ler que as melhorias no Windows Phone OS 7.1 incluíam uma maneira de aplicativos para reproduzir arquivos de som e música no fundo, eu pensei, "não temos que já?"

Acontece que eu estava correta, mas só um pouco. É realmente possível para um aplicativo de Windows Phone OS 7.0 reproduzir um arquivo de música em segundo plano, mas apenas em um caso muito especial. Em todos os outros casos, qualquer arquivo de música que joga seu aplicativo Windows Phone OS 7.0 irá parar quando seu aplicativo é movido para o plano de fundo. Naturalmente, esse comportamento é inteiramente adequado para a maioria dos aplicativos, e provavelmente exatamente o que você quer.

Mas considere um aplicativo que oferece música para o seu telefone para além da biblioteca de música normal do telefone. Para esse aplicativo, é extremamente desejável para continuar jogando enquanto outros aplicativos ocupam o primeiro plano ou quando a tela expire e entra em um estado bloqueado. E mesmo para aqueles de nós que não têm necessidade de escrever um aplicativo, este recurso fornece um divertido ponto de entrada para explorar o mundo novo de "agentes de plano de fundo", introduzido em Windows Phone OS 7.1.

A próxima edição, vou mostrar como gravar um programa Windows Phone que reproduz arquivos de música em segundo plano. Mas para fornecer uma visão mais ampla das instalações de áudio em Windows Phone, eu quero começar esta coluna com as formas mais padronizadas para reproduzir arquivos de áudio com suporte em Windows Phone OS 7.0, bem como a versão 7.1.

MediaElement e suas fontes.

A maneira mais comum para um programa de Silverlight reproduzir uma música ou som é com o MediaElement. Nada é mais simples: MediaElement deriva de FrameworkElement, para que você possa colocá-lo na árvore visual de um arquivo XAML e basta definir a propriedade Source para uma URL:

<MediaElement Source="http://www.SomeWebSite.com/CoolSong.mp3" />

Quando o arquivo XAML é carregado, o arquivo de música automaticamente começa a tocar. MediaElement oferece suporte a arquivos MP3, WMA e WAV. Detalhes estão documentadas em msdn.microsoft.com/library/ff462087(VS.92).

Como uma alternativa para fazer referência a um arquivo pela Internet, você pode incorporar um arquivo de som ou música no executável do aplicativo. Adicionar o arquivo para o programa no Visual Studio e sinalizar o Build Action como conteúdo ou recurso. (Conteúdo é preferido e incorpora o arquivo no executável XAP; com o recurso, o arquivo é incorporado na DLL para o programa.) Defina a propriedade Source para uma URL referenciando o nome de arquivo com um nome de pasta se aplicável:

<MediaElement Source="Music/LocalSong.wma" />

Embora o MediaElement pode ser muito simple, existem inúmeras maneiras de torná-lo mais complicado.Uma maneira é especificar o arquivo de áudio em tempo de execução, como fiz no programa MediaElementDemo, que faz parte do código para download deste artigo.

Neste programa, o MediaElement é ainda na árvore visual, mas a propriedade Source não é definida e reprodução automática é definida como False.Media­ElementDemo permite que você jogue os três movimentos do concerto para violino de Brahms.(Os arquivos são do Internet Archive no archive.org/­BrahmsViolinConcerto/detalhes-Heifetz.É um desempenho de 1939 com o violinista Jascha Heifetz e condução de Serge Koussevitzky, inicialmente disponível em discos de 78 rpm de Victor). Três elementos de RadioButton têm suas propriedades de Tag definidas para as fontes dos três arquivos de música.Para o primeiro botão de opção, que é a URL completa do arquivo de música no site Internet Archive.Para o segundo movimento, eu baixado o arquivo de música (chamado 02Ii.Adagio.mp3) para meu PC, criada uma pasta chamada música no projeto em Visual Studio e adicionado esse arquivo para a pasta.O segundo RadioButton faz referência a esse arquivo com o nome "Music/02Ii.Adagio.mp3". Quando qualquer um destes dois botões é marcado, o manipulador de eventos Obtém a propriedade Tag e cria um objeto Uri dele (especificando UriKind.Absolute para a referência da Web e UriKind.Relative para o conteúdo) e que define a propriedade Source do MediaElement.

O segundo movimento é um arquivo de sobre 4.5 MB, e, obviamente, aumenta o tamanho do executável por uma considerável em massa.Adicionar arquivos desse tamanho para seu executável não é recomendado e feito aqui apenas para demonstração!

Se seu aplicativo precisar de arquivos desse tamanho, um eventual compromisso está disponível: O aplicativo poderia baixar o arquivo uma vez através da Internet e salvá-lo no armazenamento isolado.Isso é o que eu fiz para o terceiro movimento do Concerto de violino.O terceiro botão de opção (que é atribuído um nome de "isoStoreRadio­botão") tem sua propriedade IsEnabled inicialmente definida como false.Figura 1 mostra o processo de download.No Construtor da página, se o arquivo não estiver no armazenamento isolado, WebClient inicia uma transferência em segundo plano.Quando a transferência estiver concluída, o arquivo é salvo para o armazenamento isolado e o RadioButton é habilitado.

Figura 1 download de um arquivo da Web para armazenamento isolado

public MainPage()
{
  InitializeComponent();
  // ...
// Check if file is in Isolated Storage; otherwise start downloading it
  using (IsolatedStorageFile isoStore =
    IsolatedStorageFile.GetUserStoreForApplication())
  {
    if (isoStore.FileExists(isoStoreRadioButton.Tag as string))
    {
      isoStoreRadioButton.IsEnabled = true;
    }
    else
    {
      WebClient webClient = new WebClient();
      webClient.OpenReadCompleted += OnWebClientOpenReadCompleted;
      webClient.OpenReadAsync(new Uri("http://www.archive.org/....mp3"));
    }
  }
  // ...
}
// When the music file is downloaded, save it to Isolated Storage
void OnWebClientOpenReadCompleted(object sender, 
  OpenReadCompletedEventArgs args)
{
  if (!args.Cancelled && args.Error == null)
  {
    Stream inpStream = args.Result;
    byte[] buffer = new byte[inpStream.Length];
    inpStream.Read(buffer, 0, buffer.Length);
    using (IsolatedStorageFile isoStore =
      IsolatedStorageFile.GetUserStoreForApplication())
    {
      string isoPathName = isoStoreRadioButton.Tag as string;
      string isoDirName = Path.GetDirectoryName(isoPathName);
      if (!isoStore.DirectoryExists(isoDirName))
      {
        isoStore.CreateDirectory(isoDirName);
      }
      using (IsolatedStorageFileStream isoStream =
        isoStore.CreateFile(isoPathName))
      {
        isoStream.Write(buffer, 0, buffer.Length);
        isoStoreRadioButton.IsEnabled = true;
      }
    }
  }
}

Em alguns contextos em Windows Phone OS 7.1, você pode definir um URI com um prefixo de "isostore" para fazer referência a um arquivo no armazenamento isolado, mas isso não funciona para o MediaElement. Felizmente, Media­elemento tem uma propriedade SetSource que aceita um objeto Stream. Figura 2mostra como o manipulador marcado para os elementos de RadioButton lida com essas diferenças.

Figura 2 definindo a fonte no MediaElement

void OnRadioButtonChecked(object sender, RoutedEventArgs args)
{
  RadioButton radioButton = sender as RadioButton;
  string uriString = radioButton.Tag as string;
  // Save index for tombstoning
  radioButtonIndex = radioButtonPanel.Children.IndexOf(radioButton);
  if (radioButton == isoStoreRadioButton)
  {
    // Call SetSource on MediaElement using Isolated Storage stream.
using (IsolatedStorageFile storage =
      IsolatedStorageFile.GetUserStoreForApplication())
    {
      using (Stream isoStream = storage.OpenFile(uriString, FileMode.Open))
      {
        mediaElement.SetSource(isoStream);
      }
    }
  }
  else
  {
    // Set Source property on MediaElement using URI
    Uri uri = new Uri(uriString, uriString.Contains(':')
      ?
UriKind.Absolute : UriKind.Relative);
    mediaElement.Source = uri;
  }
}

Transportes e lápides

Outra maneira você pode fazer MediaElement mais difícil para si mesmo é adicionando controles para fazer uma pausa e para mover para o início ou final do arquivo. Ainda mais divertido é um controle deslizante que permite mover para um determinado ponto de arquivo, como mostrado na Figura 3.

The MediaElementDemo Program
Figura 3 O programa MediaElementDemo

Os quatro botões de ApplicationBar são implementados muito simplesmente. Respectivamente, eles defina a propriedade Position do MediaElement para zero, chame o método de jogo de MediaElement, chame o método de pausa e defina a propriedade Position para a propriedade NaturalDuration.

A parte complicada é Ativando e desativando os botões. Para esse trabalho, o evento CurrentStateChanged do MediaElement é Tratado. Ao trabalhar fora MediaElement lógica, é útil pela primeira vez usar WriteLine no evento manipulador para ter uma idéia de como a propriedade CurrentState muda como um arquivo de música é carregado, armazenados em buffer, jogou, pausado e terminou.

No telefone, todos os arquivos de som e música são jogados através de uma única peça de software e hardware chamado a fila de mídia Zune. Se você usar o aplicativo de música + vídeos padrão no telefone para tocar uma canção ou álbum de sua coleção de músicas, que a música continuará a desempenhar no plano de fundo quando você deixar esse aplicativo e iniciar outros aplicativos — e até mesmo quando você inicia o programa de MediaElementDemo. No entanto, se você iniciar um dos movimentos de tocar o concerto para violino de Brahms, vai parar a música de fundo. Agora MediaElementDemo está no controle.

Mas se MediaElementDemo deixa o primeiro plano — quer pelo usuário pressionar o botão Iniciar ou deixar o tela tempo limite — o Brahms vai parar, mesmo se o programa não é marcados para exclusão.

Em tal circunstância, o que você quer que aconteça quando o usuário retorna para o programa? Se a resposta é "Nada", você está na sorte! Mas se você deseja que a música para começar de novo do ponto onde parou, MediaElementDemo demonstra como isso pode ser feito. Em sua substituição de OnNavigatedFrom, o programa salva o índice do movimento que atua, o Estado (provavelmente jogar ou em pausa) e a posição. Em OnNavigatedTo, o programa verifica o RadioButton e define o Estado e a posição no manipulador de MediaOpened.

MediaLibrary e MediaPlayer

Eu mencionei que antes Windows Phone OS 7.1, um recurso já existia jogar determinados arquivos de música no telefone em segundo plano. O problema é que esses arquivos de música devem ser parte da biblioteca de música do telefone. Seu programa pode jogar uma dessas canções, ou ele pode reproduzir todas as músicas em um álbum ou todas as músicas de um determinado artista ou gênero, ou todas as músicas em uma lista de reprodução.

As classes para isso são membros do namespace Microsoft.Xna.Framework.Media. Para usar essas classes XNA em um projeto do Silverlight para o telefone, você precisa primeiro adicionar uma referência para a biblioteca Microsoft.Xna.Framework. Com Windows Phone OS 7.0, Visual Studio lhe deu um aviso sobre como fazer isso. Esse aviso é ido com Windows Phone OS 7.1.

Qualquer programa de Silverlight que usa classes XNA para tocar música deve incluir uma classe especial que implementa IApplicationService e chama FrameworkDispatcher.Update cada dia 30 de segundo. Você pode dar essa classe qualquer nome que você deseja, mas vou referenciá-lo no arquivo app. XAML na seção de ApplicationLifetimeObjects:

<local:XnaFrameworkDispatcherService />

Para reproduzir uma música da biblioteca musical do usuário, comece por instanciar a classe MusicLibrary. Propriedades nomeadas artistas, álbuns, gêneros e listas de reprodução fornecem coleções de objetos do tipo de artista, álbum, gênero e Playlist, e todas essas classes incluem uma propriedade de canções do tipo SongCollection que é uma coleção de objetos de música. (Essas coleções são somente leitura; seu aplicativo não é possível acrescentar nada a biblioteca musical do usuário ou modificá-lo de qualquer maneira.)

Para começar a jogar algo, use Membros da classe estática MediaPlayer. O Método MediaPlayer aceita um objeto de canção, um SongCollection ou um SongCollection com um índice para indicar a canção para começar.

O programa de PlayRandomSong contém um botão denominado "Reproduzir música aleatória", e quando você bate que, o código a seguir executa:

void OnButtonClick(object sender, RoutedEventArgs args)
{
    MediaLibrary mediaLib = new MediaLibrary();
    AlbumCollection albums = mediaLib.Albums;
    Album album = albums[random.Next(albums.Count)];
    SongCollection songs = mediaLib.Songs;
    Song song = songs[random.Next(songs.Count)];
    MediaPlayer.Play(song);
}

Este código extrai um álbum aleatório de sua coleção, uma música aleatória do álbum e começa a jogá-lo. (Emulador Windows Phone contém um álbum com alguns ficheiros de música minúsculo, para que este programa seja executado no emulador apenas multa.)

Se você iniciar uma música tocando com PlayRandomSong, você encontrará que você pode navegar longe o programa ou mesmo terminar o programa e a canção vai continuar jogando. É exatamente como se você tocou essa música do aplicativo de música + vídeos regular do telefone — e se você iniciar o aplicativo, você verá a capa do álbum e o título da canção. Além disso, se você pressionar o botão de controle de volume no telefone, você vai ver a canção na parte superior da tela e obtenha acesso aos botões para pausar ou ir para o início ou final da canção.

Assim como aplicativo de música + vídeos do telefone sabe que música você jogou com o MediaPlayer, seu aplicativo pode determinar qual canção aplicativo de música + vídeos do telefone está a ser reproduzido. Esta informação está disponível da propriedade fila do MediaPlayer, que fornece um objeto MediaQueue que indica a canção atualmente jogando e uma coleção de canções se um álbum ou playlist está jogando. O programa de PlayRandomSong usa um timer para verificar a propriedade ActiveSong da fila e exibe informações sobre o que a canção. Como alternativa, você pode definir manipuladores para o evento ActiveSongChanged do MediaPlayer.

Criação de objetos de canção

O programa PlayRandomSong Obtém um objeto de canção de uma das propriedades ou coleções de MediaLibrary, mas a música também tem uma propriedade estática chamada FromUri que cria uma propriedade de música com base em um arquivo não em sua biblioteca de música. Esse URI pode fazer referência a um arquivo de música através da Internet ou que é parte do arquivo XAP do programa. (Ele não pode referenciar um arquivo no armazenamento isolado). Você pode usar MediaPlayer para reproduzir este objeto de música. (Você não pode criar seus próprios objetos de SongCollection).

O programa de MediaPlayerDemo mostra como isso é feito. Este programa permite que você jogue o Concerto Duplo de Brahms (outra gravação de 1939 de archive.org/details/BrahmsDoubleConcerto_339) com Heifetz novamente, Emanuel Feuermann no violoncelo e Eugene Ormandy conduzindo. Porque você não pode usar MediaPlayer com armazenamento isolado, os primeiros e últimos movimentos são referências da Web.

Outra diferença é que a propriedade Position do MediaElement é gettable e configurável, enquanto a propriedade PlayPosition do MediaPlayer é apenas gettable. Por conseguinte, os dois botões de ApplicationBar ir para o início e o fim da pista não são aplicáveis. Além disso, há, aparentemente, não há maneira de obter a duração de um objeto de música criado dessa maneira, assim que o controle deslizante é irrelevante, bem. Eu removi também toda a lógica de marcação para exclusão do presente programa porque não é possível iniciar uma faixa onde você parou.

Porque MediaPlayer desempenha um objeto de música obtido de biblioteca de música do telefone em segundo plano, você poderia esperá-lo também para jogar qualquer objeto de música em segundo plano. Ele não. A este respeito, MediaPlayer é apenas como o MediaElement. A música pára assim que você navega longe da aplicação. No entanto, se você navega longe o programa MediaPlayerDemo e não é para exclusão — que muitas vezes acontece com Windows Phone OS 7.1 — a música só é suspenso. Quando você navega de volta para o aplicativo, ele pega onde parou.

Eu acho que quando você giz até os prós e contras, o Silverlight MediaElement é um pouco à frente de MediaPlayer XNA, mas o auto­matic retomada de reprodução é um recurso muito bom MediaPlayer.

Streaming e Beyond

Eu tenho estado a discutir reproduzir arquivos de áudio e música comuns em WMA e MP3 formatos. Windows Phone também permite que um programa gerar som dinamicamente dentro do aplicativo. No contexto da Windows Phone de programação, isto é conhecido como "streaming". Demonstrei uma abordagem no programa SpeakMemo na minha coluna de fronteiras de interface do usuário de fevereiro de 2011 (msdn.microsoft.com/magazine/gg598930) usando a classe DyanamicSoundEffectInstance XNA. Você também pode usar a classe MediaStreamSource no Silverlight para fazer algo semelhante. Isto é como implementar a síntese de música eletrônica no telefone. Mais uma vez, no entanto, estas são apenas utilizáveis por aplicativos de primeiro plano.

A partir de Windows Phone OS 7.1, foi introduzido o conceito de um "agente de plano de fundo", e você pode usar isso para jogar ou arquivos de música ou streaming de áudio enquanto seu programa foi suspenso em segundo plano.

Na edição seguinte, falarei sobre como isso é feito.

Charles Petzold é antigo editor colaborador da MSDN Magazine. Seu site é charlespetzold.com.

Graças ao seguinte especialista técnico para revisão deste artigo: Mark Hopkins