Dieser Artikel wurde maschinell übersetzt.

Touch and Go

Hintergrundaudiofunktion in Windows Phone 7.5

Charles Petzold

Charles PetzoldZurück in den alten Tagen von MS-DOS können Programmierer eine krude Form des Task-Wechsel mit einer Technik namens Terminate and Stay Resident (auch bekannt als TSR) implementieren.TSR-Programme Haken in Tastatur Interrupts oder andere OS-Mechanismen installiert und dann beendet, verlassen das Programm im Speicher bereit, in Aktion treten, wenn der Benutzer eine bestimmte Tastenkombination oder etwas anderes von Interesse geschehen.

MS-DOS war nicht behandeln auch diese Ebene der rudimentären Task-Wechsel, so dass diese TSRs einige schwerwiegende Probleme erstellt ausgestattet.Sie würden miteinander in Konflikt stehen und häufig abstürzen der ganzen OS.Dies war einer der Hauptgründe, dass einige von uns begrüßt Windows zu ergänzen und ersetzen später MS-DOS.

Ich erwähne diese alte Geschichte, weil ich werde Ihnen zeigen, wie man spielt Musikdateien im Hintergrund aus einer Anwendung mit Windows Phone 7,5, und ich weiß, dass Entwickler eine Tendenz haben, außerhalb des Kastens denken.Im Allgemeinen ist dies eine gute Sache, aber Sie sollten diese Technik nicht für andere Zwecke als die Wiedergabe von Musikdateien verwenden.Dies könnte die Anwendung durch den Windows Phone Markt zurückgewiesen werden verursachen.

Die Technik, die ich Ihnen zeigen werde ist nur für die Wiedergabe von Sound oder Musik-Dateien von einem Speicherort im Web oder isolierten Speicher.Wenn Ihre Anwendung erfordert, Lieder aus dem Telefon normalen Musikbibliothek zu spielen, können Sie dies mithilfe der MediaLibrary und MediaPlayer-Klasse, ich in der letzten Ausgabe habe (msdn.microsoft.com/magazine/hh708760).

Anwendung und DLL

In Windows Phone 7,5 ist ein Objekt, das läuft im Hintergrund, um eine Anwendung zu unterstützen als Vermittler bekannt.Um Musik-Dateien im Hintergrund wiederzugeben, verwenden Sie eine Klasse, die von AudioPlayerAgent abgeleitet ist.Diese Klasse muss in einer dynamic Link Library, die von der Anwendung verwiesen wird.Programmcode wird nicht im Hintergrund ausgeführt; was im Hintergrund läuft, ist dieser Klasse, die von AudioPlayerAgent abgeleitet ist.

Der herunterladbare Code enthalten für diesen Artikel ein Visual Studio-Projektmappe mit dem Namen SimpleBackgroundAudio ist.Der Klarheit halber enthält dieses Programms nur über der Mindestmenge an Code zum Hintergrund audio Arbeiten erhalten.Ich habe die Lösung aus dem Dialogfeld Neues Projekt nach Angabe von Windows Phone Anwendung und dann Windows Phone 7.1.(Die 7.1 Bezeichnung wird intern innerhalb der Windows Phone OS und Windows Phone Anwendungen verwendet, aber es bedeutet die gleiche Sache wie die üblicher 7,5 Bezeichnung.)

Die MainPage-Klasse des SimpleBackgroundAudio-Projekts hinzugefügt ich eine Schaltfläche und ein Click-Ereignishandler für diese Schaltfläche:

void OnPlayButtonClick(object sender, RoutedEventArgs args)
{
  AudioTrack audioTrack = 
    new AudioTrack(new Uri("http://www.archive.org/.../Iv.Presto.mp3"), 
                   "Symphony No.
9: 4th Movement", 
                   "Felix Weingartner", 
                   "Beethoven: Symphony No.
9", 
                   null, 
                   null, 
                   EnabledPlayerControls.Pause);
  BackgroundAudioPlayer.Instance.Track = audioTrack;
}

