just the arpegiattor
forked from SiON SoundObject Quartet (diff: 483)
SiON ver0.6x's new concept "SoundObject"
ActionScript3 source code
/**
* Copyright aobyrne ( http://wonderfl.net/user/aobyrne )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/bBXN
*/
// forked from keim_at_Si's SiON SoundObject Quartet
// SiON ver0.6x's new concept "SoundObject"
package {
import flash.display.*;
import flash.events.*;
import org.si.sion.*;
import org.si.sion.events.*;
import org.si.sion.effector.*;
import org.si.sound.*;
import org.si.sound.patterns.*;
import org.si.sound.synthesizers.*;
import com.bit101.components.*;
public class main extends Sprite {
// SiON SoundObjects (in org.si.sound.*)
//----------------------------------------
// SoundObjects are SiON-based software instruments which brings an operational feeling of DisplayObject.
// SoundObject は,DisplayObject のような感覚で操作を行える SiON を音源として使用したソフトウェア楽器です.
public var Ar:Arpeggiator;
public var Bs:BassSequencer;
public var Cp:ChordPad;
public var Dm:DrumMachine;
// Synthesizers (in org.si.sound.synthesizers.*)
//----------------------------------------
// Synthesizers are wrapper classes of SiONVoice.
// Synthesizer は SiONVoice のラッパークラスです.
// This provides more direct, easier controls of SiON's voice.
// より直感的で簡単な SiONVoice の操作を提供します.
public var waveTableSynth:WaveTableSynth;
public var analogSynth:AnalogSynth;
public var padVoiceLoader:PresetVoiceLoader;
// Effectors (in org.si.sion.effector.*)
//----------------------------------------
// Effectors are from SiON's effector package.
// Effector は SiON の effector package を使用します.
// SoundObject.effectors property or SiDriver.effector.slot* property has a similar operation of "DisplayObject.filters".
// SoundObject.effectors プロパティ や SiDriver.effector.slot* は DisplayObject.filters と似たような操作を行えます.
public var equaliser:SiEffectEqualiser;
public var delay:SiEffectStereoDelay;
public var chorus:SiEffectStereoChorus;
public var autopan:SiEffectAutoPan;
// SiONDriver
//----------------------------------------
// Even using SoundObject, you have to create new SiONDriver and call play() method.
// SoundObject を使用する場合でも,SiONDriver を生成して play() メソッドを呼び出す必要があります.
public var driver:SiONDriver;
private var cnsole:TextArea;
// constructor
function main() {
_generalSettings();
// create new SiON objects
//----------------------------------------
// In current version(0.60), you have to create new effectors after SiONDriver creation.
// 現バージョン(0.60)では,SiONDriver を生成後にエフェクタを生成する必要があります.
driver = new SiONDriver();
equaliser = new SiEffectEqualiser();
delay = new SiEffectStereoDelay(300, 0.25, true, 1);
chorus = new SiEffectStereoChorus(20, 0.2, 4, 20, 1);
autopan = new SiEffectAutoPan();
// SiONDriver
//----------------------------------------
// Set up general parameters (BPM, effect and so on) by SiONDriver's property.
// BPM やグローバルエフェクトなど全体に関する操作は SiONDriver のプロパティで行います.
driver.autoStop = true; // set auto stop after fade out
driver.bpm = 132; // BPM = 132
driver.effector.slot0 = [equaliser]; // The equaliser is applied to slot0 (master effector)
//driver.effector.slot1 = [delay]; // The delay effector is applied to slot1 (global effector)
//driver.effector.slot2 = [chorus]; // The chorus effector is applied to slot2 (global effector)
driver.addEventListener(SiONTrackEvent.BEAT, _onBeat); // handler for each beat
driver.addEventListener(SiONEvent.STREAM_START, _onStartStream); // handler when streaming starts
driver.addEventListener(SiONEvent.STREAM_STOP, _onStopStream); // handler when streaming stopped
new GlobalPanel(this);
// Arpeggiator
//----------------------------------------
// The Arpeggiator is a monophonic sequencer plays arpeggio pattern specifyed by int Array.
// Arpeggiator は,int 型 Array で指定したアルペジオパターンを演奏する単音シーケンサです.
Ar = new Arpeggiator();
Ar.scaleName = "o6Emp"; // scaled in E minor pentatonic on octave 6
Ar.pattern = [0,1,2,3,4,2,3,1]; // basic pattern is "egab<d>abg" in MML
Ar.noteLength = 1; // note langth = 16th
Ar.gateTime = 0.2; // gate time = 0.2
Ar.effectors = [autopan]; // apply auto-panning effector to Arpeggiator (local effector)
// These effect send level calculations porvides MIDI's effect send feelings (but not perfectly same).
// 下記のエフェクトセンドレベル計算を行う事で,ある程度 MIDI 音源のエフェクトセンドのように振舞う事ができます.
Ar.volume = 0.1; // dry volume = 0.3
Ar.effectSend1 = Ar.volume * 0.1; // effect send for slot1 = 0.3 * 0.4 = 0.12
Ar.effectSend2 = Ar.volume * 0.1; // effect send for slot2 = 0.3 * 0.4 = 0.15
// In this sample, the wave table synthesizer is applied to Arpeggiator.
// このサンプルでは,波形メモリシンセを Arpeggiator に適用しました.
// The wave table synthesizer provides simple additive synthesis by SiON's "wavecolor". Or you can edit wave shape directly.
// 波形メモリシンセは SiON の "wavecolor" を指定するシンプルな加算合成方式シンセサイザです. 波形を直接編集することも出来ます.
waveTableSynth = new WaveTableSynth();
waveTableSynth.color = 0x1203acff; // wavecolor value
waveTableSynth.releaseTime = 0.2; // release time
Ar.synthesizer = waveTableSynth; // apply synthesizer
new ArPanel(this, Ar);
var hbox:HBox=new HBox(this,0,300);
var apl:uint = Ar.pattern.length;
var ns:NumericStepper;
for(var i:uint ; i<apl;i++){
ns = new NumericStepper(hbox,0,0,onNumericStepper);
ns.name=i.toString();
ns.width=50;
ns.minimum=-7;
ns.maximum=7;
ns.value=Ar.pattern[i];
}
cnsole = new TextArea(this,0,320);
play();
}
private function onNumericStepper(ev:Event):void{
var ns:NumericStepper = ev.target as NumericStepper;
Ar.pattern[ns.name]=ns.value;
Ar.scaleIndex = Ar.scaleIndex;
//Ar.gateTime = Ar.gateTime;
//Ar.noteLength = Ar.noteLength;
//Ar.portament = Ar.portament;
}
public function play() : void {
// Even using SoundObject, you have to call SiONDriver.play() method to start SiON's sound streaming.
// SoundObject を使用する場合であっても,SiON で音を合成するのために SiONDriver.play() メソッドを呼び出す必要があります.
// In this sample, effectors are specifyed before calling play() method,
// このサンプルでは,play() メソッド呼び出しの前にエフェクタを設定しているため,
// so we pass false in the 2nd argument to avoid initializing effector inside.
// 第二引数で false を渡して内部でエフェクタの初期化を行わないようにしています.
driver.play(null, false);
// The SoundObject starts playing sound by play() method and stop it by stop() method.
// SoundObject は,play() メソッドで演奏を開始し,stop() メソッドで演奏を停止します.
Ar.play();
//Bs.play();
//Cp.play();
//Dm.play();
}
// started
protected function _onStartStream(e:SiONEvent) : void {
// switch the play button's label
GlobalPanel.changePlayButtonLabel("stop");
}
// stopped
protected function _onStopStream(e:SiONEvent) : void {
// In current version (0.60), you have to stop all SoundObjects explicitly when the SiON's stream stopped.
// 現バージョン(0.60)では,SiON のストリーミング終了時に,明示的に SoundObject の演奏を止める必要があります.
// In the future version, it may stop automatically. Sorry for the inconvenience.
// 将来のバージョンでは,自動的に演奏を止めるようになる予定です.ご面倒おかけしてすいません.
Ar.stop();
//Bs.stop();
//Cp.stop();
//Dm.stop();
// switch the play button's label
GlobalPanel.changePlayButtonLabel("play");
}
// So, what shall we do here ?
protected function _onBeat(e:SiONTrackEvent) : void {
cnsole.text = e.toString()+Math.random();
}
// So, what shall we do here ???
protected function _onKeyDown(e:KeyboardEvent) : void {
switch (String.fromCharCode(e.charCode)) {
case 'c': _updateChord("o6Emp", "o3CM7", "o5CM7"); break;
case 'd': _updateChord("o6Emp", "o3D9", "o5D9"); break;
case 'e': _updateChord("o6Emp", "o3Em9", "o5Em9"); break;
case 'f': _updateChord("o6F+b", "o3F+7", "o5F+7"); break;
case 'g': _updateChord("o6Emp", "o3GM7", "o5GM7"); break;
case 'a': _updateChord("o6Emp", "o3Am9", "o5CM7"); break;
case 'b': _updateChord("o5Bb", "o2B7", "o4B7"); break;
}
}
// Specify chord and scale name for each instrument except for DrumMachine.
// DrumMachine 以外の各楽器にコード名/スケール名を指定します.
public function _updateChord(ArScale:String, BrChord:String, CpChord:String) : void {
cnsole.text = ArScale;
Ar.scaleName = ArScale;
Ar.scaleIndex = Ar.scaleIndex;
//Bs.chordName = BrChord;
//Cp.chordName = CpChord;
}
// General settings
private function _generalSettings() : void {
// color setting
Style.BACKGROUND = 0x808080;
Style.BUTTON_FACE = 0x606060;
Style.LABEL_TEXT = 0xaaaaaa;
Style.DROPSHADOW = 0;
Style.PANEL = 0x303030;
Style.PROGRESS_BAR = 0x404040;
Style_POINTER = 0x8080ff;
// draw background
var shape:Shape = new Shape();
shape.graphics.beginFill(0);
shape.graphics.drawRect(0,0,465,465);
shape.graphics.endFill();
addChild(shape);
// add event handlers
stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
}
}
}
//----------------------------------------
// Hints for advanced usage
//----------------------------------------
// Sorry, there are extremely few comments in the following code.
// すいません.以降,本領発揮でほとんどコメントがありません.
// You may search some hints with the keywords of "Ar.", "Bs.", "Cp." and "Dm.".
// "Ar.", "Bs.", "Cp.", "Dm." の各文字列で以降を検索すると,SoundObject のプロパティの使い方のヒントになるかもしれません.
// And if you are interested in customized Component, please search by "custom".
// また,もしカスタマイズされた Component に興味がある場合,"custom" で検索してみて下さい.
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import org.si.sion.*;
import org.si.sion.utils.*;
import org.si.sion.events.*;
import org.si.sion.effector.*;
import org.si.sound.*;
import org.si.sound.events.*;
import org.si.sound.patterns.*;
import org.si.sound.synthesizers.*;
import com.bit101.components.*;
var Style_POINTER:uint;
class GlobalPanel extends Panel {
static private var me:GlobalPanel;
public var start:PushButton, vol:Knob_, tempo:Knob_, low:Knob_, mid:Knob_, high:Knob_, lowVal:Label, midVal:Label, highVal:Label;
private var _driver:SiONDriver, _eq:SiEffectEqualiser, _updateChord:Function, _play:Function;
private var _table:Vector.<Number> = new Vector.<Number>(101);
function GlobalPanel(_main:main) {
super(_main, 2, 25);
setSize(461, 51);
for (var i:int=0; i<101; i++) _table[i] = Math.pow(2, i*0.04-2);
_driver = _main.driver;
_eq = _driver.effector.getEffectorList(0)[0] as SiEffectEqualiser;
_updateChord = _main._updateChord;
_play = _main.play;
var tl:Label = new Label(content, 4, -4, "SiON SoundObject Quartet");
tl.scaleX = tl.scaleY = 2;
start = new PushButton(content, 4, 30, "stop", function(e:Event) : void {
if (start.label == "stop") _driver.fadeOut(3);
else _play();
});
start.setSize(38, 14);
new PushButton(content, 50, 30, "GM7", function(e:Event):void { _updateChord("o6Emp", "o3GM7", "o5GM7");}).setSize(33, 14);
new PushButton(content, 85, 30, "Am9", function(e:Event):void { _updateChord("o6Emp", "o3Am9", "o5CM7");}).setSize(33, 14);
new PushButton(content, 120, 30, "CM7", function(e:Event):void { _updateChord("o6Emp", "o3CM7", "o5CM7");}).setSize(33, 14);
new PushButton(content, 155, 30, "D9", function(e:Event):void { _updateChord("o6Emp", "o3D9", "o5D9"); }).setSize(33, 14);
new PushButton(content, 190, 30, "Em9", function(e:Event):void { _updateChord("o6Emp", "o3Em9", "o5Em9");}).setSize(33, 14);
vol = new Knob_(content, 242, -3, "volume", function(e:Event):void { _driver.volume = vol.value; });
vol.value = _driver.volume;
tempo = new Knob_(content, 278, -3, "BPM", function(e:Event):void { _driver.bpm = tempo.value; });
tempo.setSize(20, 20);
tempo.minimum = 70;
tempo.maximum = 200;
tempo.value = _driver.bpm;
new Label(content, 350, 35, "3 Band Equaliser");
low = _eqKnob(370, "low");
mid = _eqKnob(400, "middle");
high = _eqKnob(430, "high");
new Label(content, 310, -2, "low");
new Label(content, 310, 11, "middle");
new Label(content, 310, 24, "high");
lowVal = new Label(content, 340, -2, "1.00");
midVal = new Label(content, 340, 11, "1.00");
highVal = new Label(content, 340, 24, "1.00");
me = this;
}
protected function _eqKnob(x:Number, label:String) : Knob_ {
var knob:Knob_ = new Knob_(content, x, -3, label, _updateEQ);
knob.minimum = 0;
knob.maximum = 100;
knob.value = 50;
knob.valueLabel.visible = false;
return knob;
}
protected function _updateEQ(e:Event) : void {
var l:Number = _table[int(low.value)], m:Number = _table[int(mid.value)], h:Number = _table[int(high.value)];
_eq.setParameters(l, m, h);
lowVal.text = l.toFixed(2);
midVal.text = m.toFixed(2);
highVal.text = h.toFixed(2);
}
static public function changePlayButtonLabel(label:String) : void {
me.start.label = label;
}
}
class SoundObjectPanel extends Panel {
private var _soundObject:SoundObject, es1:VSlider, es2:VSlider, mute:CheckBox;
function SoundObjectPanel(parent:DisplayObjectContainer, x:Number, y:Number, title:String, soundObject:SoundObject, fadeButton:Boolean = true) {
super(parent, x, y);
_soundObject = soundObject;
setSize(230, 180);
var tl:Label = new Label(content, 4, -4, title);
tl.scaleX = tl.scaleY = 2;
mute = new CheckBox(content, 7, 32, "mute", function(e:Event):void{
_soundObject.mute = mute.selected;
});
var vol:Knob_ = new Knob_(content, 10, 30, "", function(e:Event):void{
_soundObject.volume = e.target.value;
_soundObject.effectSend1 = _soundObject.volume * es1.value;
_soundObject.effectSend2 = _soundObject.volume * es2.value;
});
vol.value = _soundObject.volume;
es1 = new VSlider(content, 8, 90, function(e:Event):void{ _soundObject.effectSend1 = _soundObject.volume * es1.value; });
es2 = new VSlider(content, 24, 90, function(e:Event):void{ _soundObject.effectSend2 = _soundObject.volume * es2.value; });
new Label(content, 6, 162, "Del");
new Label(content, 22, 162, "Cho");
es1.setSize(12,72);
es2.setSize(12,72);
es1.tick = 0.0078125;
es2.tick = 0.0078125;
es1.setSliderParams(0,1,_soundObject.effectSend1/_soundObject.volume);
es2.setSliderParams(0,1,_soundObject.effectSend2/_soundObject.volume);
if (fadeButton) {
new PushButton(content, 141, 4, "fadeIn", function(e:Event):void{ _soundObject.fadeIn(3); }).setSize(40,14);
new PushButton(content, 183, 4, "fadeOut", function(e:Event):void{ _soundObject.fadeOut(4); }).setSize(43,14);
}
}
}
class ArPanel extends SoundObjectPanel {
public var Ar:Arpeggiator, waveTableSynth:WaveTableSynth, manual:CheckBox;
public var pad:ControlPad, si:Label, gt:Label, nl:Label, po:Label, wc:Label;
public var col8th:VSlider, col5th:VSlider, col4th:VSlider, ws:RotarySelector;
function ArPanel(parent:DisplayObjectContainer, Ar:Arpeggiator) {
super(parent, 2, 78, "Arpeggiator", Ar, false);
this.Ar = Ar;
this.waveTableSynth = Ar.synthesizer as WaveTableSynth;
new Label(content, 50, 30, "wavecolor :");
wc = new Label(content, 50, 43, "");
col4th = new VSlider(content, 52, 95, onColorChanged);
col5th = new VSlider(content, 67, 95, onColorChanged);
col8th = new VSlider(content, 82, 95, onColorChanged);
col4th.setSliderParams(0, 1, 0.2);
col5th.setSliderParams(0, 1, 0.4);
col8th.setSliderParams(0, 1, 0.6);
col4th.setSize(12, 80);
col5th.setSize(12, 80);
col8th.setSize(12, 80);
col4th.tick = 0.0078125;
col5th.tick = 0.0078125;
col8th.tick = 0.0078125;
ws = new RotarySelector(content, 65, 72, "", onColorChanged);
ws.numChoices = 4;
ws.choice = 3;
ws.setSize(16, 16);
onColorChanged(null);
new Label(content, 110, 120, "scaleIndex property");
new Label(content, 110, 134, "gateTime property");
new Label(content, 110, 148, "noteLength property");
new Label(content, 110, 162, "protament property");
si = new Label(content, 200, 120, ": "+Ar.scaleIndex.toString());
gt = new Label(content, 200, 134, ": "+Ar.gateTime.toFixed(2));
nl = new Label(content, 200, 148, ": "+Ar.noteLength.toFixed(1));
po = new Label(content, 200, 162, ": "+Ar.portament.toString());
manual = new CheckBox(content, 120, 6, "manual control", function(e:Event) : void {
if (manual.selected) {
pad.onStart = Ar.play;
pad.onStop = Ar.stop;
Ar.stop();
} else {
pad.onStart = null;
pad.onStop = null;
Ar.play();
}
});
manual.selected = false;
pad = new ControlPad(content, 120, 22, 100, 100);
pad.onChange = function() : void {
Ar.scaleIndex = pad.rx * 20 - 10;
Ar.gateTime = pad.ry;
Ar.noteLength = [2,1,2,1][int(pad.ry * 3.9)];
Ar.portament = (pad.ry == 0 || pad.ry == 1) ? 5 : 0;
gt.text = ": "+Ar.gateTime.toFixed(2);
po.text = ": "+Ar.portament.toString();
si.text = ": "+Ar.scaleIndex.toString();
nl.text = ": "+Ar.noteLength.toFixed(1);
}
pad.setPointer(0.5, Ar.gateTime);
}
protected function onColorChanged(e:Event) : void {
var c8:Number = col8th.value, c5:Number = col5th.value, c4:Number = col4th.value, col:uint = 0;
col |= (c8<0.75) ? 15 : int((1 - c8) * 60);
col |= ((c8<0.50) ? (c8*30) : (c8<0.75) ? 15 : int((1 - c8) * 44 + 4))<<4;
col |= ((c8<0.25) ? 0 : (c8<0.75) ? int((c8-0.25)*30) : int((1 - c8) * 32 + 7))<<12;
col |= ((c8<0.50) ? 0 : int((c8-0.5)*30))<<24;
col |= ((c5<0.50) ? int(c5*30) : 15)<<8;
col |= ((c5<0.50) ? 0 : int((c5-0.5)*30))<<20;
col |= (c4*15)<<16;
col |= [0,3,5,1][ws.choice]<<28;
waveTableSynth.color = col;
wc.text = "0x"+("0000000"+col.toString(16)).substr(-8,8);
}
}
// custom component "Knob_" is a small knob.
class Knob_ extends Component {
public var knob:Sprite, label:Label, valueLabel:Label, _startY:Number, rad:Number=10, value:Number=0;
public var minimum:Number=0, maximum:Number=1;
function Knob_(parent:DisplayObjectContainer, x:Number, y:Number, labelText:String, onChange:Function) {
super(parent, x, y);
addChild(knob = new Sprite());
knob.filters = [getShadow(1)];
knob.buttonMode = true;
knob.useHandCursor = true;
knob.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
label = new Label(this, 0, 0, labelText);
label.autoSize = true;
label.draw();
label.x = rad - label.width / 2;
valueLabel = new Label(this);
valueLabel.autoSize = true;
if (onChange != null) addEventListener(Event.CHANGE, onChange);
}
override public function draw() : void {
knob.graphics.clear();
knob.graphics.beginFill(Style.BACKGROUND);
knob.graphics.drawCircle(0, 0, rad);
knob.graphics.endFill();
knob.graphics.beginFill(Style.BUTTON_FACE);
knob.graphics.drawCircle(0, 0, rad - 2);
knob.graphics.endFill();
knob.graphics.beginFill(Style.BACKGROUND);
knob.graphics.drawRect(rad*0.5, -rad*0.1, rad*0.6, rad*0.2);
knob.graphics.endFill();
knob.x = rad;
knob.y = rad + 20;
knob.rotation = -225 + (value - minimum)/(maximum - minimum) * 270;
valueLabel.text = value.toFixed(2);
valueLabel.draw();
valueLabel.x = rad - valueLabel.width * 0.5;
valueLabel.y = rad * 2 + 20;
}
protected function onMouseDown(event:MouseEvent) : void {
_startY = mouseY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
protected function onMouseMove(event:MouseEvent):void {
var oldValue:Number=value, diff:Number=_startY-mouseY,
range:Number=maximum-minimum, percent:Number=range/200;
value += percent * diff;
if (value < minimum) value = minimum;
else if (value > maximum) value = maximum;
if (value != oldValue) {
invalidate();
dispatchEvent(new Event(Event.CHANGE));
}
_startY = mouseY;
}
protected function onMouseUp(event:MouseEvent) : void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
// custom component "ControlPad" shown in Arpeggiator panel
class ControlPad extends Component {
public var back:Sprite, pointer:Sprite, rx:Number=0.5, ry:Number=0.5, w:Number, h:Number;
public var onStart:Function=null, onChange:Function, onStop:Function=null;
function ControlPad(parent:DisplayObjectContainer, x:Number, y:Number, width:Number, height:Number) {
super(parent, x, y);
addChild(back = new Sprite());
back.filters = [getShadow(2, true)];
back.addEventListener(MouseEvent.MOUSE_DOWN, onBackClick);
addChild(pointer = new Sprite());
pointer.filters = [getShadow(1)];
pointer.buttonMode = true;
pointer.useHandCursor = true;
pointer.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
setSize(width, height);
w = width - 12;
h = height - 12;
}
override public function draw() : void {
super.draw();
back.graphics.clear();
back.graphics.beginFill(Style.BACKGROUND);
back.graphics.drawRect(0, 0, width, height);
back.graphics.endFill();
pointer.graphics.beginFill(Style_POINTER, 0.5);
pointer.graphics.lineStyle(2,Style.BUTTON_FACE);
pointer.graphics.drawCircle(5, 5, 5);
pointer.graphics.endFill();
updatePointerPosition();
}
protected function onDrag(e:Event) : void {
stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onSlide);
pointer.startDrag(false, new Rectangle(0, 0, w, h));
if (onStart != null) onStart();
}
protected function onDrop(e:MouseEvent) : void {
stage.removeEventListener(MouseEvent.MOUSE_UP, onDrop);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onSlide);
stopDrag();
if (onStop != null) onStop();
}
protected function onSlide(e:MouseEvent) : void {
var _rx:Number = rx, _ry:Number = ry;
rx = pointer.x / w;
ry = pointer.y / h;
rx = (rx<0) ? 0 : (rx>1) ? 1 : rx;
ry = (ry<0) ? 0 : (ry>1) ? 1 : ry;
if (_rx != rx || _ry != ry) onChange();
}
protected function onBackClick(e:MouseEvent) : void {
pointer.x = mouseX - 6;
pointer.y = mouseY - 6;
onSlide(e);
onDrag(null);
}
public function setPointer(x:Number, y:Number) : void {
rx = x;
ry = y;
updatePointerPosition();
}
public function updatePointerPosition() : void {
pointer.x = rx * w;
pointer.y = ry * h;
}
}