SoundMixer.computeSpectrum() vs. Sound.extract()

Mit der Veröffentlichung des FlashPlayer 9 wurde den Entwicklern die Möglichkeit gegeben, Sounddaten nicht mehr nur abzuspielen, sondern auch deren Frequenz auszulesen. SoundMixer.computeSpectrum() heißt die Methode die dies bewerkstelligt. Sie liefert die Fequenzdaten zum aktuellen Zeitpunkt des Sounds in einem ByteArray zurück. Diese können als raw-Daten genutzt werden oder die Funktion liefert schon eine schnelle Fourier-Transformation mit, um die Frequenzen nach deren Bereich zu „sortieren“ (Tiefen zuerst und dann geht es zu den Höhen).

Das alles ist soweit eine wunderbare Sache. Solange nur Audioddaten aus einer Instanz des FlashPlayers kommen. Denn sollte sich noch eine andere Instanz mit Soundwiedergabe „einmischen“, ist die Wahrscheinlichkeit sehr hoch bis hin zu 100 Prozent, dass ein Sicherheitsfehler vom SoundMixer geworfen wird. Dies kommt daher, dass der SoundMixer eine globale Funktion ist. Er hört einfach auf alle Sounds, die auf einem Client-Rechner, innerhalb aller FlashPlayer-Instanzen ablaufen. So auch wenn der User zum Beispiel mehrere Tabs des Browsers offen hat. Dann kommt es zu einem Sicherheitsfehler, der besagt, dass der Soundmixer nicht auf die fremde Quelle zugreifen kann. Soll er eigentlich auch nicht. Meist, oder eigentlich immer, sind Sounds anderer Instanzen völlig uninteressant für die eigene Anwendung.

Das oben beschriebene Problem ist Adobe schon länger bekannt (z.B. http://bugs.adobe.com/jira/browse/FP-499). Doch wesentlich hat sich nichts geändert. Auch der im Bugreport beschriebene Workaround ist doch mehr als Späßchen zu verstehen. Man solle doch alle anderen Seiten, die Sound und Video enthalten, schließen. Doch wie macht man es einem User sinnvoll klar, er soll alle anderen Seiten schlißen um diese eine zu sehen. Bei einer solchen Aufforderung würde ich selbst alle anderen Seiten offen lassen und die eine schließen. Und das war es dann. Auch andere Möglichkeiten wie zum Beispiel einen try-catch um den computeSpectrum zu bauen, oder auf areSoundsInaccessible() abzuprüfen sind nicht von Erfolg gekrönt.

Wenn man sich zu diesem Thema in entsprechenden Forum umsieht, stehen viele vor diesem Problem und auch die Beschwerden darüber nehmen zu. Aber das alles hilft nichts, wenn eine Lösung her muss. Mit dem Release des FlashPlayer 10 gab es zusätzliche Features für die Sound-Klasse. So auch die Sound.extract() Methode. Sie liefert, wie auch computeSpectrum(), einen ByteArray zurück, mit dem Unterschied aber, dass das volle Spektrum ausgelesen wird, bevor der Sound abgespielt wird und dass die Fourier-Transformation nicht integriert ist. Das heißt man bekommt die Sound-Raw-Daten des ganzen Sound-Files gleich zu Anfang, wenn man einen Sound lädt.

Des Weiteren gibt es nun einen SampleDataEvent.SAMPLE_DATA Event, der ausgelöst wird wenn der Player (Sound) neue Audio-Daten anfordert. Um nun das Spektrum auszulesen, werden nun zwei SoundObjekte benötigt. Eines das den Sound abspielt. Dieses bekommt auch den gerade beschriebenen Listener zugewiesen und wird mittels play() ohne Paramter gestartet. Dies klingt im ersten Moment seltsam, aber entsprechend der Definition von oben eine einleutchtende Tatsache. Dieser Output-Sound sagt also nun, ich habe nichts zum abspielen also gibt mir was dazu. Im Handler für den SampleDataEvent wird nun der ByteArray für den OutputSound erzeugt (event.data.writeByteArray(myByteArray)).

Wie nun dieser ByteArray erzeugt wird obliegt nun dem Entwickler. Denkbar wäre hier feste Frequenzen zu erzeugen im Script. So zum Beispiel einen KeyboardEvent zu integrieren, jeder Taste eine feste Fequenz zuweisen und fertig wäre das Keyboard-Klavier. Wir benötigen aber die Sound-Daten aus einer mp3-Datei. Dafür benutzen wir nun das zweite SoundObjekt. Dieses lädt die Sound-Datei, spielt sie jedoch nicht ab! Bei Event.COMPLETE des Ladens, wird das oben beschriebene Ausgabe-SoundObjekt instanziiert und bekommt den SampleDataEvent zugewiesen. Nun wird im Handler für den SampleDataEvent die extract()-Methode für den sourceSound benutzt, um die Sound-Raw-Daten aus diesem auszulesen. Der entstehende ByteArray wird in seinem RAW-Zustand in das event.data Objekt geschrieben und der OutputSound gibt den Sound aus. Wie gesagt, es wirkt im ersten Moment etwas seltsam, aber wenn man bedenkt, dass diese Funktionalität auch dafür da ist dynamisch Sound zu generieren, ist dies alles nicht mehr so abwegig.

Um nun die Sound-Daten visualisieren zu können, legt man einfach eine „Kopie“ des durch die extract-Methode ausgelesenen Sounds an. Jetzt ist sozusagen wieder alles wie bei computeSpektrum(), man ließt die Bytes aus und macht damit was man möchte. Jedoch sind, wie schon oben gesagt, sind die Daten im Raw-Format, also nicht soriert. Will man dies noch tun, schiebt man den ByteArray durch eine schnelle Fourier-Transformation und alles ist wirklich wie gehabt.

Abschließend kann man also sagen, dass es eine Möglichkeit gibt, SoundDaten auszulesen ohne Sicherheitsfehler. Eben so wie es sein sollte, auch wenn im Vergleich zum Aufrufen von SoundMixer.computeSpectrum(), der oben beschriebene Workflow wesentlich komplizierter ist. Aber so lange es danach geht, ist dies, denke ich, zu verkraften.

Sound, SoundMixer und SampleDataEvent in den Adobe LiveDocs

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.