Die Tr4 und BackgroundAudioPlayer-Klassen befinden sich im Namespace Microsoft.Phone.BackgroundAudio. Die URL (die ich hier gekürzt habe um Spaltenbreite des Magazins unterzubringen) verweist auf eine MP3-Datei auf das Internet Archive (archive.org) mit dem letzten Satz von Beethovens Sinfonie Nr. 9 wie von Felix Weingartner in einer Aufnahme 1935 durchgeführt. (Sie können alternativ eine URL, die eine Datei im isolierten Speicher Adressen angeben; Verwenden Sie das UriKind.Relative-Argument an den Konstruktor Uri wenn das der Fall ist.) Die nächsten drei Argumente des Konstruktors Tr4 bieten Album, Künstler und Titelinformationen.

Die BackgroundAudioPlayer-Klasse ist etwas ähnlich wie die MediaElement oder MediaPlayer-Klassen, die Wiedergabe von Audiodateien im Vordergrund. BackgroundAudioPlayer verfügt über keinen Konstruktor; Stattdessen erhalten Sie die einzige Instanz mit der statischen Instanz-Eigenschaft. In diesem Code wird in das gerade erstellte Tr4-Objekt die Track-Eigenschaft festgelegt.

Sie können Programm kompilieren und Ausführen dieses, aber es wird nicht alles tun. Um BackgroundAudioPlayer zu singen, müssen Sie eine DLL mit eine Klasse, die von AudioPlayerAgent abgeleitet ist. Visual Studio können Sie diese Klasse und Bibliothek-Projekt erstellen. Für mein Programm ich den Namen den SimpleBackgroundAudio-Projektmappe in Visual Studio mit der rechten Maustaste, neues Projekt hinzufügen aus dem Menü ausgewählt und wählte dann Windows Phone Audio-Wiedergabe-Agent aus der Liste der Vorlagen. Ich nannte dieses neue Projekt SimpleAudioPlaybackAgent.

Visual Studio erstellt ein Steuerelementbibliothek-Projekt mit einer Klasse namens AudioPlayer, die AudioPlayerAgent, initialisiert mit mehreren Skelett Methoden abgeleitet. Dies ist die Klasse, die im Hintergrund ausgeführt wird.

Sehr wichtig: Erstellen Sie einen Verweis aus der Anwendung auf diese DLL! Um dies zu tun, ich geklickt haben, im Abschnitt "Informationsquellen" unter dem SimpleBackgroundAudio Projekt ausgewählten Verweis hinzufügen, und aktiviert anschließend im Dialogfeld die Registerkarte Projekte, und dann das SimpleAudioPlaybackAgent-Projekt

In die AudioPlayerAgent Ableitung sollten Sie mindestens zwei Methoden ändern: OnPlayStateChanged und OnUserAction. Die vollständige AudioPlayer-Klasse des Projekts (abzüglich der using-Direktiven und Kommentare) erscheint Abbildung 1.

Abbildung 1 die AudioPlayer-Klasse in SimpleBackgroundAudio

namespace SimpleAudioPlaybackAgent
{
  public class AudioPlayer : AudioPlayerAgent
  {
    protected override void OnPlayStateChanged(BackgroundAudioPlayer player,
                                      AudioTrack track, PlayState playState)
    {
      switch (playState)
      {
        case PlayState.TrackReady:
          player.Play();
          break;

        case PlayState.TrackEnded:
          player.Track = null;
          break;
      }
      NotifyComplete();
    }
    protected override void OnUserAction(BackgroundAudioPlayer player, 
                                      AudioTrack track, UserAction action, 
                                      object param)
    {
      switch (action)
      {
        case UserAction.Pause:
          player.Pause();
          break;

        case UserAction.Play:
          player.Play();
          break;
      }
      NotifyComplete();
    }

    protected override void OnError(BackgroundAudioPlayer player, 
                                      AudioTrack track, Exception error, 
                                      bool isFatal)
    {
        base.OnError(player, track, error, isFatal);
        NotifyComplete();
    }
    protected override void OnCancel()
    {
        base.OnCancel();
        NotifyComplete();
    }
  }
}

Blick auf die OnPlayStateChanged zuerst überschreiben. Diese Methode wird aufgerufen, wenn die Eigenschaft PlayState die BackgroundAudioPlayer ändert. Das erste Argument ist die gleiche BackgroundAudioPlayer in den Programmcode verwiesen wird; das letzte Argument ist ein Mitglied der PlayState-Enumeration.

