##### Traditional equalizers are arranged in columns known as bands.

Each column has a height representing a sound’s amplitude at specific frequency. It is these amplitudes that give the traditional equalizer its distinct look. But traditional is boring. Who says equalizers need to be linear?

Let’s look at how we can mix it up a bit and create a circular audio visualization.

### Audio in Flash

In Flash, the SoundMixer class handles each audible channel within your Flash project. Within the SoundMixer class, analysis of the samples is managed through the static method ComputeSpectrum(). ComputeSpectrum() analyzes the currently playing sound and returns an array of values (between -1 and 1 – or 0 and 1 if FFT is set to true) into a provided byteArray. The values in the array represent the full audio spectrum playing at the moment the routine is called. Within the returned byteArray are 512 sampled values; the first 256 represent the stereo left channel of audio while the second 256 represent the right.

Knowing this, we can utilize and output the values anyway we’d like. In this case we will use our 512 bytes to generate a circular equalizer.

### Creating the Equalizer

This article assumes you are familiar with the SoundMixer.ComputeSpectrum(byteArray,FFT,HTZ) method and have FFT set to true.

Let’s assume you have your file set up as I do, which you can **download here**. This article does not speak to how to use the method but merely how to utilize the returned byteArray into a circle.

Let’s use only the left channel for simplicity sake.

byteArrayContainingAllBytes = new ByteArray(); _angIncrement=360/256 addEventListener(Event.ENTER_FRAME, onFrame); private function onFrame(event : Event):void{ SoundMixer.computeSpectrum(byteArrayContainingAllBytes, true,0); var leftChannelArrayOnly : Vector.<Number> = new Vector.<Number>(); byteArrayContainingAllBytes.position = 0; var index:int = 0; var leftChannel:int = 256; while (index<leftChannel) { leftChannelArrayOnly[index] = Number(byteArrayContainingAllBytes.readFloat()); index++; } var l:int = leftChannelArrayOnly.length; var firstPoint:Boolean = false; var endPtX:Number; var endPtY:Number; _matte.graphics.clear(); _matte.graphics.lineStyle(4,0); while (--l>=0) { var ang:Number= ((l*_angIncrement)*Math.PI)/180; var center:Number = 250; var minRadius:int = 100; var maxRadius:int = 110; var rasd:Number=minRadius + int((leftChannelArrayOnly[l] * ((maxRadius - minRadius)))); var ypt:Number = center + Math.sin(ang) * rasd;// (leftChannelArrayOnly[l]*100); var xpt:Number = center + Math.cos(ang) * rasd;//(leftChannelArrayOnly[l]*100) if (! firstPoint) { firstPoint = true; endPtX = xpt; endPtY = ypt; _matte.graphics.moveTo(xpt,ypt); } _matte.graphics.lineTo(xpt,ypt); } _matte.graphics.lineTo(endPtX,endPtY); _matte.graphics.endFill(); } } }

### Explanation

Every frame, our SoundMixer uses ComputeSpectrum() to sample the currently playing sound and populate the ByteArray with 512 values representing the amplitudes of each frequency across the audio spectrum. (Remember: In this example, we are only worried about the first 256 values.)

To create our circular equalizer, we need to spread these amplitudes around a circle. Knowing we have 256 values to spread around 360 degrees, we need to devise a manner that allows each value to be a representative of a degree. We achieve this by dividing 360 degrees by 256, the number of measurements we have, which gives us 1.41 degrees/value. We will store this in the variable angIncrement.

Now that we know our spread, we want to plot all 256 values around the circle. To do this, we use a loop.

Within the loop we need to analyze each value to determine its position on our canvas. To keep the equalizer looking like a circle we have chosen, not just a radius for our circle but also a limit to how much a frequency’s amplitude can deviate from this radius. This will force the sphere to remain circular versus a starburst, because there will be many frequencies whose amplitude will be 0.

var ang:Number= ((l*_angIncrement)*Math.PI)/180

As we step into our loop, we determine the polar coordinate of the given index by multiplying the index number (“I”) against our spread (“angIncrement”) and then converting our degrees into radians (multiply by π/180).

Next, we will plot our found angle against the origin point of the circle and convert our polar coordinates into Cartesian coordinates using cos and sin.

var xCoord:Number= centerPoint + Math.cos(ang) var yCoord:Number= centerPoint + Math.sin(ang)

If we were to plot this at this moment, we would have a circle but it would be so tiny because cos and sin return only values within -1 and 1. We would indeed have a circle of 256 points, but they will only be with a max spread of 1 pixel around our centerpoint. Therefore, to give our circle a larger radius, we need to multiply our result by a large number.

var largerRadius:Number = 100;

“centerPoint + Math.cos(ang) * largerRadius” would plot an assumed point at the angle theta of centerpoint with a radius of 100. We cannot simply multiply the angle by the amplitude of our current sample index, because some values will be 0. Drawing them would create a starburst, not a circle.

So we know we want to multiply by a radius of a constant length (largerRadius). We will add our amplitude to this value, but we need to set a minimum and maximum range so that our circle does not seem so offset when there are not enough frequencies playing.

This is where our value rasd comes in.

var rasd:Number= minRange + int(leftChannelArrayOnly[l] * (maxRange - minRange))

Since we know that our amplitude at any given frequency is a value between 0 and 1, we can use this to multiply within a specific range. In the code supplied I chose min ranges of 100, which is our radius, and a max range of 110. Which means that 0 will always equal 100 and 1 will always be 10 + radius

This is the meat and potatoes of the post. The rest is to ensure the result is successfully drawn to the screen.

Because we need to move our drawing API before we begin drawing and afterwards we need to complete the sphere we need to first retain the coordinates of the first plot which is the rational for this method.

if(!firstPoint){ firstPoint=true; endPtX=xpt; endPtY=ypt; _matte.graphics.moveTo(xpt,ypt) }

Each time we draw our circle, we will need to retain the information of the origin, or “firstPoint,” so that we will be able to close the endpoints when our loop has completed.

_matte.graphics.lineTo(endPtX,endPtY) _matte.graphics.endFill()

There you have it. What was originally 256 values is now an interesting circle of amplitudes.

*Follow TEN: @**agencyTEN*