2016-01-19 15:02:46 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/instrument/Instrument", "Tone/source/FMOscillator", "Tone/component/Filter",
|
|
|
|
"Tone/component/FrequencyEnvelope", "Tone/component/AmplitudeEnvelope", "Tone/core/Gain", "Tone/signal/Scale", "Tone/signal/Multiply"],
|
|
|
|
function (Tone) {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inharmonic ratio of frequencies based on the Roland TR-808
|
|
|
|
* Taken from https://ccrma.stanford.edu/papers/tr-808-cymbal-physically-informed-circuit-bendable-digital-model
|
|
|
|
* @private
|
|
|
|
* @static
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
|
|
|
var inharmRatios = [1.0, 1.483, 1.932, 2.546, 2.630, 3.897];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class A highly inharmonic and spectrally complex source with a highpass filter
|
|
|
|
* and amplitude envelope which is good for making metalophone sounds. Based
|
|
|
|
* on CymbalSynth by [@polyrhythmatic](https://github.com/polyrhythmatic).
|
|
|
|
* Inspiration from [Sound on Sound](http://www.soundonsound.com/sos/jul02/articles/synthsecrets0702.asp).
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @extends {Tone.Instrument}
|
|
|
|
* @param {Object} [options] The options availble for the synth
|
|
|
|
* see defaults below
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth = function(options){
|
2016-01-19 15:02:46 +00:00
|
|
|
|
2016-01-30 20:48:32 +00:00
|
|
|
options = this.defaultArg(options, Tone.MetalSynth.defaults);
|
2016-01-19 15:02:46 +00:00
|
|
|
Tone.Instrument.call(this, options);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The frequency of the cymbal
|
|
|
|
* @type {Frequency}
|
|
|
|
* @signal
|
|
|
|
*/
|
|
|
|
this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The array of FMOscillators
|
|
|
|
* @type {Array}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._oscillators = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The frequency multipliers
|
|
|
|
* @type {Array}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._freqMultipliers = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The amplitude for the body
|
|
|
|
* @type {Tone.Gain}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._amplitue = new Tone.Gain(0).connect(this.output);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* highpass the output
|
|
|
|
* @type {Tone.Filter}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._highpass = new Tone.Filter({
|
|
|
|
"type" : "highpass",
|
|
|
|
"Q" : 0
|
|
|
|
}).connect(this._amplitue);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of octaves the highpass
|
|
|
|
* filter frequency ramps
|
|
|
|
* @type {Number}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._octaves = options.octaves;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scale the body envelope
|
|
|
|
* for the bandpass
|
|
|
|
* @type {Tone.Scale}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._filterFreqScaler = new Tone.Scale(options.resonance, 7000);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The envelope which is connected both to the
|
|
|
|
* amplitude and highpass filter's cutoff frequency
|
|
|
|
* @type {Tone.Envelope}
|
|
|
|
*/
|
|
|
|
this.envelope = new Tone.Envelope({
|
|
|
|
"attack" : options.envelope.attack,
|
|
|
|
"attackCurve" : "exponential",
|
|
|
|
"decay" : options.envelope.decay,
|
|
|
|
"sustain" : 0,
|
|
|
|
"release" : options.envelope.release,
|
|
|
|
}).chain(this._filterFreqScaler, this._highpass.frequency);
|
|
|
|
this.envelope.connect(this._amplitue.gain);
|
|
|
|
|
|
|
|
for (var i = 0; i < inharmRatios.length; i++){
|
|
|
|
var osc = new Tone.FMOscillator({
|
|
|
|
"type" : "square",
|
|
|
|
"modulationType" : "square",
|
|
|
|
"harmonicity" : options.harmonicity,
|
|
|
|
"modulationIndex" : options.modulationIndex
|
|
|
|
});
|
|
|
|
osc.connect(this._highpass).start(0);
|
|
|
|
this._oscillators[i] = osc;
|
|
|
|
|
|
|
|
var mult = new Tone.Multiply(inharmRatios[i]);
|
|
|
|
this._freqMultipliers[i] = mult;
|
|
|
|
this.frequency.chain(mult, osc.frequency);
|
|
|
|
}
|
|
|
|
|
|
|
|
//set the octaves
|
|
|
|
this.octaves = options.octaves;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.extend(Tone.MetalSynth, Tone.Instrument);
|
2016-01-19 15:02:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* default values
|
|
|
|
* @static
|
|
|
|
* @const
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth.defaults = {
|
2016-01-19 15:02:46 +00:00
|
|
|
"frequency" : 200,
|
|
|
|
"envelope" : {
|
|
|
|
"attack" : 0.0015,
|
|
|
|
"decay" : 1.4,
|
|
|
|
"release" : 0.2
|
|
|
|
},
|
|
|
|
"harmonicity" : 5.1,
|
|
|
|
"modulationIndex" : 32,
|
|
|
|
"resonance" : 4000,
|
|
|
|
"octaves" : 1.5
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger the attack.
|
|
|
|
* @param {Time} time When the attack should be triggered.
|
|
|
|
* @param {NormalRange=1} velocity The velocity that the envelope should be triggered at.
|
2016-01-30 20:48:32 +00:00
|
|
|
* @return {Tone.MetalSynth} this
|
2016-01-19 15:02:46 +00:00
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth.prototype.triggerAttack = function(time, vel) {
|
2016-01-19 15:02:46 +00:00
|
|
|
time = this.toSeconds(time);
|
|
|
|
vel = this.defaultArg(vel, 1);
|
|
|
|
this.envelope.triggerAttack(time, vel);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger the release of the envelope.
|
|
|
|
* @param {Time} time When the release should be triggered.
|
2016-01-30 20:48:32 +00:00
|
|
|
* @return {Tone.MetalSynth} this
|
2016-01-19 15:02:46 +00:00
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth.prototype.triggerRelease = function(time) {
|
2016-01-19 15:02:46 +00:00
|
|
|
time = this.toSeconds(time);
|
|
|
|
this.envelope.triggerRelease(time);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger the attack and release of the envelope after the given
|
|
|
|
* duration.
|
|
|
|
* @param {Time} duration The duration before triggering the release
|
|
|
|
* @param {Time} time When the attack should be triggered.
|
|
|
|
* @param {NormalRange=1} velocity The velocity that the envelope should be triggered at.
|
2016-01-30 20:48:32 +00:00
|
|
|
* @return {Tone.MetalSynth} this
|
2016-01-19 15:02:46 +00:00
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth.prototype.triggerAttackRelease = function(duration, time, velocity) {
|
2016-09-24 00:06:07 +00:00
|
|
|
time = this.toSeconds(time);
|
|
|
|
duration = this.toSeconds(duration);
|
2016-01-19 15:02:46 +00:00
|
|
|
this.triggerAttack(time, velocity);
|
|
|
|
this.triggerRelease(time + duration);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The modulationIndex of the oscillators which make up the source.
|
|
|
|
* see Tone.FMOscillator.modulationIndex
|
2016-01-30 20:48:32 +00:00
|
|
|
* @memberOf Tone.MetalSynth#
|
2016-01-19 15:02:46 +00:00
|
|
|
* @type {Positive}
|
|
|
|
* @name modulationIndex
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Object.defineProperty(Tone.MetalSynth.prototype, "modulationIndex", {
|
2016-01-19 15:02:46 +00:00
|
|
|
get : function(){
|
|
|
|
return this._oscillators[0].modulationIndex.value;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
for (var i = 0; i < this._oscillators.length; i++){
|
|
|
|
this._oscillators[i].modulationIndex.value = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The harmonicity of the oscillators which make up the source.
|
|
|
|
* see Tone.FMOscillator.harmonicity
|
2016-01-30 20:48:32 +00:00
|
|
|
* @memberOf Tone.MetalSynth#
|
2016-01-19 15:02:46 +00:00
|
|
|
* @type {Positive}
|
|
|
|
* @name harmonicity
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Object.defineProperty(Tone.MetalSynth.prototype, "harmonicity", {
|
2016-01-19 15:02:46 +00:00
|
|
|
get : function(){
|
|
|
|
return this._oscillators[0].harmonicity.value;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
for (var i = 0; i < this._oscillators.length; i++){
|
|
|
|
this._oscillators[i].harmonicity.value = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The frequency of the highpass filter attached to the envelope
|
2016-01-30 20:48:32 +00:00
|
|
|
* @memberOf Tone.MetalSynth#
|
2016-01-19 15:02:46 +00:00
|
|
|
* @type {Frequency}
|
|
|
|
* @name resonance
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Object.defineProperty(Tone.MetalSynth.prototype, "resonance", {
|
2016-01-19 15:02:46 +00:00
|
|
|
get : function(){
|
|
|
|
return this._filterFreqScaler.min;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
this._filterFreqScaler.min = val;
|
|
|
|
this.octaves = this._octaves;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of octaves above the "resonance" frequency
|
|
|
|
* that the filter ramps during the attack/decay envelope
|
2016-01-30 20:48:32 +00:00
|
|
|
* @memberOf Tone.MetalSynth#
|
2016-01-19 15:02:46 +00:00
|
|
|
* @type {Number}
|
|
|
|
* @name octaves
|
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Object.defineProperty(Tone.MetalSynth.prototype, "octaves", {
|
2016-01-19 15:02:46 +00:00
|
|
|
get : function(){
|
|
|
|
return this._octaves;
|
|
|
|
},
|
|
|
|
set : function(octs){
|
|
|
|
this._octaves = octs;
|
|
|
|
this._filterFreqScaler.max = this._filterFreqScaler.min * Math.pow(2, octs);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clean up
|
2016-01-30 20:48:32 +00:00
|
|
|
* @returns {Tone.MetalSynth} this
|
2016-01-19 15:02:46 +00:00
|
|
|
*/
|
2016-01-30 20:48:32 +00:00
|
|
|
Tone.MetalSynth.prototype.dispose = function(){
|
2016-01-19 15:02:46 +00:00
|
|
|
Tone.Instrument.prototype.dispose.call(this);
|
|
|
|
for (var i = 0; i < this._oscillators.length; i++){
|
|
|
|
this._oscillators[i].dispose();
|
|
|
|
this._freqMultipliers[i].dispose();
|
|
|
|
}
|
|
|
|
this._oscillators = null;
|
|
|
|
this._freqMultipliers = null;
|
|
|
|
this.frequency.dispose();
|
|
|
|
this.frequency = null;
|
|
|
|
this._filterFreqScaler.dispose();
|
|
|
|
this._filterFreqScaler = null;
|
|
|
|
this._amplitue.dispose();
|
|
|
|
this._amplitue = null;
|
|
|
|
this.envelope.dispose();
|
|
|
|
this.envelope = null;
|
|
|
|
this._highpass.dispose();
|
|
|
|
this._highpass = null;
|
|
|
|
};
|
|
|
|
|
2016-01-30 20:48:32 +00:00
|
|
|
return Tone.MetalSynth;
|
2016-01-19 15:02:46 +00:00
|
|
|
});
|