Schnelle Fourier-Transformation (1D + 2D) für ActionScript

Wie schon in einem älteren Beitrag (SoundMixer.computeSpectrum() vs. Sound.extract()) von mir beschrieben, gibt es (noch immer) ein Problem mit der computeSpectrum Methode des SoundMixers. Diese Methode zum Auslesen des Spektrums ist an sich eine schöne Funktion, gehört aber dem SoundMixer. Dieser ist eine globale Flash-Klasse, was einige Probleme mit sich bringt. Nutzt man diese Funktion innerhalb eines SWFs und hat andere SWF, welche Sound beinhaltet offen, kommt es unweigerlich zu Sicherheitsfehlern und wirklich unschönen Geräuschen.

Umgehen lässt sich dies mit Hilfe der der extract() Methode der Sound-Klasse. Hier wird das gesamte Frequenzspektum des Sounds augelesen und kann weiter verwendet werden. Aber es gibt einen gravierenden Unterschied bei beiden Mehtoden. Die computeSpectrum liefert von Haus aus eine schnelle Fourier Transformation mit. Hingegen liefert die extract()-Methode nur RAW-Daten zurück. Kurz zusammengefasst bedeutet dies, dass mit Hilfe der computeSpectrum die zurückgelieferten Frequenzen nach Bereich sortiert werden und bei der anderen Funktion eben nicht.

Klar könnte man hingehen und selbst eine einfache Sortierfunktion schreiben. Doch bei der etwas größeren Datenmenge die bewältigt werden muss, wird dies das Script auf jeden Fall verlangsamen. Eine Methode die verwendet werden kann zur Sortierung ist die Schnelle Fourier-Transformation, die auf der Diskreten Fourier-Transformation basiert. Aber leider gab es diese Funktion, wie gesagt, nur innerhalb der computeSepctrum() Methode.

Nun hat Eugene Zatepyakin sich die Mühe gemacht und eine Implementierung der schnellen Fourier-Transformation in ActionScript 3 geschrieben, die frei verfügbar bei Google-Code ist. Mit Hilfe dieser Klassen konnte ich, nach ein paar Tests, den SoundVisualizer mit der Funktionalität zur Sortierung der Sound Daten erweitern, ohne die oben kurz angerissenen Probleme wieder aufzureißen.

Die Implementierung ist, wie Eugene sie auch beschreibt, sehr einfach. Für die Verwendung mit Sound wird die FFT-Klasse verwendet. Des weiteren gibt es noch die FFT2D-Klasse, welche die Möglichkeit eine zweidimensionale Transforation durchzuführen bietet, wie sie etwa bei Bildern Verwendung findet. Bei der Initialisierung der Klasse mit deren init()-Methode sollte man auch nicht über 2048 Bytes gehen, da sonst wiederum die Performance leidet. Besonders ist darauf zu achten, dass die Anzahl der ausgelesenen Bytes über die extract-Methode mit der bei der init Methode übereinstimmt.

Der eingentliche Analyzer ist in der FFTSpectrumAnalyzer-Klasse. Hier wird in der initLogarithmicAverages-Methode zunächst die Mindestbandbreite angegeben und die Anzahl an Bändern, in die eine Oktave unterteilt werden soll.
Sehr wichtig ist auch die Einstellung „Little Endian“ für den ByteArray, welcher die resultierenden Daten hält. Innerhalb des onSampleDataEvent-Handlers werden die ausgelesenen Bytes dann der fft-Klass mittels setStereoRAWDataByteArray(bytes) mitgegeben, um dann anschließend die forwardFFT() aufzurufen, welche die eigentliche Transformation ausführt. fftHelp.analyzeSpectrum() liefert dann den analysierten ByteArray, welcher dann weiter benutzt werden kann für alle möglichen visuellen Ausgaben, zurück. Noch ist hier der Sound selbst noch nicht ausgegeben. Dafür müssen die Bytes zurück transformiert werden über fft.inverseFFT(). Nach dem Zurücksetzen der Position innerhalb des BytesArrays können nun die SoundBytes an event.data zur Ausgabe übergeben werden. Den genauen Ablauf der Funktionen kann man auch auf Google Code nachlesen. Hier hat Eugene zwei Beispielscripts eingefügt. Und als anschauliches Beispiel für die Verwendung seiner Bibliothek einfach den zeroseven SoundVisualizer anschauen. Hier ist im letzten Reiter eine weitere Checkbox, in der man angeben kann, ob die Daten sortiert (FFT) oder unsortiert (RAW) verwendet werden sollen.

Links:

Der zeroseven SOUND VISUALIZER

