Version Anpassungen
1.0 (2020-04-03) Initiale Version
1.1 (2023-06-18) Fehler bei Benutzung der Extension auf mehreren Screens beseitigt.

Motivation

Maximilian hat mich gebeten für sein Schulprojekt das android.media.audiofx.Visualizer-Interface als AI2-Extension zur Verfügung zu stellen. Für das Projekt sollte die auf einem Android-Smartphone abgespielte Musik optisch unterstützt würden. Bei diesem Projekt von Maximilian Heppner aus Kitzingen handelt es sich um einen "Infinity Mirror/Cube", dessen Färbung und Helligkeit sich mit der Musik ändert. Also so etwas wie eine "moderne Lichtorgel".

Dazu soll das Spektrum der abgespielten Musik per FFT analysiert und in kurzen Abständen per UDP an einen Mikroprozessor gesandt werden, der dann die LEDs im Cube ansteuert. Die Smartphone-App soll mit dem MIT App Inventor 2 entwickelt werden. Ich wurde gefragt, ob ich bei der Entwicklung einer Extension helfen könnte, die die FFT-Analyse übernimmt. Hier ist meine Lösung.

Maximilian hat sein Projekt auf GitHub dokumentiert: https://github.com/eloquenter/led_cube/tree/master/Led_Cube.


Download

Das ZIP-Archiv UrsAI2Visualizer zum Download. Das Archiv enthält den Quellcode, das kompilierte Binary zum Upload in den App Inventor und eine Beispiel-Anwendung.

Funktionsweise

Hinweis: Wenn die Extension auf mehreren Screens benutzt wird, muss die Erfassung vor Aufruf des zweiten Screens gestoppt werden, ebenso vor dem Verlassen des zweiten Screens.

OprnScreen   Close Screen

Android stellt die Klasse android.media.audiofx.Visualizer zur Verfügung, die sowohl die aktuelle Wellenform als auch eine FFT-Analyse bereit stellt. Es war also nur notwendig, die Klasse aus dem App Inventor heraus anzusteuern und die Ergebnisse der FFT-Analyse so aufzubereiten, dass sie für das Projekt verwendet werden können. Die folgenden Angaben beziehen sich auf ein Smartphone vom Typ Xiaomi Redmi 5 Plus mit Android Oreo 8.1.

Das "Einfangen" der Audio-Ausgabe bezeichnet man als Capture. Zunächst gilt es, dieses zu Konfigurieren. Dazu müssen zwei Parameter eingestellt werden. Das ist zum einem die capture rate, die angibt, wir häufig pro Sekunden die Daten analysiert werden. Die Visualizer-Klasse liefert hierzu mit getMaxCaptureRate() die höchste mögliche Rate (20 bei meinem Smartphone). Der zweite Parameter ist die capture size. Sie gibt an, wie viele Datenpunkte die Analyse enthalten soll. Die Methode getCaptureSizeRange() stellt den möglichen Bereich zur Verfügung. Diese Eigenschaft wird in der Extension nicht hinaus geführt. Intern wird immer der größte mögliche Wert gewählt (1024 bei meinem Smartphone).

Der dritte wichtige Parameter ist die sampling rate. Die gibt an, mit welcher Frequenz das Audio-Signal vom System digitalisiert wird. Bei meinem Smartphone sind dies 44.100 Hz.

Mit diesen Parametern liefert die FFT 513 Datenpunkte mit einem Frequenzabstand von 43 Hz (Die entsprechenden Formeln hierzu sind in der Android Dokumentation der Klasse erklärt).

Nr: Hz
0 : 0 (= Gleichstromanteil, kann man ignorieren)
1 : 43
2 : 86
3 : 129
4 : 172
5 : 215
6 : 258
7 : 301
8 : 344
9 : 387
...
508 : 21877
509 : 21920
510 : 21963
511 : 22006
512 : 22050

In der Extension kann man die Grenzfrequenzen der Ausgabekanäle einstellen. Nach jeder vom System bereit gestellten FFT-Analyse wir der Maximalert der Amplituden aller FFT-Frequenzen pro Ausgabekanal ermittelt und als Ereignis bereit gestellt.

Extension

Die Extension benötigt besondere Berechtigungen zum Zugriff auf das Audio-System. Wenn diese noch nicht vergeben wurden, werden Sie beim Start der App abgefragt. Werden die Berechtigungen verweigert, liefern die Methoden dieser Extension keine sinnvollen Werte.

Block Funktion Anmerkung
Capture Rate Ruft die Capture-Rate (Anzahl Analysen pro Sekunde) ab oder legt sie fest.

MaxCaptureRate liefert den Maximalwert. Der Minimalwert ist 1.
Bei der Ausführung von Start wird ein zu großer Wert auf MaxCaptureRate korrigiert.

Werte kleiner als 1 werden ignoriert.
Channels Ruft die Ausgabekanalgrenzen ab oder legt sie fest. Die Grenzen können als Liste oder komma-separierter String angeben werden.

Channels liefert eine Liste.
Die Anzahl der Angaben ist beliebig. Im Beispiel werden drei Ausgangskanäle definiert: 0..300 Hz, 300..2000 Hz, 2000..∞ Hz.

Ist die Liste leer, verbleibt ein Kanal, der alle Frequenzen umfasst.
Gain Ruft den Verstärkungsfaktor ab oder legt ihn fest. Der Wertebereich ist -10..0..+10.

GainMultiplikator liefert den Faktor, mit dem die FFT-Werte multipliziert werden.
Der Multiplikationsfaktor ist 10^(Wert/100). 0 gibt den Faktor 1, +10 verstärkt um den Faktor 10, -10 dämpft um den Faktor 10.

Die Angabe wird logarithmisch ausgewertet, d.h. um den Faktor 1 herum lässt sich die Verstärkung feiner einstellen als bei großen Verstärkungen/Dämfpungen.
Start Stop Start startet das Capture.
Stop beendet das Capture.
IsEnabled liefert true, wenn das Capture läuft.
onFFT liefert das Ergebnis der FFT-Analyse. Der Rückgabewert ist eine Liste mit einem Eintrag für jeden Ausgangskanal.
Der Ausgabewerte für die einzelnen Kanäle liegen im Bereich 0..255.

Im Beispiel hat die Liste drei Einträge.
Permission Über aksForPermissions können die Berechtigung nachträglich erfragt werden.

afterPermissionRequest liefert das Ergebnis der Anfrage.
Die Extension fragt, wenn notwendig, selbständig nach den Berechtigungen.

Granted liefert true oder false.
Designer Einige der Eigenschaften können im Designer eingestellt werden.  

App Beispiel

Eine kleine Beispiel-App zeigt die Verwendung der Extension (s. Download). Vor dem Starten der App sollte man eine Musik-App starten (Radio, MP3-Player, ...). Nach dem drücken der Schaltfläche Start beginnt die App mit der Frequenz-Analyse (3 Kanäle, 5 Samples pro Sekunde) und stellt die relativen Lautstärken optisch dar. Zusätzlich werden die Werte per UDP an die Broadcast-Adresse "255.255.255.255:2003" übertragen.

Nach dem Drücken auf Start wird die aktuelle Tonquelle

Screenshot

Werkzeuge

Für die Erstellung eigener Extensions habe ich einige Tipps zusammengestellt: AI2 FAQ: Extensions entwickeln.