// forked from seagirl's Dynamic Sound Generation /* Just experimenting with sound generation to learn how it works. Nothing to see here really Currently plays Ode To Joy Key: the note numbers are MIDI notes A0 (440hz) = note #69 Middle C (C0) = note #60 - Billy */ package { import flash.display.Sprite; import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; [SWF(backgroundColor="#ABCDEF")] public class DSG extends Sprite { public function DSG() { super(); init(); } private var timer:Timer; private var songOffset:Number = 0; //songs private var odeToJoy:Array = [64, 64, 65, 67, 67, 65, 64, 62, 60, 60, 62, 64, 64, 62, 62, 0, 0, 0, 0, 0]; //The song you want to play. Copy an array. private var song:Array = odeToJoy; private function init():void { timer = new Timer(500); timer.addEventListener(TimerEvent.TIMER, timerHandler); timer.start(); } private function playSong():void { if (songOffset >= song.length - 1) { songOffset = 0; } var noteNumber:int = song[songOffset]; new Tone(noteNumber, .5, 8, 1, 1, 5, 1).start(); songOffset++; } private function timerHandler(event:TimerEvent):void { playSong(); } } } import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; import flash.events.EventDispatcher; import flash.events.SampleDataEvent; import flash.media.Sound; import flash.utils.ByteArray; class Tone extends EventDispatcher { public static const SAMPLING_RATE:Number = 44100; public static const BUFFER_SIZE:int = 8192; public static const UNIT:int = 10; public function Tone(noteNumber:uint, length:Number, volume:Number, attack:Number, decay:Number, sustain:Number, release:Number) { this.frequency = noteNumber2frequency(noteNumber); this.length = length; this.volume = volume; this.attack = attack; this.decay = decay; this.sustain = sustain; this.release = release; check(); initializeVelocity(); } private var sound:Sound; private var phase:Number = 0; private var amplify:Number = 0; private var state:int = 1; // 1: Attack, 2: Decay, 3: Release private var frequency:Number; private var length:Number; private var volume:Number; private var attack:Number; private var decay:Number; private var sustain:Number; private var release:Number; private var v1:Number; private var v2:Number; private var v3:Number; private function check():void { if (attack + decay + release != UNIT) { attack = 0; decay = 5; release = 5; } if (volume > UNIT) volume = UNIT; if (volume < 0) volume = 0; if (sustain > UNIT) sustain = UNIT; if (sustain < 0) sustain = 0; } private function initializeVelocity():void { v1 = volume / (SAMPLING_RATE * length * attack / UNIT); v2 = (volume - sustain) / (SAMPLING_RATE * length * decay / UNIT); v3 = sustain / (SAMPLING_RATE * length * release / UNIT); } private function updateAmplify():void { if (state == 1) { amplify += v1; if (amplify >= volume) { amplify = volume; state = 2; } } else if (state == 2) { amplify -= v2; if (volume < sustain) { if (amplify >= sustain) { amplify = sustain; state = 3; } } else { if (amplify <= sustain) { amplify = sustain; state = 3; } } } else if (state == 3) { amplify -= v3; if (amplify <= 0) { amplify = 0; stop(); } } } public function start():void { sound = new Sound(); sound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler); sound.play(); } public function stop():void { sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, soundSampleDataHandler); dispatchEvent(new Event(Event.COMPLETE)); } private function soundSampleDataHandler(event:SampleDataEvent):void { var bytes:ByteArray = new ByteArray(); for (var i:int = 0; i < BUFFER_SIZE; ++i) { phase += frequency / SAMPLING_RATE; var phaseAngle:Number = phase * Math.PI * 2; var sample:Number = Math.sin(phaseAngle) * amplify / UNIT; sample *= 0.2; bytes.writeFloat(sample); bytes.writeFloat(sample); updateAmplify(); } event.data.writeBytes(bytes); } } function noteNumber2frequency(value:uint):Number { //Clamp to valid values if (value > 127) value = 127; return 440 * Math.pow(2, (value - 69) / 12); } Ode To Joy (Sound Generation fork)