Using AS3′s computeSpectrum
Hey there, since I've got this blog anyway, I might as well share some of my knowledge. This is a very basic tutorial on how to build a simple spectrum analyzer using SoundMixer.computeSpectrum, which is new to actionscript 3. This is also my first tutorial ever, so feedback is welcome!
This is what we will be creating:

Circle visualizer | View source
Who is this meant for?
Well, if you're familiar with AS3, this probably isn't for you. My aim is to introduce AS3 to people familiar with AS1 or AS2. If you're not at all familiar with actionscript, I'm not sure this is for you, but you might follow along, and see how simple it actually is (for a rather complex example).
What do I need to know?
Basic knowledge of flash is required and some knowledge in oop and actionscript will help, but I'll go thru every aspect anyway, so you'll get it to work with copy-paste, if nothing else
Still, if you want to learn something new, I suggest you read what I'm saying here, look at the examples, and do it yourself. Some knowledge of trigonometry & math will help you understand what I'm doing with the data (how i output it visually).
Alright, enough with the chit chat, let's start!
First of all you'll need the flash 9 alpha preview to compile, so if you don't have it, go get it from here.
Like with any project planning in advance helps a lot in the future, so let's write down a couple points on what we want to do. I was thinking of a circle where spikes come out depending on the music. If it's quiet it's nice and smooth, if there's loadsa action it's all spiky. Sounds good? Oh, and let's make it respond to beats! Mm, yeah, that oughta be nice. You can of course easily modify it later on, once you get a hang of how this stuff works.
1. Start by creating a .fla file. Give it a black background color, a framerate of approx. 30fps, and save it somewhere with the name spectrum.fla. We'll come back to this later on. Place some mp3 called song.mp3 in the same folder.
2. Let's create a class for our spectrum analyzer. Create a new .as file and save it as SpectrumAnalyzer.as in the same place as the .fla file is. Please note that normally you'd want to have all you classes in a central place (for structure and code reusability), but for the sake of simplicity we'll just put the .as file in the same folder.
3. Inside SpectrumAnalyzer.as add this:
-
// SpectrumAnalyzer.as
-
-
package {
-
import flash.display.*; // needed for Sprite
-
-
public class SpectrumAnalyzer extends Sprite {
-
}
-
}
Update: When this tutorial was written you didn't need to specify that the class is public. Now you do. So, we add public in front of the class
This defines our class inside a new package that extends Sprite (Sprite is basically a MovieClip, without the timeline stuff we don't need anwyay).
4. Currently we've got a class that has a visual appearance (just doesn't show anything yet), so let's go back to the .fla file and create an instance of that class. We'll also pass along what mp3 to play, and what size the visualization should be.
-
// spectrum.fla
-
-
import SpectrumAnalyzer;
-
var visualization:SpectrumAnalyzer = new SpectrumAnalyzer("song.mp3", 550, 400);
-
addChild(visualization);
This imports the class, adds a new instance of it, and creates a child of that instance, placing it on the stage. addChild is similar to createEmptyMovieClip() and/or attachMovie() in AS 1 & 2.
5. Alright, we've got everything set up pretty good now, let's start getting in some data.
-
// SpectrumAnalyzer.as
-
-
package {
-
import flash.display.*;
-
import flash.media.*; // needed for Sound, SoundMixer
-
-
import flash.net.*; // needed for URLRequest
-
-
public class SpectrumAnalyzer extends Sprite {
-
private var music : Sound = new Sound;
-
private var __width : uint;
-
private var __height : uint;
-
-
function SpectrumAnalyzer(mp3 : String, _width : uint, _height : uint) {
-
__width = _width;
-
__height = _height;
-
x = __width / 2;
-
y = __height / 2
-
music.load( new URLRequest( mp3 ) );
-
music.play( 0, 999 );
-
}
-
}
-
}
Let's start playing music. We create the contructor function that gets executed automatically when a class instance is created, and pass on mp3, _width and _height to it (which we sent from spectrum.fla, remember?). We store _width and _height as variables for future usage. Then we load the mp3. The syntax here is new to AS3 with the URLRequest class, but it's still pretty straight forward. We'll also offset the SpectrumAnalyzer with ½ of the height & width, so that it's in the center.
If you now publish spectrum.fla, you should hear that song playing.6. Ok, but this isn't an mp3-player, let's get some visual feedback!
If you look at the docs, you'll notice computeSpectrum, which is the key function here, wants a byteArray as an argument, so let's first create one, then make an onEnterFrame alá AS3.
-
// SpectrumAnalyzer.as
-
-
package {
-
import flash.display.*;
-
import flash.media.*;
-
import flash.net.*;
-
import flash.utils.ByteArray; // needed for ByteArray
-
-
import flash.events.*;
-
-
// needed for Event
-
-
public class SpectrumAnalyzer extends Sprite {
-
private var music : Sound = new Sound;
-
private var ba : ByteArray = new ByteArray( );
-
-
function SpectrumAnalyzer(mp3 : String) {
-
__width = _width;
-
__height = _height;
-
x = __width / 2;
-
y = __height / 2
-
music.load( new URLRequest( mp3 ) );
-
music.play( 0, 999 );
-
addEventListener( Event.ENTER_FRAME, processSound );
-
}
-
-
private function processSound(ev : Event) : void {
-
SoundMixer.computeSpectrum( ba, true, 0 );
-
}
-
}
-
}
You don't have mc.onEnterFrame in AS3 anymore, so initially this may seem confusing. Still, when you get the hang of it, it's a lot more logical. Anyway, here we're now listening to the Event.ENTER_FRAME event which is a constant of Event, equivalent to the string "enterFrame". The SpectrumAnalyzer broadcasts it automatically every frame because it extends Sprite, which eventually extends EventDispatcher, and executes a function called processSound every time. This way we're constantly processing the sound, which will result in a visualizer that changes all the time. A visualizer that doesn't update would be pretty boring, huh ?
In the processSound function we add the SoundMixer.computeSpectrum to analyze the music that's playing and output it's values to a ByteArray which we've here called ba.
6. That's it really! We're now getting the music as a stream into ba, that gets updated every frame. If you look at the computeSpectrum docs, you'll notice that the ByteArray contains 512 floating point values, 256 for the left channel and 256 for the right channel. Now the real fun starts!
Let's draw the circle with our data, where we're putting the volume as the size of the circle at a certain point. This will make the circle look spiky since it's quite unlikely the volume at all frequencies is the same (unless it's totally silent)
We'll also add a couple variables so that we can easily later on change the appearance of the circle.
-
// SpectrumAnalyzer.as
-
-
package {
-
import flash.display.*;
-
import flash.media.*;
-
import flash.net.*;
-
import flash.utils.ByteArray;
-
import flash.events.*;
-
-
public class SpectrumAnalyzer extends Sprite {
-
-
// Settings
-
-
private var lineThickness : Number = 2;
-
private var lineColor : Number = 0x993300;
-
private var circleSize : Number = 75;
-
-
private var music : Sound = new Sound;
-
private var ba : ByteArray = new ByteArray( );
-
private var __width : uint;
-
private var __height : uint;
-
-
function SpectrumAnalyzer(mp3 : String, _width : uint, _height : uint) {
-
__width = _width;
-
__height = _height;
-
x = __width / 2;
-
y = __height / 2
-
music.load( new URLRequest( mp3 ) );
-
music.play( 0, 999 );
-
addEventListener( Event.ENTER_FRAME, processSound );
-
}
-
-
private function processSound(ev : Event) : void {
-
SoundMixer.computeSpectrum( ba, true, 0 );
-
graphics.clear( );
-
graphics.moveTo( 0, -circleSize );
-
graphics.lineStyle( lineThickness, lineColor );
-
for (var i : uint = 0; i <512 ; i++) {
-
var lev : Number = ba.readFloat( );
-
graphics.lineTo( -Math.sin( i / 256 * Math.PI ) * circleSize * (lev + 1), Math.cos( a / 256 * Math.PI ) * circleSize * (lev + 1) );
-
}
-
}
-
}
-
}
Ok, some more code here. This is pretty much the same you'd do in AS1/2, with the exception of graphics, which now is a part of the Sprite (you can't directly just use mc.lineTo anymore). First we clear whatever has been drawn from the previous frame, then we move the pointer up by the size of the circle and define the lineStyle according to the variables defined earlier. Then we loop thru 512 values in the ByteArray. This may seem a bit odd, but when you do ba.readFloat(); it returns the float value (0 and up), and moves on to the next value in the ByteArray. This way we can get all the frequencies very easily with a simple for loop.
Then some basic trigonometry (remember high-school?). Sinus & cosinus of 2 * pi is a full circle, and anything in between will go to that point. Sinus of pi is ½ etc. We then divide i with 256 to get a value from 0-2, depending on where we are in the for loop, and then multiply that with pi so that it'll draw a full circle when the for loop completes. We then multiply that with circleSize so that we get a bit bigger circle than 1px
Then we multiply it with the level. At total silence this will be 0, and everybody knows that x * 0 = 0 -> we want to add 1 to make the minimum size of the circle circleSize, so that it multiplies the result of the sinus/cosinus with 1 or more, not 0. Pretty simple, but if you didn't get it (probably due to my messy explanations), just play around with it to see what happens.
7. Ok, so we've now got it acting to the music, but not quite the way we want it to. One side of the circle is upside down, which doesn't look as cool. The reason for this is that it continues around the circle, and since we're changing the channel, the first values (of the final 256) will be similar to first of the whole 512. Flipping the other side will fix that easily.
I'll leave out all the excess actionscript, because otherwise this page will eventually be reaaally long. Let's modify the processSound function a bit:
-
private function processSound(ev : Event) : void {
-
SoundMixer.computeSpectrum( ba, true, 0 );
-
graphics.clear( );
-
graphics.moveTo( 0, -circleSize );
-
graphics.lineStyle( lineThickness, lineColor );
-
for (var i : uint = 0; i <512 ; i++) {
-
var lev : Number = ba.readFloat( );
-
var a : uint = i;
-
if (i <256) a += 256;
-
if (i == 256) graphics.moveTo( 0, -circleSize );
-
graphics.lineTo( -Math.sin( i / 256 * Math.PI ) * circleSize * (lev + 1), Math.cos( a / 256 * Math.PI ) * circleSize * (lev + 1) );
-
}
-
}
So, what's happening here? We store i in a temporary variable called a. If we modified i directly, it'd mess up the for loop, which we don't want to do. We'll want to flip the left side, and since a & i will start from 0 in the left channel, and 256 in the right channel, all we need to do is to add 256 to a, which is exactly what we do. This will fix the issue with the flipped ½ circle. We also want to move the pointer up to the original starting point (center-up) when we change from the left channel to the right channel, otherwise there will be an ugly line in the middle when the line draws from the bottom to the top.
If you now try it, you'll see it working correctly. Sweet!
8. Beat detection. We wanted beat detection. Ok, beat detection might be the wrong word, since we're done with all the sound analysis. Still let's collect the entire volume of the computeSpectrum snapshot, see if it's over a certain level, and if it is, scale the spiky thingy up a bit. Shouldn't be too difficult, but will add a cool effect.
First lets define a couple variables, so that we've got all the settings in the same place for easy manipulation later on
-
// Settings
-
private var lineThickness:Number = 2;
-
private var lineColor:Number = 0x993300;
-
private var circleSize:Number = 75;
-
private var scaleOnBeat:Number = 1.1; // 110%
-
private var reactToBeat:Number = 30;
-
//
Alright we've added two settings, one which is how much we'll scale it up, and a second when we'll scale it up. 30 is just a value i got by testing, you might get better results with another number. You could trace out vol, which we're defining next, and determine a suitable level for the song you've chosen. The song i had here originally was Prodigy - Voodoo People (Pendulum Remix). Obviously I can't include it due to copyright issues etc.. And bandwidth isn't free either
-
private function processSound(ev : Event) : void {
-
SoundMixer.computeSpectrum( ba, true, 0 );
-
graphics.clear( );
-
graphics.moveTo( 0, -circleSize );
-
graphics.lineStyle( lineThickness, lineColor );
-
var vol : Number = 0;
-
for (var i : uint = 0; i <512 ; i++) {
-
var lev : Number = ba.readFloat( );
-
vol += lev;
-
var a : uint = i;
-
if (i <256) a += 256;
-
if (i == 256) graphics.moveTo( 0, -circleSize );
-
graphics.lineTo( -Math.sin( i / 256 * Math.PI ) * circleSize * (lev + 1), Math.cos( a / 256 * Math.PI ) * circleSize * (lev + 1) );
-
}
-
if (vol> reactToBeat) {
-
scaleX = scaleY = scaleOnBeat;
-
} else {
-
scaleX = scaleY = 1;
-
}
-
}
Should be pretty straight forward. It adds up all the floats, and checks if it's over the value you defined. If it is, it'll scale the whole thingy to whatever you said, and if not, scale it (back) to 1. You could of course add several values with it, make it rotate at beat or whatever. Sky's the limit!
9. Well, we're pretty much done here. The next step really is to play around, use your imagination and make some cool visualizers. Using BitmapData and/or filters will easily make it look a lot better. This tutorial, however, won't go into that, just read the docs or check some examples/tutorials on google and you'll get the hang of it.
Here's the complete code:
-
// spectrum.fla
-
-
import SpectrumAnalyzer;
-
var visualization:SpectrumAnalyzer = new SpectrumAnalyzer("song.mp3", 550, 400);
-
addChild(visualization);
-
// SpectrumAnalyzer.as
-
package {
-
-
import flash.display.*;
-
import flash.media.*;
-
import flash.net.*;
-
import flash.utils.ByteArray;
-
import flash.events.*;
-
-
public class SpectrumAnalyzer extends Sprite {
-
// Settings
-
-
private var lineThickness : Number = 2;
-
private var lineColor : Number = 0x993300;
-
private var circleSize : Number = 75;
-
private var scaleOnBeat : Number = 1.1;
-
// 120%
-
-
private var reactToBeat : Number = 30;
-
//
-
-
private var music : Sound = new Sound;
-
private var ba : ByteArray = new ByteArray( );
-
private var __width : uint;
-
private var __height : uint;
-
-
function SpectrumAnalyzer(mp3 : String, _width : uint, _height : uint) {
-
__width = _width;
-
__height = _height;
-
x = __width / 2;
-
y = __height / 2
-
music.load( new URLRequest( mp3 ) );
-
music.play( 0, 999 );
-
addEventListener( Event.ENTER_FRAME, processSound );
-
}
-
-
private function processSound(ev : Event) : void {
-
SoundMixer.computeSpectrum( ba, true, 0 );
-
graphics.clear( );
-
graphics.moveTo( 0, -circleSize );
-
graphics.lineStyle( lineThickness, lineColor );
-
var vol : Number = 0;
-
for (var i : uint = 0; i <512 ; i++) {
-
var lev : Number = ba.readFloat( );
-
vol += lev;
-
var a : uint = i;
-
if (i <256) a += 256;
-
if (i == 256) graphics.moveTo( 0, -circleSize );
-
graphics.lineTo( -Math.sin( i / 256 * Math.PI ) * circleSize * (lev + 1), Math.cos( a / 256 * Math.PI ) * circleSize * (lev + 1) );
-
}
-
if (vol> reactToBeat) {
-
scaleX = scaleY = scaleOnBeat;
-
} else {
-
scaleX = scaleY = 1;
-
}
-
}
-
}
-
}
The file included with the zip above is DiscoFruity by Osnoff.
That's it! Hope you learned something new!
Feel free to comment with whatever is on your mind.
Comments
30 Comments
2006-07-22, 13:02 by speed
Really nice tut., well done very usefull. Going to play with it.
cometo #flash quakenet irc
2006-07-22, 14:12 by Deviant
Very nice tutorial Shax! Post it on the forum and lots of people can read it
2006-07-22, 16:30 by Chris Benjaminsen
Very nice indeed, care to post it at parentnode to?
2006-07-23, 0:32 by Antti Author comment
Thanks for the feedback. Posted on parentnode too
2006-09-26, 20:51 by todd
Excellent tutorial, for a first one you did really well. Thanks for the help.
2006-10-20, 21:03 by DigiSamples
Nice tut – have been looking everywhere and yours really cleared up some questions. Thanks for opening an entire new world of possibilities
2006-11-11, 8:26 by EsCanar
Very Nice, THX! Will try it with some other Pendulum-Track
2007-04-02, 4:18 by Al Kent
i’ve been working around some spectrum computation since actionscript 2.0, and while i was ecstatic to see adobe include this feature in their new release, i’m still troubled by the output.
perhaps i’m an audio nerd, or a perfectionist, but hear me out.
it seems adobe adopted an algorithm to compute the spectrum data similar to that of swiftMP3. sync is great, response is fantastic, but frequency reporting is way off.
to examine this further…
play a song in windows media player, using the simple bars visualization, and note the range of frequencies that tend to peak the most. now preview the same song with a flash audio spectrum and the low end seems to dominate over all other frequencies.
i tested one song in particular that had majority frequency in mid range and flash is showing me a spectrum of heavy low ends. why would this be the case?
it seems that they cover all the audible range but the low end is scaled way out of proportion. has anyone else noticed this? or has anyone found a nice way of altering the data to “re-scale” the output to look a little more accurate?
again, i may be nitpicking, but it kind of bothers me.
other than that, great tutorial.
2007-04-04, 9:13 by Antti Author comment
Yeah i totally agree with you there Al Kent, the spectrum is (or at least seems?) far off. It’s perfect for cool visualizations (as you can see it react to the music), but for frequency analysis etc it’s not that good. Don’t know there’s something you can do about it (please let me know if you find out!
)
2007-04-21, 4:58 by William V
I am pretty sure the problem with the frequency analysis is they use the same interval each frequency step. This math could be completely off but this is what I think…
It captures 256 samples so:
22050/256=~80hz (I’m not sure if that number should be 44100 … if so this would be even worse)
Usually media player’s frequency analysis goes up around double each time… 20-40-80-160-320. Doubling means going up one octave so 20-40 is one octave and 10k-20k is also an octave. It seems kind of silly to waste half of the spectrum on one octave.
So what I’m trying to say, is if my assumptions are right then Flash’s first frequency interval is equal to the first 3 or 4 intervals of a normal media player. Generally speaking anything above 12k is going to be the same or not that important which is why it usually takes up one bar in other spectrums where here it is taking up almost half of it.
Again these are completely my assumptions so they could be totally wrong but if it is true then hopefully Adobe can fix this algorithm? Can anyone prove this theory right or wrong?
2007-07-02, 19:59 by Diego Frata
Loved the tut… Is there anyway to capture the full spectrum? I’m trying to build an application that shows the full spectrum of an audio stream, but can’t find a way to do it…
2007-08-09, 21:24 by Nate
This is really cool, but I can’t get it to work… Every time I try to start it up, it says: 1046: Type was not found or was not a compile-time constant: SpectrumAnalyzer.
I’d really like to know how to work this, although I’m not so great with action script…
2007-09-20, 23:09 by hauntedcastle.org » Blog Archive » Fun with Flash Beat Detection
[...] enjoying playing with Antti Kupila’s SpectrumAnalyzer class for AS3, with some slight and simple modifications. These icons link to social bookmarking sites [...]
2007-10-09, 16:51 by Valentijn
Hi
I want to create a player like bleep.com uses.
See : http://www.datavraag.nl/sonic/player.swf and .fla
Cannot figure it out to get it to work fully… no visual pregraphics before it actually loads…
Any ideas? So I don’t need realtime graphics but pregraphics during preloading then after x seconds it starts to load…. So it generates the peak levels and visualizing that…. the code is Flash 6 based… but how to create a smart as 3 version that;s better but doing the smae thing?? any ideads ??
ontwerp AT zonnet .nl
Cheers Valentijn
2007-11-13, 22:12 by R Fender
Great tutorial!! Thanks for putting in the time to put it together!
2008-01-28, 19:05 by Greg
I also get error 1046
2008-02-03, 14:22 by Jindo
Error 1046 ;(.
This was after trying the tutorial, but it also gave me the same error when I tried the source D:.
2008-02-20, 19:35 by sirobk
Great tut;
I had this 1046 error too.
To solve it, you just need to write the word ‘public’ in front of ‘class’ in the SpectrumAnalyzer.as file.
Have fun!
2008-02-21, 7:52 by Antti Author comment
I updated the source files now, declared the class as public (thanks for the tip sirobk). When this tutorial was written it was not required (jeez, the time flies).
2008-05-30, 23:17 by Sam
Thats very strange. The demo works for me in Firefox (2.0.0.14) but not in Safari (3.1.1). I thought flash was meant to be independent of browser issues!
2008-06-03, 17:17 by Antti Author comment
Sam: The flash player plugin is different; are you sure you have flash player 9 or newer installed in Safari (right click to see)
2008-06-24, 21:19 by Tutorials | Great How-to Articles on Create Audio Spectrums with AS3 « Flash Enabled Blog
[...] Read Tutorial [...]
2008-07-02, 19:45 by Análisis de espectros de sonido con computeSpectrum en AS3 « Shift F12
[...] Julio 2, 2008 in FlashTags: Audio, computeSpectrum, Procesamiento de Audio Ya antes había escrito un poco al respecto, pero ahora es momento de poner un par de ligas más: más tutoriales y recursos para utilizar el componente computeSpectrum, sugiero primero seguir los puntos del tutorial de Ultrashock para entender lo básico, y después ver los ejemplos de Antti Kupila. [...]
2008-08-06, 13:27 by Flash Tutorials | Great How-to Articles on Create Audio Spectrums with AS3 | Lemlinh.com
[...] Read Tutorial [...]
2008-10-30, 19:21 by Dave See
very nice tutorial, thanks for the time spent on this. I’ve been so far from flash lately, its nice to get back into it, esp since AS3!
keep up the hotness.
2008-12-06, 4:59 by serdar
This following comment by William V, I quoted from above, is the only piece of text I can find on the net that touches my problem with the frequency-type analyzer of computeSpectrum() method (FFTMode=true).
The frequency-type analyzer of Flash, has very high rates for bass/lower frequencies, and too low on higher/treble frequencies.
Looks really off, comparing any other analyzer (winamp’s and such).
I would love to see any documentation on exact spectrum intervals that computeSpectrum calculates.
The documentation at adobe site is just very very brief, and contains no detailed information.
William if you are reading this please contact me at this email address ‘described’ in paranthesis ( add just what you see in the single quotes ‘serdar’ ‘soy’ ‘@’ ‘gmail’ ‘.com’ ).
2007-04-21, 4:58 by William V
I am pretty sure the problem with the frequency analysis is they use the same interval each frequency step. This math could be completely off but this is what I think…
It captures 256 samples so:
22050/256=~80hz (I’m not sure if that number should be 44100 … if so this would be even worse)
Usually media player’s frequency analysis goes up around double each time… 20-40-80-160-320. Doubling means going up one octave so 20-40 is one octave and 10k-20k is also an octave. It seems kind of silly to waste half of the spectrum on one octave.
So what I’m trying to say, is if my assumptions are right then Flash’s first frequency interval is equal to the first 3 or 4 intervals of a normal media player. Generally speaking anything above 12k is going to be the same or not that important which is why it usually takes up one bar in other spectrums where here it is taking up almost half of it.
Again these are completely my assumptions so they could be totally wrong but if it is true then hopefully Adobe can fix this algorithm? Can anyone prove this theory right or wrong?
2009-05-30, 9:26 by Luke
Thanks for this! it looks great.
The only problem that I have is that the sound and the visuals don’t seem to be synchronized. What’s odd is that the visuals seem to be AHEAD of the music: when I publish the SWF, I see activity on the spectrum analyzer while there is still silence and, subsequently, the beats on the spectrum seem to be about 1 sec ahead of the beats in the music.
My CPU isn’t running fast and it doesn’t seem like the computer is struggling to keep up. Any idea what’s going wrong?
2009-05-30, 9:30 by Luke
UPDATE:
publishing the file using the Flash IDE caused a lot of lag between the music and the visuals but, when I just run the SWF standalone, it works OK.
Gonna be playing with this live next weekend…
2009-07-08, 17:15 by Jaco
How would you get the mp3 to stop playing?
2010-05-24, 7:56 by JC
Hi Antii, i just wondering. Can i change the shape instead of circle? Like square or some image element ? Im new to this Spectrum analyzer. Hope to get your reply.
Cheers !
Post a comment