In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: FUCK YEAH RADIO!!!1

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
Get Adobe Flash player
by MiDo.GoDzee 04 Nov 2013
// forked from makc3d's 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;
    }

}