2014-09-04 02:35:27 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/instrument/MonoSynth", "Tone/source/Source"],
|
2014-08-25 17:26:26 +00:00
|
|
|
function(Tone){
|
|
|
|
|
2014-09-04 04:41:40 +00:00
|
|
|
"use strict";
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
|
|
|
* @class Creates a polyphonic synthesizer out of
|
|
|
|
* the monophonic voice which is passed in.
|
|
|
|
*
|
2014-09-09 04:40:25 +00:00
|
|
|
* @example
|
|
|
|
* //a polysynth composed of 6 Voices of MonoSynth
|
|
|
|
* var synth = new Tone.PolySynth(6, Tone.MonoSynth);
|
|
|
|
* //set the MonoSynth preset
|
|
|
|
* synth.setPreset("Pianoetta");
|
|
|
|
*
|
2014-08-25 17:26:26 +00:00
|
|
|
* @constructor
|
2014-09-20 23:24:04 +00:00
|
|
|
* @extends {Tone.Instrument}
|
|
|
|
* @param {number|Object} [polyphony=4] the number of voices to create
|
|
|
|
* @param {function} [voice=Tone.MonoSynth] the constructor of the voices
|
2014-08-25 17:26:26 +00:00
|
|
|
* uses Tone.MonoSynth by default
|
2014-09-20 23:24:04 +00:00
|
|
|
* @param {Object} voiceOptions the options to pass to the voice
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
|
|
|
Tone.PolySynth = function(){
|
|
|
|
|
2014-09-20 23:24:04 +00:00
|
|
|
Tone.Instrument.call(this);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
2014-09-20 23:24:04 +00:00
|
|
|
var options = this.optionsObject(arguments, ["polyphony", "voice", "voiceOptions"], Tone.PolySynth.defaults);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the array of voices
|
|
|
|
* @private
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
|
|
|
this._voices = new Array(options.polyphony);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the queue of free voices
|
|
|
|
* @private
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
|
|
|
this._freeVoices = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* keeps track of which notes are down
|
|
|
|
* @private
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
this._activeVoices = {};
|
|
|
|
|
|
|
|
//create the voices
|
|
|
|
for (var i = 0; i < options.polyphony; i++){
|
|
|
|
var v = new options.voice(options.voiceOptions);
|
|
|
|
this._voices[i] = v;
|
|
|
|
v.connect(this.output);
|
|
|
|
}
|
|
|
|
|
|
|
|
//make a copy of the voices
|
|
|
|
this._freeVoices = this._voices.slice(0);
|
|
|
|
};
|
|
|
|
|
2014-09-20 23:24:04 +00:00
|
|
|
Tone.extend(Tone.PolySynth, Tone.Instrument);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the defaults
|
|
|
|
* @const
|
|
|
|
* @static
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.defaults = {
|
|
|
|
"polyphony" : 4,
|
|
|
|
"voice" : Tone.MonoSynth,
|
|
|
|
"voiceOptions" : {
|
|
|
|
"portamento" : 0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* trigger the attack
|
2014-10-19 20:17:40 +00:00
|
|
|
* @param {string|number|Object|Array} value the value of the note(s) to start.
|
|
|
|
* if the value is an array, it will iterate
|
|
|
|
* over the array to play each of the notes
|
2014-08-25 17:26:26 +00:00
|
|
|
* @param {Tone.Time=} [time=now] the start time of the note
|
2014-09-04 02:35:27 +00:00
|
|
|
* @param {number=} velocity the velocity of the note
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
2014-09-04 02:35:27 +00:00
|
|
|
Tone.PolySynth.prototype.triggerAttack = function(value, time, velocity){
|
2014-10-19 20:17:40 +00:00
|
|
|
if (!Array.isArray(value)){
|
|
|
|
value = [value];
|
|
|
|
}
|
|
|
|
for (var i = 0; i < value.length; i++){
|
|
|
|
var val = value[i];
|
|
|
|
var stringified = JSON.stringify(val);
|
|
|
|
if (this._activeVoices[stringified]){
|
|
|
|
this._activeVoices[stringified].triggerAttack(val, time, velocity);
|
|
|
|
} else if (this._freeVoices.length > 0){
|
|
|
|
var voice = this._freeVoices.shift();
|
|
|
|
voice.triggerAttack(val, time, velocity);
|
|
|
|
this._activeVoices[stringified] = voice;
|
|
|
|
}
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
2014-09-04 02:35:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* trigger the attack and release after the specified duration
|
|
|
|
*
|
2014-10-19 20:17:40 +00:00
|
|
|
* @param {string|number|Object|Array} value the note(s).
|
|
|
|
* if the value is an array, it will iterate
|
|
|
|
* over the array to play each of the notes
|
2014-09-04 02:35:27 +00:00
|
|
|
* @param {Tone.Time} duration the duration of the note
|
|
|
|
* @param {Tone.Time=} time if no time is given, defaults to now
|
|
|
|
* @param {number=} velocity the velocity of the attack (0-1)
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.triggerAttackRelease = function(value, duration, time, velocity){
|
|
|
|
time = this.toSeconds(time);
|
|
|
|
this.triggerAttack(value, time, velocity);
|
2014-09-09 04:40:25 +00:00
|
|
|
this.triggerRelease(value, time + this.toSeconds(duration));
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* trigger the release of a note
|
2014-10-19 20:17:40 +00:00
|
|
|
* @param {string|number|Object|Array} value the value of the note(s) to release.
|
|
|
|
* if the value is an array, it will iterate
|
|
|
|
* over the array to play each of the notes
|
2014-08-25 17:26:26 +00:00
|
|
|
* @param {Tone.Time=} [time=now] the release time of the note
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.triggerRelease = function(value, time){
|
2014-10-19 20:17:40 +00:00
|
|
|
if (!Array.isArray(value)){
|
|
|
|
value = [value];
|
|
|
|
}
|
|
|
|
for (var i = 0; i < value.length; i++){
|
|
|
|
//get the voice
|
|
|
|
var stringified = JSON.stringify(value[i]);
|
|
|
|
var voice = this._activeVoices[stringified];
|
|
|
|
if (voice){
|
|
|
|
voice.triggerRelease(time);
|
|
|
|
this._freeVoices.push(voice);
|
|
|
|
this._activeVoices[stringified] = null;
|
|
|
|
}
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set the options on all of the voices
|
|
|
|
* @param {Object} params
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.set = function(params){
|
|
|
|
for (var i = 0; i < this._voices.length; i++){
|
|
|
|
this._voices[i].set(params);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-09 04:40:25 +00:00
|
|
|
/**
|
|
|
|
* @param {string} presetName the preset name
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.setPreset = function(presetName){
|
|
|
|
for (var i = 0; i < this._voices.length; i++){
|
|
|
|
this._voices[i].setPreset(presetName);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
|
|
|
* clean up
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.dispose = function(){
|
2014-09-20 23:24:04 +00:00
|
|
|
Tone.Instrument.prototype.dispose.call(this);
|
2014-08-25 17:26:26 +00:00
|
|
|
for (var i = 0; i < this._voices.length; i++){
|
|
|
|
this._voices[i].dispose();
|
|
|
|
this._voices[i] = null;
|
|
|
|
}
|
|
|
|
this._voices = null;
|
|
|
|
this._activeVoices = null;
|
|
|
|
this._freeVoices = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
return Tone.PolySynth;
|
|
|
|
});
|