| 1 |
//---------------------------------------------------------------------------------------------------- |
|---|
| 2 |
// Polyphonic chord pad synthesizer |
|---|
| 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.SiONData; |
|---|
| 10 |
import org.si.sion.sequencer.SiMMLTrack; |
|---|
| 11 |
import org.si.sion.utils.Chord; |
|---|
| 12 |
import org.si.sound.patterns.Note; |
|---|
| 13 |
import org.si.sound.patterns.Sequencer; |
|---|
| 14 |
import org.si.sound.namespaces._sound_object_internal; |
|---|
| 15 |
|
|---|
| 16 |
/** @eventType org.si.sound.events.SoundObjectEvent.ENTER_FRAME */ |
|---|
| 17 |
[Event(name="enterFrame", type="org.si.sound.events.SoundObjectEvent")] |
|---|
| 18 |
/** @eventType org.si.sound.events.SoundObjectEvent.ENTER_SEGMENT */ |
|---|
| 19 |
[Event(name="enterSegment", type="org.si.sound.events.SoundObjectEvent")] |
|---|
| 20 |
|
|---|
| 21 |
/** Chord pad provides polyphonic synthesizer controled by chord and rhythm pattern. */ |
|---|
| 22 |
public class ChordPad extends MultiTrackSoundObject |
|---|
| 23 |
{ |
|---|
| 24 |
// namespace |
|---|
| 25 |
//---------------------------------------- |
|---|
| 26 |
use namespace _sound_object_internal; |
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
// constants |
|---|
| 32 |
//---------------------------------------- |
|---|
| 33 |
/** closed voicing mode [o5c,o5e,o5g,o5b,o6e,o6g] for CM7 @see voiceMode */ |
|---|
| 34 |
static public const CLOSED:int = 0x543210; |
|---|
| 35 |
|
|---|
| 36 |
/** opened voicing mode [o5c,o5g,o5b,o6e,o6g,o6b] for CM7 @see voiceMode */ |
|---|
| 37 |
static public const OPENED:int = 0x654320; |
|---|
| 38 |
|
|---|
| 39 |
/** middle-position voicing mode [o5e,o5g,o5b,o6e,o6g,o6b] for CM7 @see voiceMode */ |
|---|
| 40 |
static public const MIDDLE:int = 0x654321; |
|---|
| 41 |
|
|---|
| 42 |
/** high-position voicing mode [o5g,o5b,o6e,o6g,o6b,o7e] for CM7 @see voiceMode */ |
|---|
| 43 |
static public const HIGH:int = 0x765432; |
|---|
| 44 |
|
|---|
| 45 |
/** opened high-position voicing mode [o5g,o6e,o6g,o6b,o7e,o7g] for CM7 @see voiceMode */ |
|---|
| 46 |
static public const OPENED_HIGH:int = 0x876542; |
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
// variables |
|---|
| 52 |
//---------------------------------------- |
|---|
| 53 |
/** @private [protected] Monophonic sequencers */ |
|---|
| 54 |
protected var _operators:Vector.<Sequencer>; |
|---|
| 55 |
|
|---|
| 56 |
/** @private [protected] Sequence data */ |
|---|
| 57 |
protected var _data:SiONData; |
|---|
| 58 |
|
|---|
| 59 |
/** @private [protected] chord instance */ |
|---|
| 60 |
protected var _chord:Chord; |
|---|
| 61 |
/** @private [protected] Default chord instance, this is used when the name is specifyed */ |
|---|
| 62 |
protected var _defaultChord:Chord = new Chord(); |
|---|
| 63 |
/** @private [protected] chord notes index */ |
|---|
| 64 |
protected var _noteIndexes:int; |
|---|
| 65 |
|
|---|
| 66 |
/** @private [protected] Note pattern */ |
|---|
| 67 |
protected var _pattern:Vector.<Note>; |
|---|
| 68 |
/** @private [protected] Current length sequence pattern. */ |
|---|
| 69 |
protected var _currentPattern:Array; |
|---|
| 70 |
/** @private [protected] Next length sequence pattern to change while playing. */ |
|---|
| 71 |
protected var _nextPattern:Array; |
|---|
| 72 |
/** @private [protected] Change bass line pattern at the head of segment. */ |
|---|
| 73 |
protected var _changePatternOnSegment:Boolean; |
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
// properties |
|---|
| 79 |
//---------------------------------------- |
|---|
| 80 |
/** list of monophonic operators */ |
|---|
| 81 |
public function get operators() : Vector.<Sequencer> { return operators; } |
|---|
| 82 |
|
|---|
| 83 |
/** Number of monophonic operators */ |
|---|
| 84 |
public function get operatorCount() : int { return operators.length; } |
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
/** root note of current chord @default 60 */ |
|---|
| 88 |
override public function get note() : int { return _chord.rootNote; } |
|---|
| 89 |
override public function set note(n:int) : void { |
|---|
| 90 |
if (_chord !== _defaultChord) _defaultChord.copyFrom(_chord); |
|---|
| 91 |
_defaultChord.rootNote = n; |
|---|
| 92 |
_chord = _defaultChord; |
|---|
| 93 |
_updateChordNotes(); |
|---|
| 94 |
} |
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
/** chord instance @default Chord("C") */ |
|---|
| 98 |
public function get chord() : Chord { return _chord; } |
|---|
| 99 |
public function set chord(c:Chord) : void { |
|---|
| 100 |
if (c == null) _chord = _defaultChord; |
|---|
| 101 |
_chord = c; |
|---|
| 102 |
_updateChordNotes(); |
|---|
| 103 |
} |
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
/** specify chord by name @default "C" */ |
|---|
| 107 |
public function get chordName() : String { return _chord.name; } |
|---|
| 108 |
public function set chordName(name:String) : void { |
|---|
| 109 |
_defaultChord.name = name; |
|---|
| 110 |
_chord = _defaultChord; |
|---|
| 111 |
_updateChordNotes(); |
|---|
| 112 |
} |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
/** voicing mode @default CLOSED */ |
|---|
| 116 |
public function get voiceMode() : int { return _noteIndexes; } |
|---|
| 117 |
public function set voiceMode(m:int) : void { |
|---|
| 118 |
_noteIndexes = m; |
|---|
| 119 |
_updateChordNotes(); |
|---|
| 120 |
} |
|---|
| 121 |
|
|---|
| 122 |
|
|---|
| 123 |
/** note length in 16th beat. */ |
|---|
| 124 |
public function get noteLength() : Number { return _operators[0].defaultLength; } |
|---|
| 125 |
public function set noteLength(l:Number) : void { |
|---|
| 126 |
if (l<0.25) l=0.25; |
|---|
| 127 |
else if (l>16) l=16; |
|---|
| 128 |
for (var i:int=0; i<operatorCount; i++) { |
|---|
| 129 |
_operators[i].defaultLength = l; |
|---|
| 130 |
_operators[i].gridStep = l * 120; |
|---|
| 131 |
} |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
|
|---|
| 135 |
/** Number Array of the sequence notes' length. If the value is 0, insert rest instead. */ |
|---|
| 136 |
public function get pattern() : Array { return _currentPattern || _nextPattern; } |
|---|
| 137 |
public function set pattern(pat:Array) : void { |
|---|
| 138 |
if (isPlaying && _changePatternOnSegment) _nextPattern = pat; |
|---|
| 139 |
else _updateSequencePattern(pat); |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 |
/** True to change bass line pattern at the head of segment. @default true */ |
|---|
| 144 |
public function get changePatternOnSegment() : Boolean { return _changePatternOnSegment; } |
|---|
| 145 |
public function set changePatternOnSegment(b:Boolean) : void { |
|---|
| 146 |
_changePatternOnSegment = b; |
|---|
| 147 |
} |
|---|
| 148 |
|
|---|
| 149 |
|
|---|
| 150 |
|
|---|
| 151 |
|
|---|
| 152 |
// constructor |
|---|
| 153 |
//---------------------------------------- |
|---|
| 154 |
/** constructor |
|---|
| 155 |
* @param chord org.si.sion.utils.Chord, chord name String or null is suitable. |
|---|
| 156 |
* @param operatorCount Number of monophonic operators (1-6). |
|---|
| 157 |
* @param voiceMode Voicing mode. |
|---|
| 158 |
* @param pattern Number Array of the sequence notes' length. If the value is 0, insert rest instead. |
|---|
| 159 |
* @param changePatternOnSegment When this is true, pattern and chord are changed at the head of next segment. |
|---|
| 160 |
*/ |
|---|
| 161 |
function ChordPad(chord:*=null, operatorCount:int=3, voiceMode:int=CLOSED, pattern:Array=null, changePatternOnSegment:Boolean=true) |
|---|
| 162 |
{ |
|---|
| 163 |
super("ChordPad"); |
|---|
| 164 |
|
|---|
| 165 |
if (operatorCount<1 || operatorCount>6) throw new Error("ChordPad; Number of operators should be in the range of 1 - 6."); |
|---|
| 166 |
|
|---|
| 167 |
_data = new SiONData(); |
|---|
| 168 |
_operators = new Vector.<Sequencer>(operatorCount); |
|---|
| 169 |
_noteIndexes = voiceMode; |
|---|
| 170 |
|
|---|
| 171 |
var defaultVelocity:int = 256 / operatorCount; |
|---|
| 172 |
if (defaultVelocity >128) defaultVelocity = 128; |
|---|
| 173 |
for (var i:int=0; i<operatorCount; i++) { |
|---|
| 174 |
_operators[i] = new Sequencer(this, _data, 60, defaultVelocity, 1); |
|---|
| 175 |
} |
|---|
| 176 |
|
|---|
| 177 |
if (chord is Chord) { |
|---|
| 178 |
_chord = chord as Chord; |
|---|
| 179 |
} else { |
|---|
| 180 |
_chord = _defaultChord; |
|---|
| 181 |
if (chord is String) { |
|---|
| 182 |
_chord.name = chord as String; |
|---|
| 183 |
} |
|---|
| 184 |
} |
|---|
| 185 |
|
|---|
| 186 |
_nextPattern = null; |
|---|
| 187 |
_pattern = new Vector.<Note>(); |
|---|
| 188 |
_changePatternOnSegment = changePatternOnSegment; |
|---|
| 189 |
|
|---|
| 190 |
_updateChordNotes(); |
|---|
| 191 |
_updateSequencePattern(pattern); |
|---|
| 192 |
} |
|---|
| 193 |
|
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 |
|
|---|
| 197 |
// configure |
|---|
| 198 |
//---------------------------------------- |
|---|
| 199 |
|
|---|
| 200 |
|
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 |
// operations |
|---|
| 204 |
//---------------------------------------- |
|---|
| 205 |
/** play drum sequence */ |
|---|
| 206 |
override public function play() : void |
|---|
| 207 |
{ |
|---|
| 208 |
var i:int, imax:int = _operators.length, opn:int; |
|---|
| 209 |
stop(); |
|---|
| 210 |
_tracks = _sequenceOn(_data, false, false); |
|---|
| 211 |
if (_tracks && _tracks.length == imax) { |
|---|
| 212 |
_synthesizer._registerTracks(_tracks); |
|---|
| 213 |
for (i=0, opn=0; i<imax; i++) { |
|---|
| 214 |
if (_tracks[opn].trackNumber > _tracks[i].trackNumber) opn = i; |
|---|
| 215 |
_operators[i].play(_tracks[i]); |
|---|
| 216 |
} |
|---|
| 217 |
_operators[opn].onEnterFrame = _onEnterFrame; |
|---|
| 218 |
_operators[opn].onEnterSegment = _onEnterSegment; |
|---|
| 219 |
} else { |
|---|
| 220 |
throw new Error("unknown error"); |
|---|
| 221 |
} |
|---|
| 222 |
} |
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 |
/** stop sequence */ |
|---|
| 226 |
override public function stop() : void |
|---|
| 227 |
{ |
|---|
| 228 |
if (_tracks) { |
|---|
| 229 |
for (var i:int=0; i<_operators.length; i++) { |
|---|
| 230 |
_operators[i].stop(); |
|---|
| 231 |
_operators[i].onEnterFrame = null; |
|---|
| 232 |
_operators[i].onEnterSegment = null; |
|---|
| 233 |
} |
|---|
| 234 |
_synthesizer._unregisterTracks(_tracks[0], _tracks.length); |
|---|
| 235 |
for each (var t:SiMMLTrack in _tracks) t.setDisposable(); |
|---|
| 236 |
_tracks = null; |
|---|
| 237 |
_sequenceOff(false); |
|---|
| 238 |
} |
|---|
| 239 |
_stopEffect(); |
|---|
| 240 |
} |
|---|
| 241 |
|
|---|
| 242 |
|
|---|
| 243 |
|
|---|
| 244 |
|
|---|
| 245 |
// internals |
|---|
| 246 |
//---------------------------------------- |
|---|
| 247 |
/** @private [protected] update chord notes */ |
|---|
| 248 |
protected function _updateChordNotes() : void |
|---|
| 249 |
{ |
|---|
| 250 |
var i:int, imax:int = _operators.length, noteIndex:int; |
|---|
| 251 |
for (i=0; i<imax; i++) { |
|---|
| 252 |
_operators[i].defaultNote = _chord.getNote((_noteIndexes>>(i<<2)) & 15); |
|---|
| 253 |
} |
|---|
| 254 |
} |
|---|
| 255 |
|
|---|
| 256 |
|
|---|
| 257 |
/** @private [protected] update sequence pattern */ |
|---|
| 258 |
protected function _updateSequencePattern(lengthPattern:Array) : void |
|---|
| 259 |
{ |
|---|
| 260 |
var i:int, imax:int; |
|---|
| 261 |
|
|---|
| 262 |
_currentPattern = lengthPattern; |
|---|
| 263 |
if (_currentPattern) { |
|---|
| 264 |
imax = _currentPattern.length; |
|---|
| 265 |
_pattern.length = imax; |
|---|
| 266 |
for (i=0; i<imax; i++) { |
|---|
| 267 |
if (_pattern[i] == null) _pattern[i] = new Note(); |
|---|
| 268 |
if (lengthPattern[i] == 0) _pattern[i].setRest(); |
|---|
| 269 |
else _pattern[i].setNote(-1, -1, _currentPattern[i]); |
|---|
| 270 |
} |
|---|
| 271 |
imax = _operators.length; |
|---|
| 272 |
for (i=0; i<imax; i++) { |
|---|
| 273 |
_operators[i].pattern = _pattern; |
|---|
| 274 |
} |
|---|
| 275 |
} else { |
|---|
| 276 |
for (i=0; i<imax; i++) { |
|---|
| 277 |
_operators[i].pattern = null; |
|---|
| 278 |
} |
|---|
| 279 |
} |
|---|
| 280 |
} |
|---|
| 281 |
|
|---|
| 282 |
|
|---|
| 283 |
/** @private [protected] on enter segment */ |
|---|
| 284 |
override protected function _onEnterSegment(seq:Sequencer) : void |
|---|
| 285 |
{ |
|---|
| 286 |
if (_nextPattern != null) { |
|---|
| 287 |
_updateSequencePattern(_nextPattern); |
|---|
| 288 |
_nextPattern = null; |
|---|
| 289 |
} |
|---|
| 290 |
super._onEnterSegment(seq); |
|---|
| 291 |
} |
|---|
| 292 |
} |
|---|
| 293 |
} |
|---|
| 294 |
|
|---|