Tone.js/Tone/control/CtrlPattern.js

268 lines
6.8 KiB
JavaScript

import Tone from "../core/Tone";
/**
* @class Generate patterns from an array of values.
* Has a number of arpeggiation and randomized
* selection patterns.
* <ul>
* <li>"up" - cycles upward</li>
* <li>"down" - cycles downward</li>
* <li>"upDown" - up then and down</li>
* <li>"downUp" - cycles down then and up</li>
* <li>"alternateUp" - jump up two and down one</li>
* <li>"alternateDown" - jump down two and up one</li>
* <li>"random" - randomly select an index</li>
* <li>"randomWalk" - randomly moves one index away from the current position</li>
* <li>"randomOnce" - randomly select an index without repeating until all values have been chosen.</li>
* </ul>
* @param {Array} values An array of options to choose from.
* @param {Tone.CtrlPattern.Type=} type The name of the pattern.
* @extends {Tone}
*/
Tone.CtrlPattern = function(){
var options = Tone.defaults(arguments, ["values", "type"], Tone.CtrlPattern);
Tone.call(this);
/**
* The array of values to arpeggiate over
* @type {Array}
*/
this.values = options.values;
/**
* The current position in the values array
* @type {Number}
*/
this.index = 0;
/**
* The type placeholder
* @type {Tone.CtrlPattern.Type}
* @private
*/
this._type = null;
/**
* Shuffled values for the RandomOnce type
* @type {Array}
* @private
*/
this._shuffled = null;
/**
* The direction of the movement
* @type {String}
* @private
*/
this._direction = null;
this.type = options.type;
};
Tone.extend(Tone.CtrlPattern);
/**
* The Control Patterns
* @type {Object}
* @static
*/
Tone.CtrlPattern.Type = {
Up : "up",
Down : "down",
UpDown : "upDown",
DownUp : "downUp",
AlternateUp : "alternateUp",
AlternateDown : "alternateDown",
Random : "random",
RandomWalk : "randomWalk",
RandomOnce : "randomOnce",
};
/**
* The default values.
* @type {Object}
*/
Tone.CtrlPattern.defaults = {
"type" : Tone.CtrlPattern.Type.Up,
"values" : []
};
/**
* The value at the current index of the pattern.
* @readOnly
* @memberOf Tone.CtrlPattern#
* @type {*}
* @name value
*/
Object.defineProperty(Tone.CtrlPattern.prototype, "value", {
get : function(){
//some safeguards
if (this.values.length === 0){
return undefined;
} else if (this.values.length === 1){
return this.values[0];
}
this.index = Math.min(this.index, this.values.length - 1);
var val = this.values[this.index];
if (this.type === Tone.CtrlPattern.Type.RandomOnce){
if (this.values.length !== this._shuffled.length){
this._shuffleValues();
}
val = this.values[this._shuffled[this.index]];
}
return val;
}
});
/**
* The pattern used to select the next
* item from the values array
* @memberOf Tone.CtrlPattern#
* @type {Tone.CtrlPattern.Type}
* @name type
*/
Object.defineProperty(Tone.CtrlPattern.prototype, "type", {
get : function(){
return this._type;
},
set : function(type){
this._type = type;
this._shuffled = null;
//the first index
if (this._type === Tone.CtrlPattern.Type.Up ||
this._type === Tone.CtrlPattern.Type.UpDown ||
this._type === Tone.CtrlPattern.Type.RandomOnce ||
this._type === Tone.CtrlPattern.Type.AlternateUp){
this.index = 0;
} else if (this._type === Tone.CtrlPattern.Type.Down ||
this._type === Tone.CtrlPattern.Type.DownUp ||
this._type === Tone.CtrlPattern.Type.AlternateDown){
this.index = this.values.length - 1;
}
//the direction
if (this._type === Tone.CtrlPattern.Type.UpDown ||
this._type === Tone.CtrlPattern.Type.AlternateUp){
this._direction = Tone.CtrlPattern.Type.Up;
} else if (this._type === Tone.CtrlPattern.Type.DownUp ||
this._type === Tone.CtrlPattern.Type.AlternateDown){
this._direction = Tone.CtrlPattern.Type.Down;
}
//randoms
if (this._type === Tone.CtrlPattern.Type.RandomOnce){
this._shuffleValues();
} else if (this._type === Tone.CtrlPattern.Type.Random){
this.index = Math.floor(Math.random() * this.values.length);
}
}
});
/**
* Return the next value given the current position
* and pattern.
* @return {*} The next value
*/
Tone.CtrlPattern.prototype.next = function(){
var type = this.type;
//choose the next index
if (type === Tone.CtrlPattern.Type.Up){
this.index++;
if (this.index >= this.values.length){
this.index = 0;
}
} else if (type === Tone.CtrlPattern.Type.Down){
this.index--;
if (this.index < 0){
this.index = this.values.length - 1;
}
} else if (type === Tone.CtrlPattern.Type.UpDown ||
type === Tone.CtrlPattern.Type.DownUp){
if (this._direction === Tone.CtrlPattern.Type.Up){
this.index++;
} else {
this.index--;
}
if (this.index < 0){
this.index = 1;
this._direction = Tone.CtrlPattern.Type.Up;
} else if (this.index >= this.values.length){
this.index = this.values.length - 2;
this._direction = Tone.CtrlPattern.Type.Down;
}
} else if (type === Tone.CtrlPattern.Type.Random){
this.index = Math.floor(Math.random() * this.values.length);
} else if (type === Tone.CtrlPattern.Type.RandomWalk){
if (Math.random() < 0.5){
this.index--;
this.index = Math.max(this.index, 0);
} else {
this.index++;
this.index = Math.min(this.index, this.values.length - 1);
}
} else if (type === Tone.CtrlPattern.Type.RandomOnce){
this.index++;
if (this.index >= this.values.length){
this.index = 0;
//reshuffle the values for next time
this._shuffleValues();
}
} else if (type === Tone.CtrlPattern.Type.AlternateUp){
if (this._direction === Tone.CtrlPattern.Type.Up){
this.index += 2;
this._direction = Tone.CtrlPattern.Type.Down;
} else {
this.index -= 1;
this._direction = Tone.CtrlPattern.Type.Up;
}
if (this.index >= this.values.length){
this.index = 0;
this._direction = Tone.CtrlPattern.Type.Up;
}
} else if (type === Tone.CtrlPattern.Type.AlternateDown){
if (this._direction === Tone.CtrlPattern.Type.Up){
this.index += 1;
this._direction = Tone.CtrlPattern.Type.Down;
} else {
this.index -= 2;
this._direction = Tone.CtrlPattern.Type.Up;
}
if (this.index < 0){
this.index = this.values.length - 1;
this._direction = Tone.CtrlPattern.Type.Down;
}
}
return this.value;
};
/**
* Shuffles the values and places the results into the _shuffled
* @private
*/
Tone.CtrlPattern.prototype._shuffleValues = function(){
var copy = [];
this._shuffled = [];
for (var i = 0; i < this.values.length; i++){
copy[i] = i;
}
while (copy.length > 0){
var randVal = copy.splice(Math.floor(copy.length * Math.random()), 1);
this._shuffled.push(randVal[0]);
}
};
/**
* Clean up
* @returns {Tone.CtrlPattern} this
*/
Tone.CtrlPattern.prototype.dispose = function(){
this._shuffled = null;
this.values = null;
};
export default Tone.CtrlPattern;