Created
November 5, 2010 01:10
-
-
Save jeremyfromearth/663498 to your computer and use it in GitHub Desktop.
Example for rendering visualizations based on notes and beats using dynamic sound features, no actual audio is produced
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package makemachine.examples.audio.note_visualizer | |
{ | |
import com.bit101.components.*; | |
import flash.display.*; | |
import flash.events.*; | |
import flash.media.*; | |
import flash.utils.ByteArray; | |
/** | |
* Example for rendering visualizations based on notes and beats, | |
* no audio is produced | |
* For details: | |
* http://labs.makemachine.net/2010/11/visualizing-notes-and-timing/ | |
* | |
* @author Jeremy Brown | |
*/ | |
[SWF( backgroundColor="0x222222", width="620", height="110", frameRate="60" )] | |
public class NoteVisualizerExample extends Sprite | |
{ | |
public static const BUFFER_SIZE:int = 8192; | |
public static const SAMPLE_RATE:int = 44100; | |
public static const MILS_PER_SEC:int = 1000; | |
public static const WIDTH:int = 570; | |
protected var _outsound:Sound; | |
protected var _channel:SoundChannel; | |
protected var _playing:Boolean; | |
protected var _tempo:int; | |
protected var _quarterNotes:Vector.<Shape>; | |
protected var _measures:Vector.<Shape>; | |
protected var _eighthNotes:Vector.<Shape>; | |
protected var _gridContainer:Sprite; | |
protected var _timeClockLabel:Label; | |
protected var _musicClockLabel:Label; | |
protected var _button:PushButton; | |
protected var _slider:HUISlider | |
public function NoteVisualizerExample() | |
{ | |
addEventListener( Event.ENTER_FRAME, validate ); | |
} | |
/** | |
* Make sure the stage has been initalized | |
*/ | |
protected function validate( event:Event ):void | |
{ | |
if( !stage ) return; | |
if( stage.stageWidth == 0 ) return; | |
stage.align = StageAlign.TOP_LEFT; | |
stage.scaleMode = StageScaleMode.NO_SCALE; | |
_outsound = new Sound(); | |
createDisplay(); | |
removeEventListener( Event.ENTER_FRAME, validate ); | |
} | |
// ---------------------------------------------- | |
// | |
// -- sound | |
// | |
// ---------------------------------------------- | |
/** | |
* Toggles between play and stop | |
*/ | |
protected function toggle( event:Event = null ):void | |
{ | |
if( _playing ) { | |
stop(); | |
}else { | |
play(); | |
} | |
} | |
/** | |
* Stops playback, updates UI | |
*/ | |
protected function stop():void | |
{ | |
if( _channel && _playing ) | |
{ | |
_playing = false; | |
_button.label = 'Play'; | |
_outsound.removeEventListener( SampleDataEvent.SAMPLE_DATA, onSampleData ); | |
_channel.stop(); | |
_channel = null; | |
removeEventListener( Event.ENTER_FRAME, onPlaybackEnterFrame ); | |
} | |
} | |
/** | |
* Starts playback, updates UI | |
*/ | |
protected function play():void | |
{ | |
if( !_playing ) | |
{ | |
_playing = true; | |
_button.label = 'Stop'; | |
addEventListener( Event.ENTER_FRAME, onPlaybackEnterFrame ); | |
_outsound.addEventListener( SampleDataEvent.SAMPLE_DATA, onSampleData ); | |
_channel = _outsound.play(); | |
} | |
} | |
/** | |
* Fill the buffer with data, in this example we are just filling it with silence | |
*/ | |
protected function onSampleData( event:SampleDataEvent ):void | |
{ | |
var bytes:ByteArray = new ByteArray(); | |
for( var i:int = 0; i < BUFFER_SIZE; i++ ) | |
{ | |
bytes.writeFloat( 0 ); | |
} | |
event.data.writeBytes( bytes ); | |
} | |
/** | |
* Updates the grid of squares to reflect the current 1/8, 1/4 and measure | |
* Updates the timecode clocks | |
* Not optimized | |
*/ | |
protected function onPlaybackEnterFrame( event:Event ):void | |
{ | |
if( _channel ) | |
{ | |
var samplesElapsed:int = ( _channel.position / 1000 ) * SAMPLE_RATE; | |
var eighthNoteLength:int = SAMPLE_RATE * .5 * 60 / _tempo; | |
var quarterNoteLength:int = eighthNoteLength * 2; | |
var measureLength:int = quarterNoteLength * 4; | |
var eighth:int = Math.floor( samplesElapsed / eighthNoteLength % 32 ); | |
var quarter:int = Math.floor( ( samplesElapsed / quarterNoteLength % 16 ) ); | |
var measure:int = Math.floor( ( samplesElapsed / measureLength % 4 ) ); | |
var current:int; | |
var i:int = 0; | |
// -- current 1/8 note | |
for( i = 0; i < _eighthNotes.length; i++ ) | |
{ | |
if( i == eighth ) | |
{ | |
_eighthNotes[i].alpha = 1 | |
} else { | |
_eighthNotes[i].alpha = .5; | |
} | |
} | |
// -- current 1/4 note | |
for( i = 0; i < _quarterNotes.length; i++ ) | |
{ | |
if( i == quarter ) | |
{ | |
_quarterNotes[i].alpha = 1; | |
} else { | |
_quarterNotes[i].alpha = .5; | |
} | |
} | |
// -- current measure | |
for( i = 0; i < _measures.length; i++ ) | |
{ | |
if( i == measure ) | |
{ | |
_measures[i].alpha = 1; | |
} else { | |
_measures[i].alpha = .5; | |
} | |
} | |
// -- clocks | |
_timeClockLabel.text = 'Real Time: ' + | |
toTimecode( String( Math.floor( _channel.position / 1000 / 60 ) ) ) + ':' + | |
toTimecode( String(Math.floor( _channel.position / 1000 % 60 ) ) ) + ':' + | |
toTimecode( String( Math.floor( _channel.position / 10 % 100 ) ) ); | |
_musicClockLabel.text = 'Music Time: ' + | |
toTimecode( String( measure + 1 ) ) + ':' + | |
toTimecode( String( quarter + 1 ) ) + ':' + | |
toTimecode( String( eighth + 1 ) ); | |
} | |
} | |
/** | |
* Update the value of the tempo | |
*/ | |
protected function onTempoChange( event:Event ):void | |
{ | |
_tempo = _slider.value; | |
} | |
// ---------------------------------------------- | |
// | |
// -- display | |
// | |
// ---------------------------------------------- | |
/** | |
* Creates rows of squares indicating 1/8, 1/4 and measures | |
* Also creates button and text fields | |
*/ | |
protected function createDisplay():void | |
{ | |
var i:int; | |
var w:Number; | |
var shape:Shape; | |
var padding:int = 2; | |
_gridContainer = new Sprite(); | |
w = ( WIDTH / 32 ) - padding; | |
_eighthNotes = new Vector.<Shape>(); | |
// -- eighth note squares | |
for( i = 0; i < 32; i++ ) | |
{ | |
shape = getRectangleShape( w, 0x00C6FF ); | |
shape.x = ( i * w ) + ( i * padding ); | |
shape.alpha = .5; | |
_eighthNotes.push( shape ); | |
_gridContainer.addChild( shape ); | |
} | |
// -- quarter note squares | |
w = ( WIDTH / 16 ) - padding; | |
_quarterNotes = new Vector.<Shape>(); | |
for( i = 0; i < 16; i++ ) | |
{ | |
shape = getRectangleShape( w, 0x00C6FF ); | |
shape.y = 22; | |
shape.x = ( i * w ) + ( i * padding ); | |
shape.alpha = .5; | |
_quarterNotes.push( shape ); | |
_gridContainer.addChild( shape ); | |
} | |
// -- measure squares | |
w = ( WIDTH / 4 ) - padding; | |
_measures = new Vector.<Shape>(); | |
for( i = 0; i < 4; i++ ) | |
{ | |
shape = getRectangleShape( w, 0x00C6FF ); | |
shape.y = 44; | |
shape.x = ( i * w ) + ( i * padding ); | |
shape.alpha = .5; | |
_measures.push( shape ); | |
_gridContainer.addChild( shape ); | |
} | |
new Label( this, 10, 10, '1/8' ); | |
new Label( this, 10, 30, '1/4' ); | |
new Label( this, 10, 50, '1/16' ); | |
_gridContainer.x = 40; | |
_gridContainer.y = 10; | |
_timeClockLabel = new Label( this, 40, 80, 'Real Time: 00:00:00' ); | |
_musicClockLabel = new Label( this, 150, 80, 'Music Time: 01:04:16' ); | |
_slider = new HUISlider( this, 220, 80, 'Tempo', onTempoChange ); | |
_slider.minimum = 40; | |
_slider.maximum = 320; | |
_slider.value = _tempo = 120; | |
_slider.x = 270; | |
_slider.y = 80; | |
_slider.width = 250; | |
_slider.labelPrecision = 0; | |
_button = new PushButton( this, 507, 80, 'Play', toggle ); | |
_button.toggle = true; | |
addChild( _gridContainer ); | |
} | |
/** | |
* Convenience method for creating a rectangle | |
*/ | |
protected function getRectangleShape( w:int, color:uint ):Shape | |
{ | |
var shape:Shape = new Shape(); | |
var g:Graphics = shape.graphics; | |
g.beginFill( color ); | |
g.drawRoundRect( 0, 0, w, 20, 3, 3 ); | |
g.endFill(); | |
addChild( shape ); | |
return shape; | |
} | |
/** | |
* Make sure our timecode string has at least two digits | |
*/ | |
protected function toTimecode( value:String ):String | |
{ | |
if( value.length < 2 ) { | |
return '0' + value; | |
} | |
return value; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment