mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-28 20:43:08 +00:00
268 lines
6.8 KiB
JavaScript
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;
|
|
|