package {
import __AS3__.vec.Vector;
import fl.controls.Slider;
import fl.events.SliderEvent;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.external.ExternalInterface;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.utils.ByteArray;
[SWF(width="640",height="360")]
public class Granular extends Sprite
{
private var _output:Sound;
private var _outputChannel:SoundChannel;
[Embed(source="lynn.mp3")]
private var SAMPLE_MP3:Class;
private var _sound:Sound;
private var _sampleData:ByteArray;
private var _numBytes:int;
private var _bytePosition:int;
private var _speedSlider:Slider;
private var _grainSizeSlider:Slider;
private var _grainIntervalSlider:Slider;
private var _envelope:ByteArray;
private var _speed:Number = 1;
private var _grainSize:int;
private var _grainEnvelopeSpeed:int;
private var _grainInterval:int = 1000;
private var _grainIntervalCounter:int = 0;
private var _grainPool:GrainPool;
private var _activeGrains:Vector.<GrainDescription>;
private var _numActiveGrains:int = 0;
private var _grainCounter:int;
private var _tempGrain:GrainDescription;
public function Granular()
{
_speedSlider = new LabeledSlider("speed", -1, 2, 1, .01, true);
_speedSlider.addEventListener(SliderEvent.CHANGE, _speedSliderMoved);
_speedSlider.width = 540;
_speedSlider.x = 50;
_speedSlider.y = 50;
addChild(_speedSlider);
_grainSizeSlider = new LabeledSlider("grain length", 1, 5000, 1000, 1, true);
_grainSizeSlider.addEventListener(SliderEvent.CHANGE, _grainSizeSliderMoved);
_grainSizeSlider.width = 540;
_grainSizeSlider.x = 50;
_grainSizeSlider.y = 100;
addChild(_grainSizeSlider);
_grainIntervalSlider = new LabeledSlider("start interval", 1, 5000, 1000, 1, true);
_grainIntervalSlider.addEventListener(SliderEvent.CHANGE, _grainIntervalSliderMoved);
_grainIntervalSlider.width = 540;
_grainIntervalSlider.x = 50;
_grainIntervalSlider.y = 150;
addChild(_grainIntervalSlider);
_speedSlider.value = 1;
_speed = 1;
_grainSizeSlider.value = 1900;
_grainSize = 1900;
_grainEnvelopeSpeed = 44100 / _grainSize;
_grainIntervalSlider.value = 800;
_grainInterval = 800;
_activeGrains = new Vector.<GrainDescription>();
_grainPool = new GrainPool();
_createSineSquaredEnvelope();
_drawSignal(_envelope, 50, 320, 50, 540, 0x000066);
var ur:URLRequest = new URLRequest("lynn.mp3");
_sound = new SAMPLE_MP3();
_sampleData = new ByteArray();
_sound.extract(_sampleData, _sound.length*44100, 0);
_numBytes = _sampleData.length;
_floatLength = _sampleData.length/8;
_drawSignal(_sampleData, 50, 220, 50, 540, 0x006600);
_floatPosition = 0;
_bytePosition = 0;
_output = new Sound();
_output.addEventListener(SampleDataEvent.SAMPLE_DATA, _fillBuffer);
ExternalInterface.addCallback("start", start);
ExternalInterface.addCallback("stop", stop);
}
public function start():void {
_outputChannel = _output.play(0);
}
public function stop():void {
_outputChannel.stop();
}
private var _l:Number;
private var _r:Number;
private var _sample:int;
private var _amplitude:Number;
private var _grainSamplePosition:int;
private var _floatPosition:Number;
private var _floatLength:Number;
private function _fillBuffer(event:SampleDataEvent):void {
for (_sample=0; _sample<2048; ++_sample) {
_bytePosition = Math.floor(_floatPosition)*8;
_grainIntervalCounter++;
if (_grainIntervalCounter >= _grainInterval) {
_grainIntervalCounter = 0;
_tempGrain = _grainPool.getGrain();
_tempGrain.samplePositionTimes8 = _bytePosition;
_tempGrain.envelopePosition = 0;
_activeGrains.push(_tempGrain)
_numActiveGrains++;
}
_l = 0;
_r = 0;
for (_grainCounter=0; _grainCounter<_numActiveGrains; ++_grainCounter) {
_tempGrain = _activeGrains[_grainCounter];
_tempGrain.envelopePosition += _grainEnvelopeSpeed;
if (_tempGrain.envelopePosition >= 44100) {
_activeGrains.splice(_grainCounter, 1);
_numActiveGrains--;
_grainCounter--;
_grainPool.returnGrain(_tempGrain);
}
else {
_envelope.position = _tempGrain.envelopePosition * 4;
_amplitude = _envelope.readFloat();
_grainSamplePosition = _tempGrain.samplePositionTimes8;
_grainSamplePosition += 8;
if (_grainSamplePosition >= _numBytes) {
_grainSamplePosition -= _numBytes;
}
_tempGrain.samplePositionTimes8 = _grainSamplePosition;
_sampleData.position = _grainSamplePosition;
_l += _amplitude * _sampleData.readFloat();
_r += _amplitude * _sampleData.readFloat();
}
}
event.data.writeFloat(_l);
event.data.writeFloat(_r);
_floatPosition += _speed;
if (_floatPosition >= _floatLength) {
_floatPosition -= _floatLength;
}
else if (_floatPosition < 0) {
_floatPosition += _floatLength;
}
}
}
private function _speedSliderMoved(event:SliderEvent):void {
_speed = _speedSlider.value;
}
private function _grainSizeSliderMoved(event:SliderEvent):void {
_grainSize = _grainSizeSlider.value;
_grainEnvelopeSpeed = 44100 / _grainSize;
}
private function _grainIntervalSliderMoved(event:SliderEvent):void {
_grainInterval = _grainIntervalSlider.value;
}
private function _createSineSquaredEnvelope():void {
_envelope = new ByteArray();
var delta:Number = Math.PI / Number(44100);
for (var i:Number=0; i<44100; ++i) {
var val:Number = Math.sin(i*delta) * Math.sin(i*delta);
_envelope.writeFloat(val);
}
}
private function _drawSignal(bytes:ByteArray, _x:Number, _y:Number, vscale:Number, _width:Number, color:uint):void {
graphics.lineStyle(0, color);
var cnt:int = bytes.length/4;
var dx:Number = _width/(bytes.length/4);
bytes.position = 0;
graphics.moveTo(_x, _y);
for (var i:Number=0; i<cnt; ++i) {
graphics.lineTo(_x + i*dx, _y - vscale*bytes.readFloat());
}
}
}
}