Dieser Artikel ist Tag #27 der Serie 31 Tage Mango von Jeff Blankenburg.
Der Originalartikel befindet sich hier: Day #27: Microphone API.
Dieser Artikel wurde in der Originalserie von Gastautor Parag Joshi geschrieben. Bei Twitter kann Parag unter @ilovethexbox erreicht werden.
Einleitung
Je nach Tätigkeit und Nutzungsverhalten kann Spracherkennung oder die Aufnahme von Sprachnotizen ein häufiges Anwendungsszenario von Smartphones sein. Von heutige Mobiltelefonen wird erwartet, dass sie Sprachkommandos verstehen können, beispielsweise zur Steuerung von Anwendungen, zum Wählen einer Nummer oder zum Abspielen von Musik.
Die Möglichkeit, unterwegs schnell eine Sprachnotiz aufzunehmen, ist ein weiteres nützliches Feature von Smartphones.
Der erste Schritt zu einer derartigen Anwendung ist die Fähigkeit, Ton vom Mikrofon aufzunehmen und zu verarbeiten.
Heute werden wir uns also die Mikrofon API in Mango anschauen und dazu ein kleines Beispiel bauen.
Worum geht es?
Als kleines Beispiel wollen wir Ton aufnehmen und zur Wiedergabe speichern. Damit hat der Nutzer die Basisfunktionalität zur Aufnahme von Sprachnotizen.
Die folgenden Punkte verdienen besondere Erwähnung:
- Die Klasse Microphone — Diese Klasse wird uns vom Namespace Microsoft.Xna.Framework.Audio zur Verfügung gestellt und erlaubt uns Zugriff auf das Mikrofon.
- public event EventHandler
BufferReady — dies ist ein Ereignishandler, um anzuzeigen, dass die gepufferten Daten des Mikrofons zur Verfügung stehen. Wir werden dieses Ereignis behandeln um den Ton zur Wiedergabe zu speichern. - Microphone.Start — Wie der Name sagt, rufen wir diese Methode um die Aufnahme zu starten.
- Microphone.Stop — Diese Methode rufen wir zum Anhalten der Aufnahme. Ein Aufruf dieser Methode leert sofort den Puffer.
Wie Sie in der Beispielanwendung sehen werden, rufen wir Stop nicht sofort in dem Moment, wo der Benutzer das Mikrofon betätigt oder den Play Button drückt. Wir warten stattdessen zuerst auf den Buffer Ready Event, um das letzte Stückchen des aufgenommenen Tons auch noch mitzubekommen. Erst dann beenden wir die Aufnahme.
Wie Sie beim Blick auf den Namespace vermutet haben, brauchen wir eine Referenz auf das XNA Framework. Die Microphone API ist Teil des XNA Frameworks und erwartet die Simulation eines XNA Game Loops. Falls Sie von XNA noch nichts gehört haben: XNA ist ein Framework von Microsoft zur Erstellung von Spielen und grafikintensiven Anwendungen.
Die Beispielanwendung
Vorbereitungen: Installieren Sie die Mango Werkzeuge von http://create.msdn.com/. Damit bekommen Sie das Visual Studio Express 2010 und das Windows Phone SDK zur Entwicklung von Anwendungen für Windows Phone.
Laden Sie die Anwendung unten herunter, öffnen Sie das Visual Studio und öffnen Sie die Solution Datei der Anwendung. Die Anwendung wurde mit der Silverlight Projektvorlage „Windows Phone Application“ erstellt. Bauen Sie das Projekt und installieren Sie es im Emulator. Sobald die Anwendung geladen hat, werden Sie folgenden Bildschirm sehen:
Folgende Steuerelemente sind dort zu sehen:
- Der Button Mikrofon ist ein Schalter, der das Mikrofon startet und anhält.
- Der Button Play gibt die Aufnahmen wieder.
- Die drei Slider können verwendet werden, um die Lautstärke, die Höhe und die Balance einzustellen.
Um die Anwendung auszuprobieren, starten Sie eine Aufnahme durch Betätigung des Mikrofon Buttons. Anhalten können Sie, indem Sie entweder noch mal den Mikrofon Button oder den Play Button drücken. Verändern Sie die Regler für Lautstärke, Höhe und Balance nacheinander und probieren Sie deren Auswirkungen aus.
Der Code
Deklarationen: Hier ist ein Screenshot der Deklarationen.
Wir verwenden ein Objekt der Klasse SoundEffectInstance um Ton aufzunehmen und wiederzugeben. Wir hätten auch einen SoundEffect verwenden können — die Klasse SoundEffectInstance erlaubt uns aber zusätzlich, den Zustand (also gestartet oder angehalten) zu überwachen.
Die andere Deklaration ist für ein MemoryStream Objekt. Der Puffer des Mikrofons wird kontinuierlich in den MemoryStream geschrieben, bis die Aufnahme abgespielt werden soll. In diesem Moment übergeben wir den Inhalt des MemoryStream zum Abspielen an das Objekt SoundEffectInstance.
Initialisierung:
Der wesentliche Punkt hier ist die Game Loop, die wir mit Hilfe eines DispatcherTimer erstellt haben. Diese Schleife ist für die Aufnahme von Ton des Mikrofons essentiell.
Das Bild für den Play Button setzen wir abhängig von der Farbeinstellung des Telefons.
Jetzt setzen wir auch die Werte für das Mikrofon wie folgt:
Die Dauer des Puffers setzen wir auf eine halbe Sekunde und rufen dann GetSampleSizeInBytes mit der Dauer des Puffers, um die Größe in Bytes zu bestimmen. Das ist wichtig für eine störungsfreie Aufnahme.
Wir verkabeln den Event Buffer Ready und setzen das Bild für das angehaltene Mikrofon.
Ton aufnehmen:
Wenn der Benutzer auf den Mikrofon Button drückt, wird der folgende Code ausgeführt:
Hier passieren mehrere Dinge:
- Das Mikrofon nimmt gerade nicht auf: Wenn das Mikrofon angehalten ist, müssen wir die Aufnahme starten. Wir setzen zunächst den Hintergrund des Buttons. Dann setzen wir den MemoryStream zurück um eine vorherige Aufnahme zu löschen. Weiterhin prüfen wir, ob wir eine vorherige Aufnahme haben. Wenn ja, halten wir die Wiedergabe dieser Aufnahme an.
Im Wesentlichen müssen wir Microphone.Start() aufrufen. Der Rest des Codes hat eher mit der UI und dem korrekten Verhalten der Anwendung zu tun.
- Das Mikrofon nimmt gerade auf: Vor Allem müssen wir die Aufnahme beenden. Wie oben schon erwähnt, können wir Microphone.Stop nicht sofort aufrufen, da die aufgenommenen Daten noch nicht in den MemoryStream übertragen wurden. Wir verwenden also boolean Variablen, um das Anhalten der Aufnahme in den DispatcherTimer Event zu verschieben.
Hier müssen zwei Dinge passieren. Zunächst müssen wir den Microphone Buffer Ready Event feuern lassen, um das letzte Stücken der Aufnahme einzusammeln. Dann müssen wir die Wiedergabe starten. Wir machen das wie folgt:
Im Buffer Ready Event prüfen wir mit Hilfe unserer boolean Variable, ob die Aufnahme angehalten wurde. In diesem Fall rufen wir Stream.Flush(). Damit werden die restlichen Daten in den MemoryStream geleert. Dann können wir das Mikrofon anhalten.
Wir können in diesem Event allerdings nicht die Wiedergabe starten. Dies machen wir im DispatcherTimer Tick Event wie folgt:
Der Tick Event wird alle 33ms aufgerufen. Er wird also recht kurz nach dem Drücken auf Play gefeuert werden. Der Benutzer wird die Verzögerung (fast) nicht wahrnehmen. Der Vorteil ist natürlich, dass wir die gesamte Aufnahme wiedergeben können und nicht das Ende abschneiden müssen.
Wir prüfen, ob es Zeit zur Wiedergabe ist und ob der Stream geleert wurde. Wenn ja, starten wir einen neuen Thread zur Wiedergabe.
Wir starten die Wiedergabe auf einem anderen Thread, um der Benutzerschnittstelle weiterhin Aktualisierungen zu ermöglichen. Das wiederum bedeutet, dass jeder Code, der in unserer Wiedergaberoutine auf UI Elemente zugreifen will, Dispatcher.BeginInvoke aufrufen muss.
Wiedergabe:
Wir erstellen ein Objekt der Klasse SoundEffectInstance und füttern Sie mit dem aufgenommenen Audio-Stream, der Abtastrate des Mikrofons und dem Audiokanal.
Damit unsere Slider für Lautstärke, Höhe und Balance auf einem anderen Thread leben, verwenden wir Dispatcher.BeginInvoke.
Zuletzt rufen wir Play.
Zusammenfassung
Wir könnten unsere Anwendung erweitern, indem wir die Aufnahme im Isolated Storage ablegen und den Benutzer einen Namen dafür angeben lassen. Wir könnten weiterhin eine Liste der im Isolated Storage gespeicherten Aufnahmen anzeigen. Damit hätten wir fast eine vollständige Anwendung für Sprachnotizen.
Die grundlegenden Schritte zur Tonaufnahme sind:
- Fangen Sie den Event des Mikrofons um Ton aufzunehmen.
- Schreiben Sie die Aufnahme in einen Stream.
- Wenn der Benutzer die Aufnahme anhält, rufen Sie Flush des Streams und speichern Sie ihn oder geben Sie ihn wieder.
Um die Beispielanwendung mit dem gesamten Code dieses Artikels herunterzuladen, drücken Sie auf den Download Code Button:
Morgen wird Jeff Fansler die Klasse Media Library behandeln. Damit können wir auf die Musikbibliothek des Anwenders zugreifen.
Bis dahin!