2017-10-21 23:02:46 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/instrument/Synth", "Tone/source/Source"], function(Tone){
|
2014-08-25 17:26:26 +00:00
|
|
|
|
2014-09-04 04:41:40 +00:00
|
|
|
"use strict";
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
2015-06-14 04:32:17 +00:00
|
|
|
* @class Tone.PolySynth handles voice creation and allocation for any
|
2017-10-21 23:02:46 +00:00
|
|
|
* instruments passed in as the second paramter. PolySynth is
|
|
|
|
* not a synthesizer by itself, it merely manages voices of
|
|
|
|
* one of the other types of synths, allowing any of the
|
|
|
|
* monophonic synthesizers to be polyphonic.
|
2014-08-25 17:26:26 +00:00
|
|
|
*
|
|
|
|
* @constructor
|
2014-09-20 23:24:04 +00:00
|
|
|
* @extends {Tone.Instrument}
|
2015-06-20 22:03:49 +00:00
|
|
|
* @param {number|Object} [polyphony=4] The number of voices to create
|
2016-05-23 23:56:15 +00:00
|
|
|
* @param {function} [voice=Tone.Synth] The constructor of the voices
|
2017-10-21 23:02:46 +00:00
|
|
|
* uses Tone.Synth by default.
|
2015-02-28 04:24:51 +00:00
|
|
|
* @example
|
2016-05-23 23:56:15 +00:00
|
|
|
* //a polysynth composed of 6 Voices of Synth
|
|
|
|
* var synth = new Tone.PolySynth(6, Tone.Synth).toMaster();
|
2015-06-14 04:32:17 +00:00
|
|
|
* //set the attributes using the set interface
|
|
|
|
* synth.set("detune", -1200);
|
2015-06-16 02:36:20 +00:00
|
|
|
* //play a chord
|
|
|
|
* synth.triggerAttackRelease(["C4", "E4", "A4"], "4n");
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
|
|
|
Tone.PolySynth = function(){
|
|
|
|
|
2017-04-26 04:07:10 +00:00
|
|
|
var options = Tone.defaults(arguments, ["polyphony", "voice"], Tone.PolySynth);
|
|
|
|
Tone.Instrument.call(this, options);
|
2017-04-30 19:03:49 +00:00
|
|
|
options = Tone.defaultArg(options, Tone.Instrument.defaults);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
2016-03-03 18:01:11 +00:00
|
|
|
//max polyphony
|
|
|
|
options.polyphony = Math.min(Tone.PolySynth.MAX_POLYPHONY, options.polyphony);
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
|
|
|
* the array of voices
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
2014-12-08 16:03:20 +00:00
|
|
|
this.voices = new Array(options.polyphony);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
|
|
|
/**
|
2016-02-04 19:33:42 +00:00
|
|
|
* The queue of voices with data about last trigger
|
|
|
|
* and the triggered note
|
2014-08-25 17:26:26 +00:00
|
|
|
* @private
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
2016-02-04 19:33:42 +00:00
|
|
|
this._triggers = new Array(options.polyphony);
|
2014-08-25 17:26:26 +00:00
|
|
|
|
2016-05-15 00:49:55 +00:00
|
|
|
/**
|
|
|
|
* The detune in cents
|
|
|
|
* @type {Cents}
|
|
|
|
* @signal
|
|
|
|
*/
|
|
|
|
this.detune = new Tone.Signal(options.detune, Tone.Type.Cents);
|
|
|
|
this._readOnly("detune");
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
//create the voices
|
|
|
|
for (var i = 0; i < options.polyphony; i++){
|
2015-02-20 06:00:32 +00:00
|
|
|
var v = new options.voice(arguments[2], arguments[3]);
|
2017-11-26 19:22:25 +00:00
|
|
|
if (!(v instanceof Tone.Monophonic)){
|
|
|
|
throw new Error("Synth constructor must be instance of Tone.Monophonic");
|
|
|
|
}
|
2014-12-08 16:03:20 +00:00
|
|
|
this.voices[i] = v;
|
2014-08-25 17:26:26 +00:00
|
|
|
v.connect(this.output);
|
2016-05-15 00:49:55 +00:00
|
|
|
if (v.hasOwnProperty("detune")){
|
|
|
|
this.detune.connect(v.detune);
|
|
|
|
}
|
2016-02-04 19:33:42 +00:00
|
|
|
this._triggers[i] = {
|
|
|
|
release : -1,
|
|
|
|
note : null,
|
|
|
|
voice : v
|
|
|
|
};
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
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,
|
2016-02-04 19:33:42 +00:00
|
|
|
"volume" : 0,
|
2016-05-15 00:49:55 +00:00
|
|
|
"detune" : 0,
|
2016-05-23 23:56:15 +00:00
|
|
|
"voice" : Tone.Synth
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
2015-02-20 06:00:32 +00:00
|
|
|
/**
|
2015-06-20 22:03:49 +00:00
|
|
|
* Trigger the attack portion of the note
|
|
|
|
* @param {Frequency|Array} notes The notes to play. Accepts a single
|
|
|
|
* Frequency or an array of frequencies.
|
|
|
|
* @param {Time} [time=now] The start time of the note.
|
|
|
|
* @param {number} [velocity=1] The velocity of the note.
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.PolySynth} this
|
2015-06-20 22:03:49 +00:00
|
|
|
* @example
|
|
|
|
* //trigger a chord immediately with a velocity of 0.2
|
|
|
|
* poly.triggerAttack(["Ab3", "C4", "F5"], undefined, 0.2);
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
2015-06-20 22:03:49 +00:00
|
|
|
Tone.PolySynth.prototype.triggerAttack = function(notes, time, velocity){
|
2015-06-26 05:21:59 +00:00
|
|
|
if (!Array.isArray(notes)){
|
|
|
|
notes = [notes];
|
2014-10-19 20:17:40 +00:00
|
|
|
}
|
2016-02-04 19:33:42 +00:00
|
|
|
time = this.toSeconds(time);
|
2015-06-26 05:21:59 +00:00
|
|
|
for (var i = 0; i < notes.length; i++){
|
|
|
|
var val = notes[i];
|
2016-02-08 18:43:45 +00:00
|
|
|
//trigger the oldest voice
|
|
|
|
var oldest = this._triggers[0];
|
|
|
|
for (var j = 1; j < this._triggers.length; j++){
|
|
|
|
if (this._triggers[j].release < oldest.release){
|
|
|
|
oldest = this._triggers[j];
|
2016-02-04 19:33:42 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-08 18:43:45 +00:00
|
|
|
oldest.release = Infinity;
|
|
|
|
oldest.note = JSON.stringify(val);
|
|
|
|
oldest.voice.triggerAttack(val, time, velocity);
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
2015-02-02 18:30:36 +00:00
|
|
|
return this;
|
2014-09-04 02:35:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2015-06-20 22:03:49 +00:00
|
|
|
* Trigger the attack and release after the specified duration
|
2017-10-21 23:02:46 +00:00
|
|
|
*
|
2015-06-20 22:03:49 +00:00
|
|
|
* @param {Frequency|Array} notes The notes to play. Accepts a single
|
|
|
|
* Frequency or an array of frequencies.
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time} duration the duration of the note
|
|
|
|
* @param {Time} [time=now] if no time is given, defaults to now
|
2014-12-02 06:42:08 +00:00
|
|
|
* @param {number} [velocity=1] the velocity of the attack (0-1)
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.PolySynth} this
|
2015-06-20 22:03:49 +00:00
|
|
|
* @example
|
2017-10-21 23:02:46 +00:00
|
|
|
* //trigger a chord for a duration of a half note
|
2015-06-20 22:03:49 +00:00
|
|
|
* poly.triggerAttackRelease(["Eb3", "G4", "C5"], "2n");
|
2016-05-14 23:55:51 +00:00
|
|
|
* @example
|
|
|
|
* //can pass in an array of durations as well
|
|
|
|
* poly.triggerAttackRelease(["Eb3", "G4", "C5"], ["2n", "4n", "4n"]);
|
2014-09-04 02:35:27 +00:00
|
|
|
*/
|
2015-06-20 22:03:49 +00:00
|
|
|
Tone.PolySynth.prototype.triggerAttackRelease = function(notes, duration, time, velocity){
|
2014-09-04 02:35:27 +00:00
|
|
|
time = this.toSeconds(time);
|
2015-06-26 05:21:59 +00:00
|
|
|
this.triggerAttack(notes, time, velocity);
|
2017-04-26 04:24:19 +00:00
|
|
|
if (Tone.isArray(duration) && Tone.isArray(notes)){
|
2016-05-14 23:55:51 +00:00
|
|
|
for (var i = 0; i < notes.length; i++){
|
|
|
|
var d = duration[Math.min(i, duration.length - 1)];
|
2017-10-21 23:02:46 +00:00
|
|
|
this.triggerRelease(notes[i], time + this.toSeconds(d));
|
2016-05-14 23:55:51 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.triggerRelease(notes, time + this.toSeconds(duration));
|
|
|
|
}
|
2015-02-02 18:30:36 +00:00
|
|
|
return this;
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2017-10-21 23:02:46 +00:00
|
|
|
* Trigger the release of the note. Unlike monophonic instruments,
|
2015-06-20 22:03:49 +00:00
|
|
|
* a note (or array of notes) needs to be passed in as the first argument.
|
|
|
|
* @param {Frequency|Array} notes The notes to play. Accepts a single
|
|
|
|
* Frequency or an array of frequencies.
|
2017-10-21 23:02:46 +00:00
|
|
|
* @param {Time} [time=now] When the release will be triggered.
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.PolySynth} this
|
2015-06-20 22:03:49 +00:00
|
|
|
* @example
|
2015-08-16 19:17:20 +00:00
|
|
|
* poly.triggerRelease(["Ab3", "C4", "F5"], "+2n");
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
2015-06-20 22:03:49 +00:00
|
|
|
Tone.PolySynth.prototype.triggerRelease = function(notes, time){
|
|
|
|
if (!Array.isArray(notes)){
|
|
|
|
notes = [notes];
|
2014-10-19 20:17:40 +00:00
|
|
|
}
|
2016-02-04 19:33:42 +00:00
|
|
|
time = this.toSeconds(time);
|
2015-06-20 22:03:49 +00:00
|
|
|
for (var i = 0; i < notes.length; i++){
|
2014-10-19 20:17:40 +00:00
|
|
|
//get the voice
|
2015-06-20 22:03:49 +00:00
|
|
|
var stringified = JSON.stringify(notes[i]);
|
2016-02-04 19:33:42 +00:00
|
|
|
for (var v = 0; v < this._triggers.length; v++){
|
|
|
|
var desc = this._triggers[v];
|
|
|
|
if (desc.note === stringified && desc.release > time){
|
|
|
|
desc.voice.triggerRelease(time);
|
|
|
|
desc.release = time;
|
|
|
|
}
|
2014-10-19 20:17:40 +00:00
|
|
|
}
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
2015-02-02 18:30:36 +00:00
|
|
|
return this;
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
2018-01-02 21:49:41 +00:00
|
|
|
/**
|
|
|
|
* Sync the instrument to the Transport. All subsequent calls of
|
|
|
|
* [triggerAttack](#triggerattack) and [triggerRelease](#triggerrelease)
|
|
|
|
* will be scheduled along the transport.
|
|
|
|
* @example
|
|
|
|
* synth.sync()
|
|
|
|
* //schedule 3 notes when the transport first starts
|
|
|
|
* synth.triggerAttackRelease('8n', 0)
|
|
|
|
* synth.triggerAttackRelease('8n', '8n')
|
|
|
|
* synth.triggerAttackRelease('8n', '4n')
|
|
|
|
* //start the transport to hear the notes
|
|
|
|
* Transport.start()
|
|
|
|
* @returns {Tone.Instrument} this
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.sync = function(){
|
|
|
|
this._syncMethod("triggerAttack", 1);
|
|
|
|
this._syncMethod("triggerRelease", 1);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
2017-10-21 23:02:46 +00:00
|
|
|
* Set a member/attribute of the voices.
|
2015-04-24 23:34:26 +00:00
|
|
|
* @param {Object|string} params
|
|
|
|
* @param {number=} value
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time=} rampTime
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.PolySynth} this
|
2015-06-20 22:03:49 +00:00
|
|
|
* @example
|
|
|
|
* poly.set({
|
|
|
|
* "filter" : {
|
|
|
|
* "type" : "highpass"
|
|
|
|
* },
|
|
|
|
* "envelope" : {
|
|
|
|
* "attack" : 0.25
|
|
|
|
* }
|
|
|
|
* });
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
2015-04-24 23:34:26 +00:00
|
|
|
Tone.PolySynth.prototype.set = function(params, value, rampTime){
|
2014-12-08 16:03:20 +00:00
|
|
|
for (var i = 0; i < this.voices.length; i++){
|
2015-04-24 23:34:26 +00:00
|
|
|
this.voices[i].set(params, value, rampTime);
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
2015-02-02 18:30:36 +00:00
|
|
|
return this;
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
2015-02-20 06:00:32 +00:00
|
|
|
/**
|
2015-06-20 22:03:49 +00:00
|
|
|
* Get the synth's attributes. Given no arguments get
|
|
|
|
* will return all available object properties and their corresponding
|
|
|
|
* values. Pass in a single attribute to retrieve or an array
|
|
|
|
* of attributes. The attribute strings can also include a "."
|
|
|
|
* to access deeper properties.
|
2017-10-21 23:02:46 +00:00
|
|
|
* @param {Array=} params the parameters to get, otherwise will return
|
2015-02-20 06:00:32 +00:00
|
|
|
* all available.
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.get = function(params){
|
|
|
|
return this.voices[0].get(params);
|
|
|
|
};
|
|
|
|
|
2015-08-16 19:17:20 +00:00
|
|
|
/**
|
|
|
|
* Trigger the release portion of all the currently active voices.
|
|
|
|
* @param {Time} [time=now] When the notes should be released.
|
|
|
|
* @return {Tone.PolySynth} this
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.releaseAll = function(time){
|
2016-02-04 19:33:42 +00:00
|
|
|
time = this.toSeconds(time);
|
|
|
|
for (var i = 0; i < this._triggers.length; i++){
|
|
|
|
var desc = this._triggers[i];
|
|
|
|
if (desc.release > time){
|
|
|
|
desc.release = time;
|
|
|
|
desc.voice.triggerRelease(time);
|
|
|
|
}
|
2015-08-16 19:17:20 +00:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
/**
|
2015-06-20 22:03:49 +00:00
|
|
|
* Clean up.
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.PolySynth} this
|
2014-08-25 17:26:26 +00:00
|
|
|
*/
|
|
|
|
Tone.PolySynth.prototype.dispose = function(){
|
2014-09-20 23:24:04 +00:00
|
|
|
Tone.Instrument.prototype.dispose.call(this);
|
2014-12-08 16:03:20 +00:00
|
|
|
for (var i = 0; i < this.voices.length; i++){
|
|
|
|
this.voices[i].dispose();
|
|
|
|
this.voices[i] = null;
|
2014-08-25 17:26:26 +00:00
|
|
|
}
|
2016-05-15 00:49:55 +00:00
|
|
|
this._writable("detune");
|
|
|
|
this.detune.dispose();
|
|
|
|
this.detune = null;
|
2014-12-08 16:03:20 +00:00
|
|
|
this.voices = null;
|
2016-02-04 19:33:42 +00:00
|
|
|
this._triggers = null;
|
2015-02-02 18:30:36 +00:00
|
|
|
return this;
|
2014-08-25 17:26:26 +00:00
|
|
|
};
|
|
|
|
|
2016-03-03 18:01:11 +00:00
|
|
|
/**
|
2017-10-21 23:02:46 +00:00
|
|
|
* The maximum number of notes that can be allocated
|
|
|
|
* to a polysynth.
|
2016-03-03 18:01:11 +00:00
|
|
|
* @type {Number}
|
|
|
|
* @static
|
|
|
|
*/
|
|
|
|
Tone.PolySynth.MAX_POLYPHONY = 20;
|
|
|
|
|
2014-08-25 17:26:26 +00:00
|
|
|
return Tone.PolySynth;
|
2017-10-21 23:02:46 +00:00
|
|
|
});
|