FUCK YEAH RADIO!!!1
package {
import flash.display.*;
import flash.events.*;
import flash.media.*;
import flash.net.*;
import flash.text.*;
import flash.utils.*;
/**
* I wrote this code almost two years ago, but I never knew that
* Sound.extract() bypasses crossdomain check! FFT FTW!
*
* Preceeding FFT implementations (that I didn't know of at the time):
* http://stephan.acidcats.nl/blog/2009/03/01/spectral-analysis-source-files/
* http://rest-term.com/archives/1345/
*
* Since then there were several other AS3 FFT versions
* published, including here, at wonderfl.net
*/
public class FFTTest extends Sprite {
// random stations I don't actually listen to :)
// http://dir.xiph.org/by_format/MP3 for more...
private var stations:Array = [
"http://209.208.14.51:8001/pure_party",
"http://streaming101.radionomy.com:80/Radio-Mozart",
"http://rs35.stream24.org:80/stream",
"http://stream-1.ssatr.ch:80/rsc/mp3",
"http://demovibes.de:8000/necta192.mp3",
"http://217.146.71.24:80/dfm.mp3",
"http://87.104.236.199:80/SR"
];
private var info:TextField;
private var sound:Sound;
private var channel:SoundChannel;
private var bytes:ByteArray;
private var samples:Array;
private var fft:FastFourierTransform;
public function FFTTest () {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
// add info text field
info = new TextField;
info.autoSize = TextFieldAutoSize.LEFT;
info.text = "Frequencies in 0 to 5KHz range";
addChild (info);
bytes = new ByteArray; samples = new Array; fft = new FastFourierTransform;
addEventListener (Event.ENTER_FRAME, enterFrameHandler);
// play safe
for (var i:int = 0; i < 1024; i++) samples [i] = 0;
// start the madness :)
changeStation (); setInterval (changeStation, 20000);
}
private function changeStation ():void {
var station:String = stations.shift ();
stations.push (station);
if (channel) channel.stop ();
sound = new Sound;
sound.load (new URLRequest (station));
channel = sound.play ();
}
private function enterFrameHandler (event:Event):void {
// get bytes of 1024 samples
bytes.position = 0;
if (sound.extract (bytes, 1024, channel.position * 44.1) < 1024) {
// ??
//return;
}
// get samples of left channel
bytes.position = 0;
while (bytes.bytesAvailable > 0) {
samples [int (bytes.position / 8)] = bytes.readFloat (); bytes.readFloat ();
}
// analyze samples
fft.analyze (samples);
// fft.magnitudes contain full spectrum, we want 1st 5KHz
var limitHz:Number = 5000;
var i:int, n:int = fft.magnitudes.length * limitHz / (44100 / 2), w:int = 465 / n;
// draw spectrum
graphics.clear ();
graphics.lineStyle ();
graphics.beginFill (0);
for (i = 0; i < n; i++) {
graphics.drawRect (w*i, 50, w, 400 * fft.magnitudes [i]);
}
graphics.endFill ();
// draw 1KHz ticks
var ticksStep:Number = 1000;
graphics.lineStyle (0);
for (i = 0; i < limitHz / ticksStep + 1; i++) {
graphics.moveTo (w * (ticksStep / limitHz * n * i), 50);
graphics.lineTo (w * (ticksStep / limitHz * n * i), 45);
}
}
}
}
/**
* Port of java FFT class.
* @author Dr Iain A Robin iain@xunil.uklinux.net
* @see http://www.dsptutor.freeuk.com/analyser/SpectrumAnalyser.html
*/
class FastFourierTransform {
/**
* Magnitudes (corresponding frequency ranges from 0 to half of sampling frequency).
*/
public var magnitudes:Array = [];
/**
* Analyzes samples and fills <code>magnitudes</code> array.
* @param samples Array of numbers to analyze (length must be power of 2).
*/
public function analyze (samples:Array):void {
// minimal sanity check
if ((samples == null) || (samples.length < 2)) {
magnitudes.length = 0; return;
}
// find power of 2 not exceeding samles length
var n:int = 2; while (n * 2 <= samples.length) n *= 2;
// transform samples
var n2:int = n >> 1;
nu = int (Math.log (n) * Math.LOG2E);
var nu1:int = nu - 1;
var tr:Number, ti:Number, p:Number, arg:Number, c:Number, s:Number;
var i:int, k:int = 0;
for (i = 0; i < n; i++) {
xre [i] = samples [i]; xim [i] = 0.0;
}
for (var l:int = 1; l <= nu; l++) {
while (k < n) {
for (i = 1; i <= n2; i++) {
p = bitrev (k >> nu1);
arg = 2 * Math.PI * p / n;
c = Math.cos (arg);
s = Math.sin (arg);
tr = xre [k + n2] * c + xim [k + n2] * s;
ti = xim [k + n2] * c - xre [k + n2] * s;
xre [k + n2] = xre [k] - tr;
xim [k + n2] = xim [k] - ti;
xre [k] += tr;
xim [k] += ti;
k++;
}
k += n2;
}
k = 0;
nu1--;
n2 = n2 >> 1;
}
k = 0;
var r:int;
while (k < n) {
r = bitrev (k);
if (r > k) {
tr = xre [k];
ti = xim [k];
xre [k] = xre [r];
xim [k] = xim [r];
xre [r] = tr;
xim [r] = ti;
}
k++;
}
n2 = n >> 1;
var n2i:Number = 2 / n, xr:Number, xi:Number;
for (i = 0; i < n2; i++) {
xr = xre [i]; xi = xim [i];
magnitudes [i] = Math.sqrt (xr * xr + xi * xi) * n2i;
}
magnitudes [0] *= 0.5;
magnitudes.length = n2;
}
private var xre:Array = [];
private var xim:Array = [];
private var nu:int;
private function bitrev (j:int):int {
var j2:int;
var j1:int = j;
var k:int = 0;
for (var i:int = 1; i <= nu; i++) {
j2 = j1 >> 1;
k = 2 * k + j1 - 2 * j2;
j1 = j2;
}
return k;
}
}