Wenn das Programm die Track-Eigenschaft von der BackgroundAudioPlayer in den Click-Ereignishandler legt, greift die BackgroundAudioPlayer die Musik-Datei über das Internet; die SimpleAudioPlaybackAgent-DLL geladen wird und die OnPlayStateChanged-Methode bekommt schließlich einen Anruf mit dem PlayState-Argument auf PlayState.TrackReady gesetzt. Es ist die Verantwortung der OnPlayStateChanged aufrufen, die Play-Methode des Objekts BackgroundAudioPlayer zu spielen, dass Track.

Wenn das Telefon regelmäßig Musik-Player geschieht, etwas zur Zeit spielen werden, wird es beendet. An diesem Punkt können Sie navigieren Sie von dem Programm oder sogar beenden sie durch Drücken der zurück-Taste des Telefons und die Musik spielt weiter wird.

Sie können dieses Programm ausführen, in der Windows Phone-Emulator, aber Sie wollen es auf ein tatsächliches Telefon versuchen, so können Sie das Telefon Lautstärkeregelung drücken. Dies ruft ein kleines Dropdown-bekannt als Universal Volume Control (UVC), das ist ein wichtiger Bestandteil von Hintergrund-Audio. Der UVC zeigt den Namen des Tracks gespielt wird und der Künstler, basierend auf den Argumenten, dass die Anwendung für den Tr4-Konstruktor bereitgestellt. Der UVC zeigt auch Tasten für vorheriger Titel, Pause und nächsten Track. In diesem Programm ist nur die Pause-Taste aktiviert, weil das ist, was ich in das letzte Argument für den Tr4-Konstruktor angegeben. Wenn Sie die Pause-Taste drücken, wechselt es zwischen Pause und spielen.

Die AudioPlayer-Klasse behandelt diese Pause- und Abspielen-Befehle in der Überschreibung des OnUserAction angezeigt, Abbildung 1. Das UserAction-Argument gibt die bestimmte vom Benutzer gedrückte Schaltfläche. OnUserAction antwortet durch Aufrufen der entsprechenden Methode im BackgroundAudioPlayer-Objekt.

Nach Abschluss die Strecke spielen bekommt OnPlayStateChanged einen Anruf mit PlayState.TrackEnded. Die Methode reagiert, indem Sie die Track-Eigenschaft des BackgroundAudioPlayer auf null, die das Element aus der UVC entfernt. Wenn Sie möchten, können Sie gehen zurück in die Anwendung und starten Sie die Musik wieder spielt.

Beachten Sie, dass Aufrufe von NotifyComplete OnPlayStateChanged und OnUserAction schließen: Diese Eingabe ist erforderlich. Beachten Sie auch, dass weder Methode einen Aufruf an die Basisklasse-Methode enthält. Diese Basisklasse Anrufe sind Teil der Vorlage, die Visual Studio für Sie erstellt, aber ich hatte ein Problem mit der OnUserAction überschreiben, wenn die Basismethode aufgerufen wurde. Der Hintergrund-audio-Sample-Code von Microsoft (vorhanden online vom Bereich Dokumentation Windows Phone 7,5) auch Aufrufe der Basis Methoden beinhalten nicht.

Eine sehr seltsame DLL

Beim Experimentieren mit Hintergrund-Audio, möchten Sie eine Uhr auf das Ausgabefenster in Visual Studio zu halten. Wenn Sie SimpleBackgroundAudio aus dem Debugger ausführen, listet das Fenster Ausgabe alle Systembibliotheken, die auf dem Telefon, läuft das Programm, sowie das Programm selbst, die SimpleBackgroundAudio.dll geladen werden. Die Zeilen im Ausgabefenster angezeigt, die diese Bibliotheken Liste beginnt mit den Worten "UI Aufgabe,", das Programm angibt.

Wenn Sie die Schaltfläche im SimpleBackgroundAudio klopfen, sehen Sie viele der gleichen DLLs geladen werden — aber jetzt mit jeder Zeile vorangestellt durch die Worte "Hintergrundaufgabe." Diese DLLs umfassen die Anwendungs-DLL als auch SimpleAudioPlaybackAgent.dll. Das Laden dieser DLLs ist ein Grund, es braucht ein wenig Zeit für die Musik zu spielen, nachdem Sie auf die Schaltfläche tippen beginnen.

