2019-01-27 18:05:20 +00:00
|
|
|
import Tone from "../core/Tone";
|
2015-11-04 00:14:01 +00:00
|
|
|
|
2019-01-27 18:05:20 +00:00
|
|
|
/**
|
|
|
|
* @class Tone.CtrlMarkov represents a Markov Chain where each call
|
|
|
|
* to Tone.CtrlMarkov.next will move to the next state. If the next
|
|
|
|
* state choice is an array, the next state is chosen randomly with
|
|
|
|
* even probability for all of the choices. For a weighted probability
|
|
|
|
* of the next choices, pass in an object with "state" and "probability" attributes.
|
|
|
|
* The probabilities will be normalized and then chosen. If no next options
|
|
|
|
* are given for the current state, the state will stay there.
|
|
|
|
* @extends {Tone}
|
|
|
|
* @example
|
|
|
|
* var chain = new Tone.CtrlMarkov({
|
|
|
|
* "beginning" : ["end", "middle"],
|
|
|
|
* "middle" : "end"
|
|
|
|
* });
|
|
|
|
* chain.value = "beginning";
|
|
|
|
* chain.next(); //returns "end" or "middle" with 50% probability
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* var chain = new Tone.CtrlMarkov({
|
|
|
|
* "beginning" : [{"value" : "end", "probability" : 0.8},
|
|
|
|
* {"value" : "middle", "probability" : 0.2}],
|
|
|
|
* "middle" : "end"
|
|
|
|
* });
|
|
|
|
* chain.value = "beginning";
|
|
|
|
* chain.next(); //returns "end" with 80% probability or "middle" with 20%.
|
|
|
|
* @param {Object} values An object with the state names as the keys
|
|
|
|
* and the next state(s) as the values.
|
|
|
|
*/
|
|
|
|
Tone.CtrlMarkov = function(values, initial){
|
|
|
|
|
|
|
|
Tone.call(this);
|
2015-11-04 00:14:01 +00:00
|
|
|
|
|
|
|
/**
|
2019-01-27 18:05:20 +00:00
|
|
|
* The Markov values with states as the keys
|
|
|
|
* and next state(s) as the values.
|
|
|
|
* @type {Object}
|
2015-11-04 00:14:01 +00:00
|
|
|
*/
|
2019-01-27 18:05:20 +00:00
|
|
|
this.values = Tone.defaultArg(values, {});
|
|
|
|
|
2015-11-04 00:14:01 +00:00
|
|
|
/**
|
2019-01-27 18:05:20 +00:00
|
|
|
* The current state of the Markov values. The next
|
|
|
|
* state will be evaluated and returned when Tone.CtrlMarkov.next
|
|
|
|
* is invoked.
|
|
|
|
* @type {String}
|
2015-11-04 00:14:01 +00:00
|
|
|
*/
|
2019-01-27 18:05:20 +00:00
|
|
|
this.value = Tone.defaultArg(initial, Object.keys(this.values)[0]);
|
|
|
|
};
|
|
|
|
|
|
|
|
Tone.extend(Tone.CtrlMarkov);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the next state of the Markov values.
|
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
Tone.CtrlMarkov.prototype.next = function(){
|
|
|
|
if (this.values.hasOwnProperty(this.value)){
|
|
|
|
var next = this.values[this.value];
|
|
|
|
if (Tone.isArray(next)){
|
|
|
|
var distribution = this._getProbDistribution(next);
|
|
|
|
var rand = Math.random();
|
|
|
|
var total = 0;
|
|
|
|
for (var i = 0; i < distribution.length; i++){
|
|
|
|
var dist = distribution[i];
|
|
|
|
if (rand > total && rand < total + dist){
|
|
|
|
var chosen = next[i];
|
|
|
|
if (Tone.isObject(chosen)){
|
|
|
|
this.value = chosen.value;
|
|
|
|
} else {
|
|
|
|
this.value = chosen;
|
2015-11-04 00:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-27 18:05:20 +00:00
|
|
|
total += dist;
|
2015-11-04 00:14:01 +00:00
|
|
|
}
|
2019-01-27 18:05:20 +00:00
|
|
|
} else {
|
|
|
|
this.value = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.value;
|
|
|
|
};
|
2015-11-04 00:14:01 +00:00
|
|
|
|
2019-01-27 18:05:20 +00:00
|
|
|
/**
|
|
|
|
* Choose randomly from an array weighted options in the form
|
|
|
|
* {"state" : string, "probability" : number} or an array of values
|
|
|
|
* @param {Array} options
|
|
|
|
* @return {Array} The randomly selected choice
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Tone.CtrlMarkov.prototype._getProbDistribution = function(options){
|
|
|
|
var distribution = [];
|
|
|
|
var total = 0;
|
|
|
|
var needsNormalizing = false;
|
|
|
|
for (var i = 0; i < options.length; i++){
|
|
|
|
var option = options[i];
|
|
|
|
if (Tone.isObject(option)){
|
|
|
|
needsNormalizing = true;
|
|
|
|
distribution[i] = option.probability;
|
|
|
|
} else {
|
|
|
|
distribution[i] = 1 / options.length;
|
2015-11-04 00:14:01 +00:00
|
|
|
}
|
2019-01-27 18:05:20 +00:00
|
|
|
total += distribution[i];
|
|
|
|
}
|
|
|
|
if (needsNormalizing){
|
|
|
|
//normalize the values
|
|
|
|
for (var j = 0; j < distribution.length; j++){
|
|
|
|
distribution[j] = distribution[j] / total;
|
2015-11-04 00:14:01 +00:00
|
|
|
}
|
2019-01-27 18:05:20 +00:00
|
|
|
}
|
|
|
|
return distribution;
|
|
|
|
};
|
2015-11-04 00:14:01 +00:00
|
|
|
|
2019-01-27 18:05:20 +00:00
|
|
|
/**
|
|
|
|
* Clean up
|
|
|
|
* @return {Tone.CtrlMarkov} this
|
|
|
|
*/
|
|
|
|
Tone.CtrlMarkov.prototype.dispose = function(){
|
|
|
|
this.values = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default Tone.CtrlMarkov;
|
2015-11-04 00:14:01 +00:00
|
|
|
|