| 1 |
//---------------------------------------------------------------------------------------------------- |
|---|
| 2 |
// Arpeggiator class |
|---|
| 3 |
// Copyright (c) 2009 keim All rights reserved. |
|---|
| 4 |
// Distributed under BSD-style license (see org.si.license.txt). |
|---|
| 5 |
//---------------------------------------------------------------------------------------------------- |
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
package org.si.sound { |
|---|
| 9 |
import org.si.sion.*; |
|---|
| 10 |
import org.si.sion.utils.Scale; |
|---|
| 11 |
import org.si.sound.patterns.Note; |
|---|
| 12 |
import org.si.sound.patterns.Sequencer; |
|---|
| 13 |
import org.si.sound.namespaces._sound_object_internal; |
|---|
| 14 |
|
|---|
| 15 |
/** @eventType org.si.sound.events.SoundObjectEvent.ENTER_FRAME */ |
|---|
| 16 |
[Event(name="enterFrame", type="org.si.sound.events.SoundObjectEvent")] |
|---|
| 17 |
/** @eventType org.si.sound.events.SoundObjectEvent.ENTER_SEGMENT */ |
|---|
| 18 |
[Event(name="enterSegment", type="org.si.sound.events.SoundObjectEvent")] |
|---|
| 19 |
|
|---|
| 20 |
/** Arpeggiator provides monophonic arpeggio pattern sound. */ |
|---|
| 21 |
public class Arpeggiator extends PatternSequencer |
|---|
| 22 |
{ |
|---|
| 23 |
// namespace |
|---|
| 24 |
//---------------------------------------- |
|---|
| 25 |
use namespace _sound_object_internal; |
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
// variables |
|---|
| 31 |
//---------------------------------------- |
|---|
| 32 |
/** @private [protected] Table of notes on scale */ |
|---|
| 33 |
protected var _scale:Scale; |
|---|
| 34 |
/** @private [protected] scale index */ |
|---|
| 35 |
protected var _scaleIndex:int; |
|---|
| 36 |
|
|---|
| 37 |
/** @private [protected] Current arpeggio pattern. */ |
|---|
| 38 |
protected var _currentPattern:Array; |
|---|
| 39 |
/** @private [protected] Next arpeggio pattern to change while playing. */ |
|---|
| 40 |
protected var _nextPattern:Array; |
|---|
| 41 |
/** @private [protected] Change bass line pattern at the head of segment. */ |
|---|
| 42 |
protected var _changePatternOnSegment:Boolean; |
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
// properties |
|---|
| 48 |
//---------------------------------------- |
|---|
| 49 |
/** change root note of the scale */ |
|---|
| 50 |
override public function get note() : int { |
|---|
| 51 |
return _scale.rootNote; |
|---|
| 52 |
} |
|---|
| 53 |
override public function set note(n:int) : void { |
|---|
| 54 |
_scale.rootNote = n; |
|---|
| 55 |
_scaleIndexUpdated(); |
|---|
| 56 |
} |
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
/** scale instance */ |
|---|
| 60 |
public function get scale() : Scale { return _scale; } |
|---|
| 61 |
public function set scale(s:Scale) : void { |
|---|
| 62 |
_scale.copyFrom(s); |
|---|
| 63 |
_scaleIndexUpdated(); |
|---|
| 64 |
} |
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
/** specify scale by name */ |
|---|
| 68 |
public function get scaleName() : String { return _scale.name; } |
|---|
| 69 |
public function set scaleName(str:String) : void { |
|---|
| 70 |
_scale.name = str; |
|---|
| 71 |
_scaleIndexUpdated(); |
|---|
| 72 |
} |
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
/** index on scale */ |
|---|
| 76 |
public function get scaleIndex() : int { return _scaleIndex; } |
|---|
| 77 |
public function set scaleIndex(i:int) : void { |
|---|
| 78 |
_scaleIndex = i; |
|---|
| 79 |
_note = _scale.getNote(i); |
|---|
| 80 |
_scaleIndexUpdated(); |
|---|
| 81 |
} |
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
/** note length in 16th beat. */ |
|---|
| 85 |
public function get noteLength() : Number { return _sequencer.defaultLength; } |
|---|
| 86 |
public function set noteLength(l:Number) : void { |
|---|
| 87 |
if (l<0.25) l=0.25; |
|---|
| 88 |
else if (l>16) l=16; |
|---|
| 89 |
_sequencer.defaultLength = l; |
|---|
| 90 |
_sequencer.gridStep = l * 120; |
|---|
| 91 |
} |
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
/** Note index array of the arpeggio pattern. If the index is out of range, insert rest instead. */ |
|---|
| 95 |
public function get pattern() : Array { return _currentPattern || _nextPattern; } |
|---|
| 96 |
public function set pattern(pat:Array) : void { |
|---|
| 97 |
if (isPlaying && _changePatternOnSegment) _nextPattern = pat; |
|---|
| 98 |
else _updateArpeggioPattern(pat); |
|---|
| 99 |
} |
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 |
/** True to change bass line pattern at the head of segment. @default true */ |
|---|
| 103 |
public function get changePatternOnNextSegment() : Boolean { return _changePatternOnSegment; } |
|---|
| 104 |
public function set changePatternOnNextSegment(b:Boolean) : void { |
|---|
| 105 |
_changePatternOnSegment = b; |
|---|
| 106 |
} |
|---|
| 107 |
|
|---|
| 108 |
|
|---|
| 109 |
/** [NOT RECOMENDED] Only for the compatibility before version 0.58, the getTime property can be used instead of this property. */ |
|---|
| 110 |
public function get noteQuantize() : int { return gateTime * 8; } |
|---|
| 111 |
public function set noteQuantize(q:int) : void { gateTime = q * 0.125; } |
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
|
|---|
| 116 |
// constructor |
|---|
| 117 |
//---------------------------------------- |
|---|
| 118 |
/** constructor |
|---|
| 119 |
* @param scale Arpaggio scale, org.si.sion.utils.Scale instance, scale name String or null is suitable. |
|---|
| 120 |
* @param noteLength length for each note |
|---|
| 121 |
* @param pattern Note index array of the arpeggio pattern. If the index is out of range, insert rest instead. |
|---|
| 122 |
* @see org.si.sion.utils.Scale |
|---|
| 123 |
*/ |
|---|
| 124 |
function Arpeggiator(scale:*=null, noteLength:Number=2, pattern:Array=null) |
|---|
| 125 |
{ |
|---|
| 126 |
super(); |
|---|
| 127 |
name = "Arpeggiator"; |
|---|
| 128 |
|
|---|
| 129 |
_scale = new Scale(); |
|---|
| 130 |
if (scale is Scale) _scale.copyFrom(scale as Scale); |
|---|
| 131 |
else if (scale is String) _scale.name = scale as String; |
|---|
| 132 |
|
|---|
| 133 |
_nextPattern = null; |
|---|
| 134 |
_sequencer.defaultLength = 1; |
|---|
| 135 |
_sequencer.pattern = new Vector.<Note>(); |
|---|
| 136 |
_sequencer.onEnterFrame = _onEnterFrame; |
|---|
| 137 |
_sequencer.onEnterSegment = _onEnterSegment; |
|---|
| 138 |
|
|---|
| 139 |
_updateArpeggioPattern(pattern); |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 |
// operations |
|---|
| 146 |
//---------------------------------------- |
|---|
| 147 |
/** @private */ |
|---|
| 148 |
override public function reset() : void |
|---|
| 149 |
{ |
|---|
| 150 |
super.reset(); |
|---|
| 151 |
_scaleIndex = 0; |
|---|
| 152 |
} |
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 |
|
|---|
| 156 |
|
|---|
| 157 |
// internal |
|---|
| 158 |
//---------------------------------------- |
|---|
| 159 |
/** @private [protected] call this after the update of note or scale index */ |
|---|
| 160 |
protected function _scaleIndexUpdated() : void { |
|---|
| 161 |
var i:int, imax:int = _sequencer.pattern.length; |
|---|
| 162 |
for (i=0; i<imax; i++) { |
|---|
| 163 |
_sequencer.pattern[i].note = _scale.getNote(_currentPattern[i] + _scaleIndex); |
|---|
| 164 |
} |
|---|
| 165 |
} |
|---|
| 166 |
|
|---|
| 167 |
|
|---|
| 168 |
// set arpeggio pattern |
|---|
| 169 |
private function _updateArpeggioPattern(indexPattern:Array) : void { |
|---|
| 170 |
var i:int, imax:int, note:int, pattern:Vector.<Note>; |
|---|
| 171 |
|
|---|
| 172 |
_currentPattern = indexPattern; |
|---|
| 173 |
if (_currentPattern) { |
|---|
| 174 |
imax = _currentPattern.length; |
|---|
| 175 |
_sequencer.pattern.length = imax; |
|---|
| 176 |
_sequencer.segmentFrameCount = imax; |
|---|
| 177 |
pattern = _sequencer.pattern; |
|---|
| 178 |
for (i=0; i<imax; i++) { |
|---|
| 179 |
if (pattern[i] == null) pattern[i] = new Note(); |
|---|
| 180 |
note = _scale.getNote(_currentPattern[i] + _scaleIndex); |
|---|
| 181 |
if (note >= 0 && note < 128) { |
|---|
| 182 |
pattern[i].note = note; |
|---|
| 183 |
pattern[i].velocity = -1; |
|---|
| 184 |
pattern[i].length = Number.NaN; |
|---|
| 185 |
} else { |
|---|
| 186 |
pattern[i].setRest(); |
|---|
| 187 |
} |
|---|
| 188 |
} |
|---|
| 189 |
} else { |
|---|
| 190 |
_sequencer.pattern.length = 0; |
|---|
| 191 |
_sequencer.segmentFrameCount = 16; |
|---|
| 192 |
} |
|---|
| 193 |
} |
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 |
/** @private [protected] handler on enter segment */ |
|---|
| 197 |
override protected function _onEnterSegment(seq:Sequencer) : void |
|---|
| 198 |
{ |
|---|
| 199 |
if (_nextPattern != null) { |
|---|
| 200 |
_updateArpeggioPattern(_nextPattern); |
|---|
| 201 |
_nextPattern = null; |
|---|
| 202 |
} |
|---|
| 203 |
super._onEnterSegment(seq); |
|---|
| 204 |
} |
|---|
| 205 |
} |
|---|
| 206 |
} |
|---|
| 207 |
|
|---|