import Tone from "../core/Tone"; import "../source/Source"; import "../source/Oscillator"; import "../source/PulseOscillator"; import "../source/PWMOscillator"; import "../source/FMOscillator"; import "../source/AMOscillator"; import "../source/FatOscillator"; /** * @class Tone.OmniOscillator aggregates Tone.Oscillator, Tone.PulseOscillator, * Tone.PWMOscillator, Tone.FMOscillator, Tone.AMOscillator, and Tone.FatOscillator * into one class. The oscillator class can be changed by setting the `type`. * `omniOsc.type = "pwm"` will set it to the Tone.PWMOscillator. Prefixing * any of the basic types ("sine", "square4", etc.) with "fm", "am", or "fat" * will use the FMOscillator, AMOscillator or FatOscillator respectively. * For example: `omniOsc.type = "fatsawtooth"` will create set the oscillator * to a FatOscillator of type "sawtooth". * * @extends {Tone.Source} * @constructor * @param {Frequency} frequency The initial frequency of the oscillator. * @param {String} type The type of the oscillator. * @example * var omniOsc = new Tone.OmniOscillator("C#4", "pwm"); */ Tone.OmniOscillator = function(){ var options = Tone.defaults(arguments, ["frequency", "type"], Tone.OmniOscillator); Tone.Source.call(this, options); /** * The frequency control. * @type {Frequency} * @signal */ this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency); /** * The detune control * @type {Cents} * @signal */ this.detune = new Tone.Signal(options.detune, Tone.Type.Cents); /** * the type of the oscillator source * @type {String} * @private */ this._sourceType = undefined; /** * the oscillator * @type {Tone.Oscillator} * @private */ this._oscillator = null; //set the oscillator this.type = options.type; this._readOnly(["frequency", "detune"]); //set the options this.set(options); }; Tone.extend(Tone.OmniOscillator, Tone.Source); /** * default values * @static * @type {Object} * @const */ Tone.OmniOscillator.defaults = { "frequency" : 440, "detune" : 0, "type" : "sine", "phase" : 0 }; /** * @enum {String} * @private */ var OmniOscType = { Pulse : "PulseOscillator", PWM : "PWMOscillator", Osc : "Oscillator", FM : "FMOscillator", AM : "AMOscillator", Fat : "FatOscillator" }; /** * start the oscillator * @param {Time} [time=now] the time to start the oscillator * @private */ Tone.OmniOscillator.prototype._start = function(time){ this._oscillator.start(time); }; /** * start the oscillator * @param {Time} [time=now] the time to start the oscillator * @private */ Tone.OmniOscillator.prototype._stop = function(time){ this._oscillator.stop(time); }; Tone.OmniOscillator.prototype.restart = function(time){ this._oscillator.restart(time); }; /** * The type of the oscillator. Can be any of the basic types: sine, square, triangle, sawtooth. Or * prefix the basic types with "fm", "am", or "fat" to use the FMOscillator, AMOscillator or FatOscillator * types. The oscillator could also be set to "pwm" or "pulse". All of the parameters of the * oscillator's class are accessible when the oscillator is set to that type, but throws an error * when it's not. * * @memberOf Tone.OmniOscillator# * @type {String} * @name type * @example * omniOsc.type = "pwm"; * //modulationFrequency is parameter which is available * //only when the type is "pwm". * omniOsc.modulationFrequency.value = 0.5; * @example * //an square wave frequency modulated by a sawtooth * omniOsc.type = "fmsquare"; * omniOsc.modulationType = "sawtooth"; */ Object.defineProperty(Tone.OmniOscillator.prototype, "type", { get : function(){ var prefix = ""; if (this._sourceType === OmniOscType.FM){ prefix = "fm"; } else if (this._sourceType === OmniOscType.AM){ prefix = "am"; } else if (this._sourceType === OmniOscType.Fat){ prefix = "fat"; } return prefix + this._oscillator.type; }, set : function(type){ if (type.substr(0, 2) === "fm"){ this._createNewOscillator(OmniOscType.FM); this._oscillator.type = type.substr(2); } else if (type.substr(0, 2) === "am"){ this._createNewOscillator(OmniOscType.AM); this._oscillator.type = type.substr(2); } else if (type.substr(0, 3) === "fat"){ this._createNewOscillator(OmniOscType.Fat); this._oscillator.type = type.substr(3); } else if (type === "pwm"){ this._createNewOscillator(OmniOscType.PWM); } else if (type === "pulse"){ this._createNewOscillator(OmniOscType.Pulse); } else { this._createNewOscillator(OmniOscType.Osc); this._oscillator.type = type; } } }); /** * The partials of the waveform. A partial represents * the amplitude at a harmonic. The first harmonic is the * fundamental frequency, the second is the octave and so on * following the harmonic series. * Setting this value will automatically set the type to "custom". * The value is an empty array when the type is not "custom". * This is not available on "pwm" and "pulse" oscillator types. * @memberOf Tone.OmniOscillator# * @type {Array} * @name partials * @example * osc.partials = [1, 0.2, 0.01]; */ Object.defineProperty(Tone.OmniOscillator.prototype, "partials", { get : function(){ return this._oscillator.partials; }, set : function(partials){ this._oscillator.partials = partials; } }); /** * The partial count of the oscillator. This is not available on "pwm" and "pulse" oscillator types. * @memberOf Tone.OmniOscillator# * @type {Number} * @name partialCount * @example * //set the maximum number of partials * osc.partialCount = 0; */ Object.defineProperty(Tone.OmniOscillator.prototype, "partialCount", { get : function(){ return this._oscillator.partialCount; }, set : function(partialCount){ this._oscillator.partialCount = partialCount; } }); /** * Set a member/attribute of the oscillator. * @param {Object|String} params * @param {number=} value * @param {Time=} rampTime * @returns {Tone.OmniOscillator} this */ Tone.OmniOscillator.prototype.set = function(params, value){ //make sure the type is set first if (params === "type"){ this.type = value; } else if (Tone.isObject(params) && params.hasOwnProperty("type")){ this.type = params.type; } //then set the rest Tone.prototype.set.apply(this, arguments); return this; }; /** * Get the object'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. * @param {Array=|string|undefined} params the parameters to get, otherwise will return * all available. * @returns {Object} */ Tone.OmniOscillator.prototype.get = function(params){ var options = this._oscillator.get(params); options.type = this.type; return options; }; /** * connect the oscillator to the frequency and detune signals * @private */ Tone.OmniOscillator.prototype._createNewOscillator = function(oscType){ if (oscType !== this._sourceType){ this._sourceType = oscType; var OscillatorConstructor = Tone[oscType]; //short delay to avoid clicks on the change var now = this.now(); if (this._oscillator !== null){ var oldOsc = this._oscillator; oldOsc.stop(now); //dispose the old one this.context.setTimeout(function(){ oldOsc.dispose(); oldOsc = null; }, this.blockTime); } this._oscillator = new OscillatorConstructor(); this.frequency.connect(this._oscillator.frequency); this.detune.connect(this._oscillator.detune); this._oscillator.connect(this.output); if (this.state === Tone.State.Started){ this._oscillator.start(now); } } }; /** * The phase of the oscillator in degrees. * @memberOf Tone.OmniOscillator# * @type {Degrees} * @name phase */ Object.defineProperty(Tone.OmniOscillator.prototype, "phase", { get : function(){ return this._oscillator.phase; }, set : function(phase){ this._oscillator.phase = phase; } }); /** * The source type names * @private * @type {Object} */ var SourceTypeNames = { PulseOscillator : "pulse", PWMOscillator : "pwm", Oscillator : "oscillator", FMOscillator : "fm", AMOscillator : "am", FatOscillator : "fat" }; /** * The source type of the oscillator. * @memberOf Tone.OmniOscillator# * @type {String} * @name sourceType * @example * var omniOsc = new Tone.OmniOscillator(440, "fmsquare"); * omniOsc.sourceType // 'fm' */ Object.defineProperty(Tone.OmniOscillator.prototype, "sourceType", { get : function(){ return SourceTypeNames[this._sourceType]; }, set : function(sType){ //the basetype defaults to sine var baseType = "sine"; if (this._oscillator.type !== "pwm" && this._oscillator.type !== "pulse"){ baseType = this._oscillator.type; } //set the type if (sType === SourceTypeNames.FMOscillator){ this.type = "fm" + baseType; } else if (sType === SourceTypeNames.AMOscillator){ this.type = "am" + baseType; } else if (sType === SourceTypeNames.FatOscillator){ this.type = "fat" + baseType; } else if (sType === SourceTypeNames.Oscillator){ this.type = baseType; } else if (sType === SourceTypeNames.PulseOscillator){ this.type = "pulse"; } else if (sType === SourceTypeNames.PWMOscillator){ this.type = "pwm"; } } }); /** * The base type of the oscillator. * @memberOf Tone.OmniOscillator# * @type {String} * @name baseType * @example * var omniOsc = new Tone.OmniOscillator(440, "fmsquare4"); * omniOsc.sourceType // 'fm' * omniOsc.baseType //'square' * omniOsc.partialCount //4 */ Object.defineProperty(Tone.OmniOscillator.prototype, "baseType", { get : function(){ return this._oscillator.baseType; }, set : function(baseType){ if (this.sourceType !== SourceTypeNames.PulseOscillator && this.sourceType !== SourceTypeNames.PWMOscillator){ this._oscillator.baseType = baseType; } } }); /** * The width of the oscillator (only if the oscillator is set to "pulse") * @memberOf Tone.OmniOscillator# * @type {NormalRange} * @signal * @name width * @example * var omniOsc = new Tone.OmniOscillator(440, "pulse"); * //can access the width attribute only if type === "pulse" * omniOsc.width.value = 0.2; */ Object.defineProperty(Tone.OmniOscillator.prototype, "width", { get : function(){ if (this._sourceType === OmniOscType.Pulse){ return this._oscillator.width; } else { return undefined; } } }); /** * The number of detuned oscillators * @memberOf Tone.OmniOscillator# * @type {Number} * @name count */ Object.defineProperty(Tone.OmniOscillator.prototype, "count", { get : function(){ if (this._sourceType === OmniOscType.Fat){ return this._oscillator.count; } else { return undefined; } }, set : function(count){ if (this._sourceType === OmniOscType.Fat){ this._oscillator.count = count; } } }); /** * The detune spread between the oscillators. If "count" is * set to 3 oscillators and the "spread" is set to 40, * the three oscillators would be detuned like this: [-20, 0, 20] * for a total detune spread of 40 cents. See Tone.FatOscillator * for more info. * @memberOf Tone.OmniOscillator# * @type {Cents} * @name spread */ Object.defineProperty(Tone.OmniOscillator.prototype, "spread", { get : function(){ if (this._sourceType === OmniOscType.Fat){ return this._oscillator.spread; } else { return undefined; } }, set : function(spread){ if (this._sourceType === OmniOscType.Fat){ this._oscillator.spread = spread; } } }); /** * The type of the modulator oscillator. Only if the oscillator * is set to "am" or "fm" types. see. Tone.AMOscillator or Tone.FMOscillator * for more info. * @memberOf Tone.OmniOscillator# * @type {String} * @name modulationType */ Object.defineProperty(Tone.OmniOscillator.prototype, "modulationType", { get : function(){ if (this._sourceType === OmniOscType.FM || this._sourceType === OmniOscType.AM){ return this._oscillator.modulationType; } else { return undefined; } }, set : function(mType){ if (this._sourceType === OmniOscType.FM || this._sourceType === OmniOscType.AM){ this._oscillator.modulationType = mType; } } }); /** * The modulation index which is in essence the depth or amount of the modulation. In other terms it is the * ratio of the frequency of the modulating signal (mf) to the amplitude of the * modulating signal (ma) -- as in ma/mf. * See Tone.FMOscillator for more info. * @type {Positive} * @signal * @name modulationIndex * @memberof Tone.OmniOscillator# */ Object.defineProperty(Tone.OmniOscillator.prototype, "modulationIndex", { get : function(){ if (this._sourceType === OmniOscType.FM){ return this._oscillator.modulationIndex; } else { return undefined; } } }); /** * Harmonicity is the frequency ratio between the carrier and the modulator oscillators. * A harmonicity of 1 gives both oscillators the same frequency. * Harmonicity = 2 means a change of an octave. See Tone.AMOscillator or Tone.FMOscillator * for more info. * @memberOf Tone.OmniOscillator# * @signal * @type {Positive} * @name harmonicity */ Object.defineProperty(Tone.OmniOscillator.prototype, "harmonicity", { get : function(){ if (this._sourceType === OmniOscType.FM || this._sourceType === OmniOscType.AM){ return this._oscillator.harmonicity; } else { return undefined; } } }); /** * The modulationFrequency Signal of the oscillator * (only if the oscillator type is set to pwm). See * Tone.PWMOscillator for more info. * @memberOf Tone.OmniOscillator# * @type {Frequency} * @signal * @name modulationFrequency * @example * var omniOsc = new Tone.OmniOscillator(440, "pwm"); * //can access the modulationFrequency attribute only if type === "pwm" * omniOsc.modulationFrequency.value = 0.2; */ Object.defineProperty(Tone.OmniOscillator.prototype, "modulationFrequency", { get : function(){ if (this._sourceType === OmniOscType.PWM){ return this._oscillator.modulationFrequency; } else { return undefined; } } }); /** * Clean up. * @return {Tone.OmniOscillator} this */ Tone.OmniOscillator.prototype.dispose = function(){ Tone.Source.prototype.dispose.call(this); this._writable(["frequency", "detune"]); this.detune.dispose(); this.detune = null; this.frequency.dispose(); this.frequency = null; this._oscillator.dispose(); this._oscillator = null; this._sourceType = null; return this; }; export default Tone.OmniOscillator;