Experimentieren Sie mit Programmen, die Hintergrund-Audio zu spielen, Sie wahrscheinlich Debug.WriteLine-Anweisungen in allen Methode überschreibt in der AudioPlayer-Klasse einfügen möchten, und dann studieren ihre Echtzeitverhalten im Ausgabefenster angezeigt.

Sie können auch einen Konstruktor für die Klasse AudioPlayer mit einem anderen Debug.WriteLine-Anweisung zu erstellen. Sie werden feststellen, dass eine neue Instanz der AudioPlayer instanziiert wird, wenn ein Aufruf an OnPlayStateChanged oder OnUserAction vorgenommen werden muss. Jedem Aufruf wird eine neue Instanz!

Diese einfache Tatsache hat eine tief greifende Auswirkungen: Benötigen Sie diese AudioPlayer-Klasse, die Informationen zwischen den Aufrufen von OnPlayStateChanged und OnUserAction beizubehalten, müssen Sie diese Infor halten­Mation in statische Felder oder Eigenschaften.

Was passiert, wenn Sie Informationen aus einer Klasse in das SimpleBackgroundAudio-Programm der AudioPlayer-Klasse in der SimpleAudioPlaybackAgent-Bibliothek übergeben müssen? Es scheint vernünftig zu definieren eine öffentliche statische Methode in der AudioPlayer-Klasse, und rufen Sie diese Methode Hauptseite oder einer anderen Klasse im Programm. Sie können dies in der Tat tun, aber es tut nicht, was Sie wollen: Nichts von dieser Methode gespeichert werden nicht während der Aufrufe von OnPlayStateChanged und OnUserAction verfügbar.

Warum auch sein nicht es verfügbar? Erinnern Sie die Benutzeroberfläche und Hintergrundaufgabe Bezeichnungen im Ausgabefenster von Visual Studio. Dies sind zwei getrennte Prozesse. Die Instanz der DLL verweist Ihr Programm ist nicht das gleiche wie die Instanz, die wird im Hintergrund ausgeführt, und daher nicht auch statische Daten in einer Klasse in dieser DLL werden zwischen der UI-Aufgabe und der Hintergrundtask freigegeben werden.

Wenn eine Hintergrund-audio-Anwendung aus dem Debugger in Visual Studio zu testen, erleben Sie einige weiteren Seltsamkeiten. Wenn Sie ein Programm, die einige Hintergrund-Audio initiiert hat beenden, hält die Audio Wiedergabe und Visual Studio weist darauf hin, dass Code noch ausgeführt wird. Zur Fortsetzung der Bearbeitung Ihres Programms direkt von Visual Studio Debuggen beenden möchten, und selbst dann die Musik wird häufig halten weiter zu spielen. Während der Entwicklung eines Hintergrund-audio-Programms finden Sie wahrscheinlich selbst häufig deinstallieren das Programm aus dem Telefon.

Verbesserung der Anwendungs

Das SimpleBackgroundAudio-Programm hat ein großes Problem. Obwohl es eine Schaltfläche zum Starten der Musik abgespielt hat, hat es keine Möglichkeit zum Anhalten oder es abgeschaltet. Es wissen nicht einmal, wann die Musik schließt oder wenn etwas passiert ist. Ja, ein Benutzer kann immer den UVC Hintergrundaudio Steuern aufrufen, aber jedes Programm, das Musik spielen beginnt sollten auch über eigene Steuerelemente um es abgeschaltet.

Diese Verbesserungen sind in das Projekt mit dem Namen Playlist enthalten­Player. Wie der Name schon sagt, spielt das Programm eine Reihe von aufeinander folgenden Spuren – in diesem Fall die 12 kleine Klavierstücke von Claude Debussy Preludes, Book 1, gespielt von Alfred Cortot ein 1949 Aufnahme aus dem Internet-Archiv zur Verfügung.

