Tone.js/Tone/event/Sequence.js
tambien ed71d8141b amd to es6 import/export
no longer using AMD (require.js) style imports, and beginning to move to es6 "import/export" statements everywhere.
2019-01-27 13:05:20 -05:00

162 lines
4.6 KiB
JavaScript

import Tone from "../core/Tone";
import "../event/Part";
import "../core/Transport";
/**
* @class A sequence is an alternate notation of a part. Instead
* of passing in an array of [time, event] pairs, pass
* in an array of events which will be spaced at the
* given subdivision. Sub-arrays will subdivide that beat
* by the number of items are in the array.
* Sequence notation inspiration from [Tidal](http://yaxu.org/tidal/)
* @param {Function} callback The callback to invoke with every note
* @param {Array} events The sequence
* @param {Time} subdivision The subdivision between which events are placed.
* @extends {Tone.Part}
* @example
* var seq = new Tone.Sequence(function(time, note){
* console.log(note);
* //straight quater notes
* }, ["C4", "E4", "G4", "A4"], "4n");
* @example
* var seq = new Tone.Sequence(function(time, note){
* console.log(note);
* //subdivisions are given as subarrays
* }, ["C4", ["E4", "D4", "E4"], "G4", ["A4", "G4"]]);
*/
Tone.Sequence = function(){
var options = Tone.defaults(arguments, ["callback", "events", "subdivision"], Tone.Sequence);
//remove the events
var events = options.events;
delete options.events;
Tone.Part.call(this, options);
/**
* The subdivison of each note
* @type {Ticks}
* @private
*/
this._subdivision = this.toTicks(options.subdivision);
//if no time was passed in, the loop end is the end of the cycle
if (Tone.isUndef(options.loopEnd) && Tone.isDefined(events)){
this._loopEnd = (events.length * this._subdivision);
}
//defaults to looping
this._loop = true;
//add all of the events
if (Tone.isDefined(events)){
for (var i = 0; i < events.length; i++){
this.add(i, events[i]);
}
}
};
Tone.extend(Tone.Sequence, Tone.Part);
/**
* The default values.
* @type {Object}
*/
Tone.Sequence.defaults = {
"subdivision" : "4n",
};
/**
* The subdivision of the sequence. This can only be
* set in the constructor. The subdivision is the
* interval between successive steps.
* @type {Time}
* @memberOf Tone.Sequence#
* @name subdivision
* @readOnly
*/
Object.defineProperty(Tone.Sequence.prototype, "subdivision", {
get : function(){
return Tone.Ticks(this._subdivision).toSeconds();
}
});
/**
* Get/Set an index of the sequence. If the index contains a subarray,
* a Tone.Sequence representing that sub-array will be returned.
* @example
* var sequence = new Tone.Sequence(playNote, ["E4", "C4", "F#4", ["A4", "Bb3"]])
* sequence.at(0)// => returns "E4"
* //set a value
* sequence.at(0, "G3");
* //get a nested sequence
* sequence.at(3).at(1)// => returns "Bb3"
* @param {Positive} index The index to get or set
* @param {*} value Optionally pass in the value to set at the given index.
*/
Tone.Sequence.prototype.at = function(index, value){
//if the value is an array,
if (Tone.isArray(value)){
//remove the current event at that index
this.remove(index);
}
//call the parent's method
return Tone.Part.prototype.at.call(this, this._indexTime(index), value);
};
/**
* Add an event at an index, if there's already something
* at that index, overwrite it. If `value` is an array,
* it will be parsed as a subsequence.
* @param {Number} index The index to add the event to
* @param {*} value The value to add at that index
* @returns {Tone.Sequence} this
*/
Tone.Sequence.prototype.add = function(index, value){
if (value === null){
return this;
}
if (Tone.isArray(value)){
//make a subsequence and add that to the sequence
var subSubdivision = Math.round(this._subdivision / value.length);
value = new Tone.Sequence(this._tick.bind(this), value, Tone.Ticks(subSubdivision));
}
Tone.Part.prototype.add.call(this, this._indexTime(index), value);
return this;
};
/**
* Remove a value from the sequence by index
* @param {Number} index The index of the event to remove
* @returns {Tone.Sequence} this
*/
Tone.Sequence.prototype.remove = function(index, value){
Tone.Part.prototype.remove.call(this, this._indexTime(index), value);
return this;
};
/**
* Get the time of the index given the Sequence's subdivision
* @param {Number} index
* @return {Time} The time of that index
* @private
*/
Tone.Sequence.prototype._indexTime = function(index){
if (index instanceof Tone.TransportTime){
return index;
} else {
return Tone.Ticks(index * this._subdivision + this.startOffset).toSeconds();
}
};
/**
* Clean up.
* @return {Tone.Sequence} this
*/
Tone.Sequence.prototype.dispose = function(){
Tone.Part.prototype.dispose.call(this);
return this;
};
export default Tone.Sequence;