Source code available here
Note: This example uses Minimal Comps by Keith Peters
Audio by: Flying Lotus
Unlike other audio experiments I’ve been working on, this prototype uses pre-recorded audio. It extracts the raw byte data from an mp3 and draws each of the samples from left to right. The algorithm reads small chunks of the data on each frame and renders it. It is possible to read the entire track all at once but this causes they player to hang while it is calculating. The top render represents the selected portion of the audio track and the lower render represents the entire track. The slider allows you to vary the amount of detail in the rendered wave form. A value of 1 means that every single sample in the selected track is being rendered. For longer tracks, this can cause somewhat sluggish drawing. A value of 1000 means that every thousandth sample is being rendered, hence the waveform loses detail.
The basic algorithm for drawing chunks of audio data looks something like the following pseudo-code:
var n:Number; var bytes:ByteArray = new ByteArray(); var length:int = position + CHUNK_SIZE < outsample ? CHUNK_SIZE : outsample - position; insound.extract( bytes, length, position ); bytes.position = 0; while( bytes.position < bytes.length ) { // -- average left and right channles n = bytes.readFloat() + bytes.readFloat(); n *= .5; // -- this modulus allows us to only draw every other nth sample if( position % detail == 0 ) { r.lineTo( xpos, n * 100 ); xpos += lineLength; } // -- increment the position position ++; } if( position == outsample ) { // -- all done return; }
You can drag the little red in/out markers side to side to select the portion of the audio you want to loop. You can also drag the entire scrubber ( bluish area ) around. Pressing play causes the selected audio to begin looping. Previous to the dynamic sound features, achieving a seamless audio loop was nearly impossible on the Flash Platform. Now that we have the Sound.extract
it is possible. Using extract
in tandem with the Event.SOUND_COMPLETE
event, a simple audio ring buffer can be created, as demonstrated by the pseudo-code below:
protected function play():void { loopflag = false; outsound.addEventListener( SampleDataEvent.SAMPLE_DATA, onSampleData ); channel = outsound.play(); channel.addEventListener( Event.SOUND_COMPLETE, onSoundComplete ); } // -- keep filling the buffer with data protected function onSampleData( event:SampleDataEvent ):void { var bytes:ByteArray = new ByteArray(); var length:int = BUFFER_SIZE; if( loopflag ) return; if( position + event.position + BUFFER_SIZE > outsample ) { // -- if the next iteration would extend beyond the current // -- selection reset the length var so that only the number of // -- samples to finish the loop are extracted length = outsample - ( position + event.position ); loopflag = true; } insound.extract( bytes, length, _position + event.position ); event.data.writeBytes( bytes ); } // -- starts audio as soon as there are no more samples in the buffer protected function onSoundComplete( event:Event ):void { play(); }
The above code snippets are just samples. Full source code is available at the makemachine google code repository.