Ich wollte ursprünglich alle Tr4-Objekte innerhalb des Programms erstellen und dann sie an die Hintergrundaudio DLL. Das schien die allgemeineren Programmstruktur, aber ich entdeckte, dass es nicht funktioniert, weil die Anwendung Zugriff auf eine andere Instanz der DLL als die im Hintergrund laufen. Stattdessen schrieb ich die AudioPlayer-Klasse alle Tr4-Objekte selbst erstellen und sie in einer generischen Liste als Wiedergabeliste speichern.

Um dem Programm etwas schwieriger zu machen, entschied ich mich, dass die Playlist nicht Rundschreiben wäre: Ich wollte nicht aus dem letzten Track auf die erste Spur, oder von der ersten bis zur letzten überspringen. Aus diesem Grund hat die erste der Tr4-Konstruktoren ein letztes Argument, das die Flags EnabledPlayerControls.Pause und EnabledPlayerControls.SkipNext kombiniert; die letzten Tr4 kombiniert die Pause und SkipPrevious-Flags. Alle anderen haben alle drei Fahnen. Dies ist, wie die drei Schaltflächen in der UVC für die verschiedenen Tracks aktiviert sind.

Abbildung 2 zeigt die OnPlayStateChanged und OnUserAction überschreibt. In OnPlayStateChanged wenn ein Gleis endet, wird das nächste Stück auf die Track-Eigenschaft von der BackgroundAudioPlayer festgelegt. Die OnUserAction Überschreiben des vorherigen Track und NextTrack-Befehle von der UVC indem die Track-Eigenschaft auf dem vorherigen oder nächsten Tr4 in der Wiedergabeliste verarbeitet.

Abbildung 2 die AudioPlayer Überschreibungen in PlaylistPlayer

static List<AudioTrack> playlist = new List<AudioTrack>();
static int currentTrack = 0;

