flash on 2013-1-19
/**
* Copyright quilime ( http://wonderfl.net/user/quilime )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/Ahva
*/
package
{
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.Timer;
[SWF(backgroundColor='#ffffff')]
public class Main extends Sprite {
private var
sound:Sound = new Sound;
public function Main() {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
var control:Control = new Control(this);
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound.play();
}
private function onSampleData(event:SampleDataEvent):void {
var outBuffer:ByteArray = new ByteArray;
var synthBuffer:ByteArray = _synth_.generateBytes( BUFFER_SIZE );
var delayBuffer:ByteArray = _delayEffect_.generateBytes( synthBuffer, BUFFER_SIZE );
var reverbBuffer:ByteArray = _reverbEffect_.generateBytes( delayBuffer, BUFFER_SIZE );
outBuffer = reverbBuffer;
event.data.writeBytes( outBuffer );
}
}
}
import com.bit101.components.CheckBox;
import com.bit101.components.IndicatorLight;
import com.bit101.components.Knob;
import com.bit101.components.Label;
import com.bit101.components.NumericStepper;
import com.bit101.components.PushButton;
import com.bit101.components.RadioButton;
import com.bit101.components.Style;
import com.bit101.components.WheelMenu;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.external.ExternalInterface;
import flash.geom.Point;
import flash.utils.ByteArray;
const SAMPLE_RATE :int = 44100; // 22050;
const BUFFER_SIZE :int = 2048; // seaquence is at 3072 (?);
// globals
var
_bpm_:Number = 60;
class AudioGenerator {
public var
buffer:ByteArray;
// generate a byteArray
public function generateBytes( _bufferSize:Number ):ByteArray {
buffer = new ByteArray();
var sample:Number = 0;
for (var i:int = 0; i < _bufferSize; ++i) {
sample = 0;
buffer.writeFloat( sample ); // left
buffer.writeFloat( sample ); // right
}
return buffer;
}
}
class TestSynth extends AudioGenerator {
private var
timer:Oscillator = new Oscillator,
osc:Oscillator = new Oscillator,
osc2:Oscillator = new Oscillator,
filter:Filter = new Filter,
env:Envelope = new Envelope,
currentCount:Number = 0,
envBuff:Number = 0,
tone:Number = 0;
public var
mode:int = 0;
override public function generateBytes( _bufferSize:Number ):ByteArray {
buffer = new ByteArray();
var sample:Number = 0;
for (var i:int = 0; i < _bufferSize; ++i) {
switch (mode) {
case -1 :
// test tone
sample = osc.sinewave(440);
break;
case 0 :
break;
case 1 :
// annoying siren
currentCount = timer.phasor(_bpm_ / 60 / 2, 1, 2);
sample = osc.triangle(currentCount * 300);
break;
case 2 :
// fun w/filters
currentCount = timer.phasor(_bpm_ / 60 / 2, 1, 9);
if (currentCount < 5) {
sample = osc.square(currentCount * 100);
}
else if (currentCount >=5 ){
sample = osc.saw(currentCount * 50);
}
sample = filter.lores(sample, osc2.phasor(0.25, 20, 2000), 50);
break;
case 3 :
// single tone
var adsrEnv:Array = [ 0, 0,
1.0, 10,
1.0, 250,
0, 50 ];
//[ 1.0, 5, 1.0, 100, 1.0, 0, 0, 100 ];
if (int(timer.phasor(_bpm_ / 60)) != 0) {
env.trigger(0, adsrEnv[0]);
}
envBuff = env.line(8, adsrEnv); //our ADSR env has 8 value/time pairs.
sample = osc.sinewave(440 * 2) * envBuff;
break;
case 4 :
// some nice tones
var adsrEnv2:Array = [ 0, 0,
1.0, 10,
1.0, 250,
0, 50 ];
if (int(timer.phasor(_bpm_ / 60)) != 0) {
env.trigger(0, adsrEnv2[0]);
tone = Math.floor(Math.random() * 20 - 5);
}
envBuff = env.line(8, adsrEnv2); //our ADSR env has 4 value/time pairs.
sample = osc.sinewave(440 / 4 * tone) * envBuff;
break;
default:
break;
}
// turn it down a hair just in case
sample *= 0.75;
buffer.writeFloat(sample); // left
buffer.writeFloat(sample); // right
}
return buffer;
}
}
var _synth_:TestSynth = new TestSynth;
class Effect {
public var
bypass:Boolean = true,
buffer:ByteArray;
// passthrough by default
public function generateBytes( _buffer:ByteArray, _bufferSize:int ):ByteArray {
buffer = new ByteArray();
var length:int = _buffer.length;
_buffer.position = 0
while (_buffer.position < length) {
var sample_l:Number = _buffer.readFloat(); // left
var sample_r:Number = _buffer.readFloat(); // right
buffer.writeFloat(sample_l); // left
buffer.writeFloat(sample_r); // right
}
return buffer;
}
}
class ReverbEffect extends Effect {
//----------------------------------------------------------------------------------------------------
// SiOPM effect stereo reverb
// Copyright (c) 2009 keim All rights reserved.
// Modified for generic use 2013-18-01 Gabriel Dunne
// Distributed under BSD-style license (see org.si.license.txt).
//----------------------------------------------------------------------------------------------------
static private const DELAY_BUFFER_BITS:int = 13;
static private const DELAY_BUFFER_FILTER:int = (1<<DELAY_BUFFER_BITS)-1;
private var _delayBufferL:Vector.<Number>, _delayBufferR:Vector.<Number>;
private var _pointerRead0:int, _pointerRead1:int, _pointerRead2:int;
private var _pointerWrite:int;
private var _feedback0:Number, _feedback1:Number, _feedback2:Number;
private var _wet:Number;
private var
filter_l:Filter = new Filter,
filter_r:Filter = new Filter;
// constructor
//------------------------------------------------------------
public function ReverbEffect(delay1:Number = 0.7, delay2:Number = 0.4, feedback:Number = 0.8, wet:Number = 0.3) {
_delayBufferL = new Vector.<Number>(1<<DELAY_BUFFER_BITS);
_delayBufferR = new Vector.<Number>(1<<DELAY_BUFFER_BITS);
setParameters(delay1, delay2, feedback, wet);
prepareProcess();
}
// operation
//------------------------------------------------------------
/** set parameters
* @param delay1 long delay(0-1).
* @param delay2 short delay(0-1).
* @param feedback feedback decay(-1-1). Negative value to invert phase.
* @param wet mixing level(0-1).
*/
public function setParameters(delay1:Number=0.7, delay2:Number=0.4, feedback:Number=0.8, wet:Number=0.3) : void
{
if (delay1<0.01) delay1=0.01;
else if (delay1>0.99) delay1=0.99;
if (delay2<0.01) delay2=0.01;
else if (delay2>0.99) delay2=0.99;
_pointerWrite = (_pointerRead0 + DELAY_BUFFER_FILTER) & DELAY_BUFFER_FILTER;
_pointerRead1 = (_pointerRead0 + DELAY_BUFFER_FILTER*(1-delay1)) & DELAY_BUFFER_FILTER;
_pointerRead2 = (_pointerRead0 + DELAY_BUFFER_FILTER*(1-delay2)) & DELAY_BUFFER_FILTER;
if (feedback>0.99) feedback=0.99;
else if (feedback<-0.99) feedback=-0.99;
_feedback0 = feedback*0.2;
_feedback1 = feedback*0.3;
_feedback2 = feedback*0.5;
_wet = wet;
}
public function prepareProcess() : int
{
var i:int, imax:int = 1<<DELAY_BUFFER_BITS;
for (i=0; i<imax; i++) _delayBufferL[i] = _delayBufferR[i] = 0;
return 2;
}
override public function generateBytes( _buffer:ByteArray, _bufferSize:int ):ByteArray {
buffer = new ByteArray();
var length:int = _buffer.length;
_buffer.position = 0;
while (_buffer.position < length) {
var
n:Number, m:Number,
dry:Number = 1 - _wet;
var sample_l:Number = _buffer.readFloat(); // left
var sample_r:Number = _buffer.readFloat(); // right
if (!bypass) {
// left
n = _delayBufferL[_pointerRead0] * _feedback0;
n += _delayBufferL[_pointerRead1] * _feedback1;
n += _delayBufferL[_pointerRead2] * _feedback2;
_delayBufferL[_pointerWrite] = sample_l - n;
sample_l *= dry;
sample_l += n * _wet;
// right
n = _delayBufferR[_pointerRead0] * _feedback0;
n += _delayBufferR[_pointerRead1] * _feedback1;
n += _delayBufferR[_pointerRead2] * _feedback2;
_delayBufferR[_pointerWrite] = sample_r - n;
sample_r *= dry;
sample_r += n * _wet;
_pointerWrite = (_pointerWrite + 1) & DELAY_BUFFER_FILTER;
_pointerRead0 = (_pointerRead0 + 1) & DELAY_BUFFER_FILTER;
_pointerRead1 = (_pointerRead1 + 1) & DELAY_BUFFER_FILTER;
_pointerRead2 = (_pointerRead2 + 1) & DELAY_BUFFER_FILTER;
//sample_l += filter_l.lores(sample_l, 740, 1);
//sample_r += filter_r.lores(sample_r, 740, 1);
}
buffer.writeFloat(sample_l); // left
buffer.writeFloat(sample_r); // right
}
return buffer;
}
}
var _reverbEffect_:ReverbEffect = new ReverbEffect;
class DelayEffect extends Effect {
private var
delay:DelayLine = new DelayLine,
delay_l:DelayLine = new DelayLine,
delay_r:DelayLine = new DelayLine,
delay_time_mult:Number = 0.5,
delay_mix:Number = 0.75,
delay_feedback:Number = 0.65;
function DelayEffect():void{
setParameters();
}
public function setParameters(_delay_mix:Number = 0.75, _delay_feedback:Number = 0.65, _delay_time_mult:Number = 0.5) : void {
delay_mix = _delay_mix,
delay_feedback = _delay_feedback;
delay_time_mult = _delay_time_mult;
}
override public function generateBytes( _buffer:ByteArray, _bufferSize:int ):ByteArray {
buffer = new ByteArray();
var length:int = _buffer.length;
_buffer.position = 0;
while (_buffer.position < length) {
var sample_l:Number = _buffer.readFloat(); // left
var sample_r:Number = _buffer.readFloat(); // right
if (!bypass) {
sample_l = sample_l + (delay.dl(sample_l, BUFFER_SIZE * 16 * delay_time_mult, delay_feedback) * delay_mix);
sample_r = sample_r + (delay.dl(sample_r, BUFFER_SIZE * 16 * delay_time_mult, delay_feedback) * delay_mix);
}
buffer.writeFloat(sample_l); // left
buffer.writeFloat(sample_r); // right
}
return buffer;
}
}
var _delayEffect_:DelayEffect = new DelayEffect;
class DelayLine {
private var
frequency:Number = 0,
phase:int = 0,
output:Number = 0,
memory:Vector.<Number> = new Vector.<Number>(88200);
public function dl(input:Number, size:int, feedback:Number):Number {
if ( phase >= size ) {
phase = 0;
}
phase += 1;
memory[phase] = (memory[phase] * feedback) + (input * feedback) * 0.5;
return memory[phase];
}
}
class Oscillator {
public var
frequency:Number = 0,
phase:Number = 0,
startphase:Number = 0,
endphase:Number = 0,
output:Number = 0,
position:Number = 0;
// phasor
// returns a value that oscillates along a linear ramp between two values at a specific frequency
public function phasor(frequency:Number, startphase:Number = 0, endphase:Number = 1.0):Number {
if (phase < startphase) {
phase = startphase;
}
if ( phase >= endphase ) {
phase = startphase;
}
phase += (endphase - startphase) / (SAMPLE_RATE / frequency);
return phase;
}
// sinewave
public function sinewave(frequency:Number):Number {
phase = (position++) / SAMPLE_RATE * Math.PI * 2;
return Math.sin(phase * frequency);
}
// saw
// (phasor between -1 and 1)
public function saw(frequency:Number):Number {
return phasor(frequency, -1, 1);
}
// triangle
public function triangle(frequency:Number):Number {
if ( phase >= 1.0 ) {
phase -= 1.0;
}
phase += 1.0 / ( SAMPLE_RATE / frequency );
output = phase <= 0.5 ? (phase - 0.25) * 4: ((1.0-phase) - 0.25) * 4;
return output;
}
// square
public function square(frequency:Number):Number {
if (phase < 0.5) {
output = -1;
}
if (phase > 0.5) {
output = 1;
}
if ( phase >= 1.0 ) {
phase -= 1.0;
}
phase += 1.0 / (SAMPLE_RATE / frequency);
return(output);
}
// noise (white)
public function noise():Number {
return Math.random() * 2 - 1;
}
}
class Filter {
public var
gain:Number = 0,
input:Number = 0,
output:Number = 0,
inputs:Array = new Array(),
outputs:Array = new Array(),
cutoff:Number = 0,
cutoff1:Number = 0,
resonance:Number = 0,
x:Number = 0, // speed
y:Number = 0, // position
z:Number = 0, // pole
c:Number = 0; // filter coefficient
// lores filter
// from Maximilian
// takes an audio input, a frequency and a resonance factor (1-100)
// cuttof is freq in hz. res is between 1 and whatever. Watch out!
public function lores(input:Number, cutoff1:Number, resonance:Number):Number {
cutoff = cutoff1 * 0.5;
if (cutoff < 10) {
cutoff = 10;
}
if (cutoff > (SAMPLE_RATE * 0.5)) {
cutoff = (SAMPLE_RATE * 0.5);
}
if (resonance < 1.) {
resonance = 1.;
}
z = Math.cos(Math.PI * 2 * cutoff / SAMPLE_RATE);
c = 2 - 2 * z;
var r:Number = (Math.sqrt( 2.0 ) * Math.sqrt(-Math.pow((z - 1.0), 3.0)) + resonance * (z - 1))/(resonance *(z - 1));
x = x + (input - y) * c;
y = y + x;
x = x * r;
output = y;
return output;
}
// hires filter
// from Maximilian
public function hires(input:Number, cutoff1:Number, resonance:Number):Number {
cutoff = cutoff1 * 0.5;
if (cutoff < 10) {
cutoff = 10;
}
if (cutoff > SAMPLE_RATE * 0.5) {
cutoff = SAMPLE_RATE * 0.5;
}
if (resonance < 1.0) {
resonance = 1.0;
}
z = Math.cos( Math.PI * 2 * cutoff / SAMPLE_RATE);
c = 2 - 2 * z;
var r:Number = (Math.sqrt(2.0) * Math.sqrt(-Math.pow((z - 1.0), 3.0)) + resonance * (z - 1)) / (resonance * (z - 1));
x = x + (input-y) * c;
y = y + x;
x = x * r;
output = input - y;
return output;
}
}
class Envelope {
public var
input:Number = 0,
period:Number = 0,
amplitude:Number = 0,
triggered:int = 0,
startval:Number = 0,
currentval:Number = 0,
nextval:Number = 0,
isPlaying:int = 0,
valindex:int = 0;
public function trigger(index:int, amp:Number):void {
isPlaying = 1;
valindex = index;
amplitude = amp;
}
// envelope line
// from Maximilian
// numberofsegments: number of time/value pairs
// segments: envelope array holding time/value pairs
public function line(numberofsegments:int, segments:Array):Number {
// This is a basic multi-segment ramp generator that you can use for more or less anything.
if (isPlaying == 1) {
period = 2. / (segments[valindex + 1] * 0.004);
nextval = segments[valindex + 2];
currentval = segments[valindex];
if (currentval - amplitude > 0.0000001 && valindex < numberofsegments) {
amplitude += ((currentval - startval) / (SAMPLE_RATE/period));
} else if (currentval - amplitude < -0.0000001 && valindex < numberofsegments) {
amplitude -= (((currentval - startval) * (-1)) / (SAMPLE_RATE / period));
} else if (valindex > numberofsegments - 1) {
valindex = numberofsegments - 2;
} else {
valindex = valindex + 2;
startval = currentval;
}
return amplitude;
}
else {
return 0;
}
}
}
// control
class Control extends Sprite {
private var
bpmKnob:Knob,
delayLabel:Label, delayTimeKnob:Knob, delayMixKnob:Knob, delayFeedbackKnob:Knob,
reverbLabel:Label, reverbWetKnob:Knob, reverbFeedbackKnob:Knob;
private var eqt:Vector.<Number> = new Vector.<Number>(101), moving:Boolean;
private var rvbt:Vector.<Number> = new Vector.<Number>(101);
function Control(parent:DisplayObjectContainer) {
super();
// for (var i:int=0; i<101; i++) eqt[i] = Math.pow(2, i*0.04-2);
// low = _newKnob(280, "EQ Low", 0, 100, 50, _updateEQ);
// mid = _newKnob(320, "Middle", 0, 100, 50, _updateEQ);
// high =_newKnob(360, "High", 0, 100, 50, _updateEQ);
// lowVal = new Label(this, 276, 38, "1.00");
// midVal = new Label(this, 316, 38, "1.00");
// highVal = new Label(this, 356, 38, "1.00");
//Style.setStyle(Style.DARK);
bpmKnob = _newKnob(20, 0, "bpm", 1, 999, 60, function(e:Event):void {
bpmKnob.value = int(bpmKnob.value);
_bpm_ = bpmKnob.value;
});
bpmKnob.labelPrecision = 0;
var synthSelectLabel:Label = new Label(this, 100, 0, "synth selector");
var synthSelector:NumericStepper = new NumericStepper(this, 100, 20, function(e:Event):void {
_synth_.mode= synthSelector.value;
});
synthSelector.value = 0;
synthSelector.minimum = -1;
synthSelector.maximum = 4;
var delayPos:Point = new Point(100, 100);
var delayCheck:CheckBox = new CheckBox(this, delayPos.x, delayPos.y + 4, "delay", function(e:Event):void {
if (delayCheck.selected) {
_delayEffect_.bypass = false;
} else {
_delayEffect_.bypass = true;
}
});
delayTimeKnob = _newKnob(delayPos.x + 50, delayPos.y, "time", 0, 1, 0.5, _updateDelay);
delayFeedbackKnob = _newKnob(delayPos.x + 100, delayPos.y, "feedback", 0, 1, 0.65, _updateDelay);
delayMixKnob = _newKnob(delayPos.x + 150, delayPos.y, "mix", 0, 1, 0.65, _updateDelay);
function _updateDelay(e:Event):void {
_delayEffect_.setParameters(delayMixKnob.value, delayFeedbackKnob.value, delayTimeKnob.value);
}
var reverbPos:Point = new Point(100, 200);
var reverbCheck:CheckBox = new CheckBox(this, reverbPos.x, reverbPos.y + 4, "reverb", function(e:Event):void {
if (reverbCheck.selected) {
_reverbEffect_.bypass = false;
} else {
_reverbEffect_.bypass = true;
_reverbEffect_.prepareProcess();
}
});
reverbWetKnob = _newKnob(reverbPos.x + 50, reverbPos.y, "dry/wet", 0, 1, 0.3, _updateReverb);
reverbFeedbackKnob = _newKnob(reverbPos.x + 100, reverbPos.y, "feedback", 0, 1, 0.8, _updateReverb);
function _updateReverb(e:Event):void {
_reverbEffect_.setParameters(0.7, 0.4, reverbFeedbackKnob.value, reverbWetKnob.value);
}
parent.addChild(this);
}
public function set bpm(bpm:Number) : void {
bpmKnob.value = int(bpm);
}
//private function _newCheckbox(x:Number, y:Number, lable:String,
private function _newKnob(x:Number, y:Number, label:String, min:Number, max:Number, val:Number, onChange:Function) : Knob {
var knob:Knob = new Knob(this, x, y, label, onChange);
knob.radius = 12;
knob.labelPrecision = 2;
knob.minimum = min;
knob.maximum = max;
knob.value = val;
//knob.showValue = false;
return knob;
}
private function _updateEQ(e:Event) : void {
// var l:Number = eqt[int(low.value)],
// m:Number = eqt[int(mid.value)],
// h:Number = eqt[int(high.value)];
// //eq.setParameters(l, m, h);
// lowVal.text = l.toFixed(2);
// midVal.text = m.toFixed(2);
// highVal.text = h.toFixed(2);
}
}
// UTIL
class Util {
public static function clamp(value:Number, min:Number, max:Number):Number {
return value < min ? min : (value > max ? max : value);
}
}