Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package
- {
- import flash.display.Bitmap;
- import flash.display.Sprite;
- import flash.filesystem.File;
- import flash.net.FileFilter;
- import flash.net.URLLoader;
- import flash.media.Sound;
- import flash.events.*;
- import flash.net.URLRequest;
- import flash.display.BitmapData;
- import flash.utils.*;
- import flash.geom.Rectangle;
- import flash.text.*;
- /**
- * ...
- * @author Luke Davies
- */
- public class Main extends Sprite
- {
- /*
- _(\_/)
- ,((((^`\
- (((( (6 \
- ,((((( , \
- ,,,_ ,((((( /"._ ,`,
- ((((\\ ,... ,(((( / `-.-'
- ))) ;' `"'"'""(((( (
- ((( / ((( \
- )) | |
- (( | . ' |
- )) \ _ ' `t ,.')
- ( | y;- -,-""'"-.\ \/
- ) / ./ ) / `\ \
- |./ ( ( / /'
- || \\ / /
- || \\ _//'||
- || )) |_/ ||
- \_\ |_/ ||
- `'" \_\
- LUKE i changed this stuff so that now it processes the file in chunks. It reads each chunk and does the fft on it and then calls the code to draw the
- thing, but since it all happens at once you're only going to see the last chunk. If you want to see the whole file then you will need to first of all
- have the chunk processing stuff (processMusic()) process a chunk and then add the processed data onto the end of a big array of data for the whole file,
- which should not be too tricky, and then change the code to draw the spectrum to draw the whole thing.
- */
- private var m_file:File;
- private var m_sound:Sound;
- private var m_buf:Vector.<Number> = null;
- private var m_writePos:uint = 0;
- private var m_fft:FFT2; // FFT object
- private var m_tempRe:Vector.<Number>; // Temporary buffer - real part
- private var m_tempIm:Vector.<Number>; // Temporary buffer - imaginary part
- private var m_mag:Vector.<Number>; // Magnitudes (at each of the frequencies below)
- private var m_freq:Vector.<Number>; // Frequencies (for each of the magnitudes above)
- private var m_win:Vector.<Number>;
- private var done:Boolean = false;
- private const SAMPLE_RATE:Number = 44100;
- private const LOGN:uint = 9; // Log2 FFT length
- private const N:uint = 1 << LOGN;
- private const BUF_LEN:uint = N;
- public var chunkCount:int = 0;
- public var manyMags:Array = new Array();
- private var m_tickTextAdded:Boolean = false;
- private var m_timer:Timer = new Timer(11.6);
- public function Main():void
- {
- // Set up the FFT
- m_fft = new FFT2();
- m_fft.init(LOGN);
- m_tempRe = new Vector.<Number>(N);
- m_tempIm = new Vector.<Number>(N);
- m_mag = new Vector.<Number>(N / 2);
- addEventListener(Event.ENTER_FRAME, onEnterFrame);
- //m_smoothMag = new Vector.<Number>(N/2);
- // Vector with frequencies for each bin number. Used
- // in the graphing code (not in the analysis itself).
- m_freq = new Vector.<Number>(N / 2);
- var i:int;
- for ( i = 0; i < N/2; i++ )
- m_freq[i] = i*SAMPLE_RATE/N;
- // Hanning analysis window
- m_win = new Vector.<Number>(N);
- for ( i = 0; i < N; i++ )
- m_win[i] = (4.0/N) * 0.5*(1-Math.cos(2*Math.PI*i/N));
- // Create a buffer for the input audio
- m_buf = new Vector.<Number>(BUF_LEN);
- for ( i = 0; i < BUF_LEN; i++ )
- m_buf[i] = 0.0;
- m_file = File.userDirectory;
- var fileFilter:FileFilter = new FileFilter("MP3s", "*.mp3");
- m_file.browse([fileFilter]);
- m_file.addEventListener(Event.SELECT, loadFile);
- }
- private function onEnterFrame(event:Event)
- {
- if (done)
- {
- trace(m_timer.currentCount);
- drawSpectrum(manyMags[m_timer.currentCount], m_freq);
- }
- }
- private function loadFile(e:Event):void
- {
- m_sound = new Sound(new URLRequest(m_file.url));
- m_sound.addEventListener(Event.COMPLETE, processMusic);
- }
- private function processMusic(e:Event):void
- {
- trace(m_sound.length);
- var sampleLength:Number = Math.floor((m_sound.length / 1000) * SAMPLE_RATE);
- trace(sampleLength);
- var stereoData:ByteArray = new ByteArray();
- var samples = m_sound.extract(stereoData, sampleLength);
- var soundData:ByteArray = new ByteArray();
- //trace(soundData.length);
- stereoData.position = 0;
- while(stereoData.bytesAvailable > 0)
- {
- var firstChannel:Number = stereoData.readFloat();
- var secondChannel:Number = stereoData.readFloat();
- var sum: Number = firstChannel + secondChannel;
- var average:Number = sum / 2;
- soundData.writeFloat(average);
- }
- soundData.position = 0;
- for (var i:uint = 0; i < soundData.length; i += 4) // increment by 4 because a float is 32 bits (4 bytes)
- {
- m_buf[m_writePos] = soundData.readFloat();
- m_writePos = (m_writePos + 1);
- if (m_writePos >= BUF_LEN)
- {
- var percent = (i / soundData.length) * 100;
- chunkCount++;
- trace(percent + "%" + " : " + chunkCount);
- m_writePos = 0;
- updateSpectrum();
- }
- }
- // do one final update for the unfinished chunk here - have to zero out the remained of m_buf though otherwise you'll have weird data at the end
- // without this you'll lose anywhere from the last 0 to 2048 samples which won't be very noticable but this is easy enough
- if (m_writePos > 0)
- {
- for ( ; m_writePos < BUF_LEN - 1; ++m_writePos )
- {
- m_buf[m_writePos] = 0;
- }
- updateSpectrum();
- }
- m_sound.play();
- m_timer.start();
- done = true;
- }
- private function updateSpectrum():void
- {
- // Copy data from circular microphone audio
- // buffer into temporary buffer for FFT, while
- // applying Hanning window.
- var i:int;
- var pos:uint = m_writePos;
- for ( i = 0; i < N; i++ )
- {
- m_tempRe[i] = m_win[i]*m_buf[pos];
- pos = (pos+1)%BUF_LEN;
- }
- // Zero out the imaginary component
- for ( i = 0; i < N; i++ )
- m_tempIm[i] = 0.0;
- // Do FFT and get magnitude spectrum
- m_fft.run( m_tempRe, m_tempIm );
- for ( i = 0; i < N/2; i++ )
- {
- var re:Number = m_tempRe[i];
- var im:Number = m_tempIm[i];
- m_mag[i] = Math.sqrt(re*re + im*im);
- }
- // Convert to dB magnitude
- const SCALE:Number = 20/Math.LN10;
- for ( i = 0; i < N/2; i++ )
- {
- // 20 log10(mag) => 20/ln(10) ln(mag)
- // Addition of MIN_VALUE prevents log from returning minus infinity if mag is zero
- m_mag[i] = SCALE*Math.log( m_mag[i] + Number.MIN_VALUE );
- }
- manyMags.push(m_mag);
- // Draw the graph
- //drawSpectrum( m_mag, m_freq );
- }
- private function drawSpectrum(
- mag:Vector.<Number>,
- freq:Vector.<Number> ):void
- {
- // Basic constants
- const MIN_FREQ:Number = 0; // Minimum frequency (Hz) on horizontal axis.
- const MAX_FREQ:Number = 4000; // Maximum frequency (Hz) on horizontal axis.
- const FREQ_STEP:Number = 500; // Interval between ticks (Hz) on horizontal axis.
- const MAX_DB:Number = -0.0; // Maximum dB magnitude on vertical axis.
- const MIN_DB:Number = -60.0; // Minimum dB magnitude on vertical axis.
- const DB_STEP:Number = 10; // Interval between ticks (dB) on vertical axis.
- const TOP:Number = 50; // Top of graph
- const LEFT:Number = 60; // Left edge of graph
- const HEIGHT:Number = 300; // Height of graph
- const WIDTH:Number = 500; // Width of graph
- const TICK_LEN:Number = 10; // Length of tick in pixels
- const LABEL_X:String = "Frequency (Hz)"; // Label for X axis
- const LABEL_Y:String = "dB"; // Label for Y axis
- // Derived constants
- const BOTTOM:Number = TOP+HEIGHT; // Bottom of graph
- const DBTOPIXEL:Number = HEIGHT/(MAX_DB-MIN_DB); // Pixels/tick
- const FREQTOPIXEL:Number = WIDTH/(MAX_FREQ-MIN_FREQ);// Pixels/Hz
- //-----------------------
- var i:uint;
- var numPoints:uint;
- numPoints = mag.length;
- if ( mag.length != freq.length )
- trace( "mag.length != freq.length" );
- graphics.clear();
- // Draw a rectangular box marking the boundaries of the graph
- graphics.lineStyle( 1, 0x000000 );
- graphics.drawRect( LEFT, TOP, WIDTH, HEIGHT );
- graphics.moveTo(LEFT, TOP+HEIGHT);
- //--------------------------------------------
- // Tick marks on the vertical axis
- var y:Number;
- var x:Number;
- for ( var dBTick:Number = MIN_DB; dBTick <= MAX_DB; dBTick += DB_STEP )
- {
- y = BOTTOM - DBTOPIXEL*(dBTick-MIN_DB);
- graphics.moveTo( LEFT-TICK_LEN/2, y );
- graphics.lineTo( LEFT+TICK_LEN/2, y );
- if ( m_tickTextAdded == false )
- {
- // Numbers on the tick marks
- var t:TextField = new TextField();
- t.text = int(dBTick).toString();
- t.width = 0;
- t.height = 20;
- t.x = LEFT-20;
- t.y = y - t.textHeight/2;
- t.autoSize = TextFieldAutoSize.CENTER;
- addChild(t);
- }
- }
- // Label for vertical axis
- if ( m_tickTextAdded == false )
- {
- t = new TextField();
- t.text = LABEL_Y;
- t.x = LEFT-50;
- t.y = TOP + HEIGHT/2 - t.textHeight/2;
- t.height = 20;
- t.width = 50;
- //t.rotation = -90;
- addChild(t);
- }
- //--------------------------------------------
- // Tick marks on the horizontal axis
- for ( var f:Number = MIN_FREQ; f <= MAX_FREQ; f += FREQ_STEP )
- {
- x = LEFT + FREQTOPIXEL*(f-MIN_FREQ);
- graphics.moveTo( x, BOTTOM - TICK_LEN/2 );
- graphics.lineTo( x, BOTTOM + TICK_LEN/2 );
- if ( m_tickTextAdded == false )
- {
- t = new TextField();
- t.text = int(f).toString();
- t.width = 0;
- t.x = x;
- t.y = BOTTOM+7;
- t.autoSize = TextFieldAutoSize.CENTER;
- addChild(t);
- }
- }
- // Label for horizontal axis
- if ( m_tickTextAdded == false )
- {
- t = new TextField();
- t.text = LABEL_X;
- t.width = 0;
- t.x = LEFT+WIDTH/2;
- t.y = BOTTOM+30;
- t.autoSize = TextFieldAutoSize.CENTER;
- addChild(t);
- }
- m_tickTextAdded = true;
- // -------------------------------------------------
- // The line in the graph
- // Ignore points that are too far to the left
- for ( i = 0; i < numPoints && freq[i] < MIN_FREQ; i++ )
- {
- }
- // For all remaining points within range of x-axis
- var firstPoint:Boolean = true;
- for ( /**/; i < numPoints && freq[i] <= MAX_FREQ; i++ )
- {
- // Compute horizontal position
- x = LEFT + FREQTOPIXEL*(freq[i]-MIN_FREQ);
- // Compute vertical position of point
- // and clip at top/bottom.
- y = BOTTOM - DBTOPIXEL*(mag[i]-MIN_DB);
- if ( y < TOP )
- y = TOP;
- else if ( y > BOTTOM )
- y = BOTTOM;
- // If it's the first point
- if ( firstPoint )
- {
- // Move to the point
- graphics.moveTo(x,y);
- firstPoint = false;
- }
- else
- {
- // Otherwise, draw line from the previous point
- graphics.lineTo(x,y);
- }
- }
- }
- }
- /*
- var file:File;
- var loader:URLLoader;
- var sound:Sound;
- var waveForm:BitmapData;
- var samples:Array;
- }
- private var m_buf:Vector.<Number> = null;
- private var m_writePos:uint = 0;
- private var m_fft:FFT2; // FFT object
- private var m_tempRe:Vector.<Number>; // Temporary buffer - real part
- private var m_tempIm:Vector.<Number>; // Temporary buffer - imaginary part
- private var m_mag:Vector.<Number>; // Magnitudes (at each of the frequencies below)
- private var m_freq:Vector.<Number>; // Frequencies (for each of the magnitudes above)
- private var m_win:Vector.<Number>;
- private const LOGN:uint = 11; // Log2 FFT length
- private const N:uint = 1 << LOGN;
- private const BUF_LEN:uint = N;
- public function Main():void
- {
- waveForm = new BitmapData(800,100,true);
- file = File.userDirectory;
- var fileFilter:FileFilter = new FileFilter("MP3s", "*.mp3");
- file.browse([fileFilter]);
- file.addEventListener(Event.SELECT, loadFile);
- var i:uint;
- // Set up the FFT
- m_fft = new FFT2();
- m_fft.init(LOGN);
- m_tempRe = new Vector.<Number>(N);
- m_tempIm = new Vector.<Number>(N);
- m_mag = new Vector.<Number>(N/2);
- //m_smoothMag = new Vector.<Number>(N/2);
- // Vector with frequencies for each bin number. Used
- // in the graphing code (not in the analysis itself).
- m_freq = new Vector.<Number>(N/2);
- for ( i = 0; i < N/2; i++ )
- m_freq[i] = i*SAMPLE_RATE/N;
- // Hanning analysis window
- m_win = new Vector.<Number>(N);
- for ( i = 0; i < N; i++ )
- m_win[i] = (4.0/N) * 0.5*(1-Math.cos(2*Math.PI*i/N));
- // Create a buffer for the input audio
- m_buf = new Vector.<Number>(BUF_LEN);
- for ( i = 0; i < BUF_LEN; i++ )
- m_buf[i] = 0.0;
- }
- private function updateSpectrum():void
- {
- // Copy data from circular microphone audio
- // buffer into temporary buffer for FFT, while
- // applying Hanning window.
- var i:int;
- var pos:uint = m_writePos;
- for ( i = 0; i < N; i++ )
- {
- m_tempRe[i] = m_win[i]*m_buf[pos];
- pos = (pos+1)%BUF_LEN;
- }
- // Zero out the imaginary component
- for ( i = 0; i < N; i++ )
- m_tempIm[i] = 0.0;
- // Do FFT and get magnitude spectrum
- m_fft.run( m_tempRe, m_tempIm );
- for ( i = 0; i < N/2; i++ )
- {
- var re:Number = m_tempRe[i];
- var im:Number = m_tempIm[i];
- m_mag[i] = Math.sqrt(re*re + im*im);
- }
- // Convert to dB magnitude
- const SCALE:Number = 20/Math.LN10;
- for ( i = 0; i < N/2; i++ )
- {
- // 20 log10(mag) => 20/ln(10) ln(mag)
- // Addition of MIN_VALUE prevents log from returning minus infinity if mag is zero
- m_mag[i] = SCALE*Math.log( m_mag[i] + Number.MIN_VALUE );
- }
- }
- public function loadFile(e:Event):void
- {
- sound = new Sound(new URLRequest(file.url));
- //sound.load();
- sound.addEventListener(Event.COMPLETE, playMusic);
- }
- public function playMusic(e:Event):void
- {
- var sampleLength:Number = Math.floor((sound.length / 1000) * SAMPLE_RATE);
- var iunno:ByteArray = new ByteArray();
- sound.extract(iunno, sampleLength);
- iunno.position = 0;
- //var len:uint = iunno.length/4;
- for (var i:uint = 0; i < iunno.length; i++)
- {
- m_buf[m_writePos] = iunno.readFloat();
- m_writePos = (m_writePos + 1) % BUF_LEN;
- }
- sound.play();
- }
- }
- */
- }
Add Comment
Please, Sign In to add comment