...
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, 
  AudioTrack track, PlayState playState)
{
  switch (playState)
  {
    case PlayState.TrackReady:
      player.Play();
      break;

    case PlayState.TrackEnded:
      if (currentTrack < playlist.Count - 1)
      {
        currentTrack += 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;
  }
  NotifyComplete();
}

protected override void OnUserAction(BackgroundAudioPlayer player, 
  AudioTrack track, UserAction action, object param)
{
  switch (action)
  {
    case UserAction.Play:
      if (player.Track == null)
      {
        currentTrack = 0;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Play();
      }
      break;

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

    case UserAction.SkipNext:
      if (currentTrack < playlist.Count - 1)
      {
        currentTrack += 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;

    case UserAction.SkipPrevious:
      if (currentTrack > 0)
      {
        currentTrack -= 1;
        player.Track = playlist[currentTrack];
      }
      else
      {
        player.Track = null;
      }
      break;

    case UserAction.Seek:
      player.Position = (TimeSpan)param;
      break;
  }
  NotifyComplete();
}

Das Programm MainPage-Klasse hat eine Reihe von vier standard-Anwendung Schaltflächen die gleichen Funktionen wie die UVC durchführen. Es setzt auch einen Ereignishandler für das Ereignis PlayStateChanged BackgroundAudioPlayer auf den Bildschirm mit aktuellen Trackinformationen zu aktualisieren und ein CompositionTarget.Rendering-Ereignishandler um einen Slider mit aktuellen Track Progress, zu aktualisieren, wie in Abbildung 3.

The PlaylistPlayer Program
Abbildung 3 das PlaylistPlayer-Programm

Die Logik zum Aktivieren und Deaktivieren von Schaltflächen auf die Anwendung ist recht einfach: die Schaltflächen vorheriger Titel und Nächster Track aktiviert sind, basierend auf der PlayerControls-Eigenschaft von der aktuellen Tr4; So sollten sie mit den UVC vereinbar sein. Die Pause-Taste ist aktiviert, wenn der Spieler spielt; die Play-Schaltfläche ist aktiviert, wenn der Spieler angehalten wird. Wenn der aktuelle Titel null ist, Spiel aktiviert ist und alle anderen Schaltflächen werden deaktiviert.

Der Click-Ereignishandler der Schaltflächen auf der vier Anwendung machen Aufrufe an die Methoden BackgroundAudioPlayer, SkipPrevious, Play, Pause und SkipNext, bzw.. Es ist wichtig zu verstehen, dass diese Methodenaufrufe nicht direkt den Vorgang der Wiedergabe von Musik kontrollieren. Stattdessen diese Methodenaufrufe aus dem Programm auslösen OnUserAction Anrufe in der AudioPlayer-Klasse, und es ist der Code im AudioPlayer, die tatsächlich startet und stoppt die Musik.

Dies ist etwas eigenartig, weil es bedeutet, dass Aufrufe an die Methoden Play und Pause von BackgroundAudioPlayer Verhalten sich unterschiedlich je nachdem, ob sie aus einem Programm oder die Überschreibung der OnUserAction genannt werden.

Ich habe auch einen ValueChanged-Ereignishandler für die Schieberegler, um zu einer bestimmten Adresse in der Spur. Der Handler berechnet eine neue Position für die Spur und setzt eine entsprechende TimeSpan-Objekt auf die Position-Eigenschaft von der BackgroundAudioPlayer. Ähnlich wie bei Wiedergabe und Pause Aufrufe, das Festlegen dieser Eigenschaft die Position des Titels ändert sich nicht. Stattdessen generiert es einen Aufruf an die OnUserAction-Überschreibung in AudioPlayer mit einem Aktionsargument des UserAction.Seek und die TimeSpan codiert im Param-Argument. Die OnUserAction außer Kraft setzen, dann wird dieses TimeSpan-Objekt auf die Position-Eigenschaft von der BackgroundAudioPlayer, und das ist, was tatsächlich bewirkt, dass den Sprung.

In der Praxis funktioniert dieser Regler, wenn Sie klopfen Sie es auf Verschieben vor oder zurück in die Spur um 10 Prozent. Wenn Sie versuchen, schieben Sie es, mehrere Aufrufe von Seek scheinen aufzubauen, und das Ergebnis ist ein akustisches Durcheinander. Ich würde viel lieber eine regelmäßige ScrollBar anstatt einen Slider verwenden, weil dann ich kann warten, für ein EndScroll Ereignis, das auftritt, wenn der Benutzer das Steuerelement bearbeiten. Leider habe ich noch nie in der Lage zu überzeugen, die Windows Phone ScrollBar überhaupt funktioniert.

Die Einschränkungen

Es ist schon interessant zu sehen, wie Windows Phone 7,5 Programmierer einen direkteren Zugang zu den Telefon Hardware (z. B. das Video feed) sowie die Fähigkeit, einige Hintergrundverarbeitung gegeben hat. Aber ich kann nicht erwehren, es ist ein Stück fehlt diese Implementierung von Hintergrund-Audio.

Angenommen, ein Programm möchte den Benutzer aus einer Liste von Musik-Dateien wählen Sie nacheinander spielen lassen. Das Programm kann nicht auf der DLL aus der gesamten Liste Seite, so dass übernehmen die Track-Eigenschaft von der BackgroundAudioPlayer festlegen, wenn jede Spur beendet werden muss. Aber dies kann passieren, nur, wenn das Programm im Vordergrund ausgeführt wird.

Es ist möglich, einige Informationen zwischen der Anwendung und dem Hintergrund DLL durch die Tag-Eigenschaft der Klasse Tr4-Zeichenfolge übergeben. Aber nicht die Mühe nicht ableiten einer neuen Klasse von Tr4 in der Hoffnung auf zusätzliche Informationen an die DLL übergeben: Eine Kopie wird erstellt, der das Tr4-Objekt von der Anwendung erstellt werden, für die Übergabe an die DLL überschreibt.

Glücklicherweise hat die DLL Zugriff auf das Anwendungsgebiet des isolierten Speichers, so dass das Programm eine kleine Datei, beschreibt die Wiedergabeliste speichern und dann die DLL die Wiedergabeliste aus der Datei zugreifen kann kann. Das Bonusprogramm für diese Spalte ist ein Projekt mit dem Namen PlaylistFilePlayer, welche einen einfachen Ansatz für diese Technik demonstriert.

Charles Petzold ist eine langjährige redaktionelle Beiträge für das MSDN Magazin. Sein neues Buch, "Programmierung Windows Phone 7" (Microsoft Press, 2010), steht als kostenloser Download auf bit.ly/cpebookpdf.

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Mark Hopkins