Der Visualizer ist aus der einfachen Idee entstanden, Sounds mittels Flash zu visualisieren. Begonnen hat es mit einfachen Ausgaben des Spektrums eines Sounds mit Hilfe der computeSpectrum() Methode des SoundMixers. Immer weitere Versuche, was mit dem entstehenden ByteArray machbar ist, entstanden.

In Gesprächen entwickelte sich dann die Idee, eine Plattform zu schaffen, welche Sounds eines Users analysiert, visualisiert und später dann ein Bild zum Download der aktuellen Visualisierung anbietet. Ein einfacher Screenshot wäre hier möglich gewesen und die Bilder jpg- oder png-kodiert zum download anzubieten. Aber solche Bilder sind sehr klein und können dann z.B. nicht als Plakat gedruckt werden. Also suchte ich nach einer anderen Lösung. SVG wäre eine Variante gewesen. Dazu hatte ich auch schon die Grundzüge eines Exporters geschrieben, der die XML-Struktur aus Graphics-Elementen in ActionScript bilden kann. Hiermit wäre zumindest die freie Skalierbarkeit des Plakates gewährleistet. Etwa zu dieser Zeit bin ich dann auf die Bibliothek AlivePDF von Thibault Imbert gestoßen und hab auch hier erste Versuche betrieben (siehe labs-Eintrag dazu). Und nach ein paar Tests war klar, das ist der Weg, den ich für den Visualizer gehen muss.

Aber der SVG Exporter war nicht ganz gestorben. Das Logging-Tool das im Hintergrund mitläuft ist noch immer Teil der Anwendung. Einfach gesagt, merkt sich dass Tool die Spezifikationen aller Elemente. Dieses Log nutze ich jetzt beim PDF-Export zum Erstellen der Elemente des PDFs.

Anfangs mit random-Farben ausgestattet, brachte Sebastian die Idee ins Spiel Kuler für Farbprofile zu nutzen. Also erfolgte mit Hilfe des Adobe Syndication Packages das Laden und Auslesen des Feeds der 20 beliebtesten Farben von Kuler. Dennoch hat auch der User die Möglichkeit eigene Farben zu verwenden. Ein eigenes ColorPicker-Tool kommt hier zum Einsatz.

Auch die Formen wurden vielfältiger. Anfangs nur mit Kreisen gezeichnet, kamen weitere Grundformen und Polygone hinzu. Auch die Möglichkeiten der Ausgabe erweiterten sich, je mehr ich mich darin „reingetestet“ hatte. Eigene Formate entstanden sowie Hilfstools zur Umrechnung von Pixelwerten aus dem Logger hin zu Zoll und Millimeter. Die Oberfläche bekam ein schickes Design verpasst und weitere Funktionen spendiert, um dem User mehr Möglichkeiten zu geben sein eigenes Plakat zu erstellen.

Ein Problem das es zudem zu bewältigen galt, waren die Probleme der SoundMixer.computeSpectrum() Methode. An sich eine tolle Sache und auch die Möglichkeit den ByteArray als RAW-Daten oder als Daten mit einer schnellen Fourier-Transformation zu erhalten ist sehr viel wert. Aber der SoundMixer hat einen entscheidenden Nachteil. Er ist eine globale Klasse und greift somit auf alle möglichen Sounds zu. Das führte dazu, dass der Visualizer zum Beispiel versucht auf die Sounds eines Videos von YouTube zuzugreifen, wenn dieses in einem anderen Browser Tab oder Fenster läuft. Und ganz klar entsteht dabei ein Sandbox-Problem. Umgangen habe ich diese Problematik mit Hilfe der Sound.extract() Methode. Sie liefert den ByteArray gleich zu Anfang zurück und zwar für den gesamten Audiostream. Daher erfolgt hier die Abtastung anders und etwas komplizierter als bei der computeSpectrum() Methode. Aber auch hierzu gibt es schon einen labs-Eintrag von mir (Link).

So entwickelte sich mit der Zeit eine Oberfläche, die für mich zum einen als Versuchs- und Testprojekt diente, aber letztendlich ein Tool darstellt, das ein breites Spektrum der Flashfunktionen abdeckt und auch zeigt was man performancetechnisch mit Flash so anstellen kann.

Aber nun nicht länger lesen sondern einfach ausprobieren:
http://visualizer.zeroseven.de/

Des weiteren möchte ich noch Thibault Imbert für seine Mühe und Arbeit bei AlivePDF danken, ein wirklich hammerkrasses Tool! Ebenso Sascha Wolter für seinen SystemManager, den Junges von Greensock für TweenLite & TweenMax, den Entwicklern des De MonsterDebugger und denen von pixelbreaker.com für das MacMouseWheel.

www.alivepdf.org
www.wolter.biz
www.greensock.com
www.pixelbreaker.com
www.adobe.com
www.demonsters.nl