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; } } FUCK YEAH RADIO!!!1