diff --git a/.gitignore b/.gitignore
index 7f9316d1..bbe948db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,56 +1,23 @@
-
+.DS_Store
+*.asd
*.scssc
-
-examples/scratch.html
-
*.sublime-workspace
-
*.sublime-project
-# grunt modules
node_modules
-gulp/description
TODO.txt
-
-# all the npm stuff
-utils/npm/Tone
-utils/npm/build/Tone.js
-utils/npm/build/Tone.min.js
-utils/npm/build/Tone.Preset.js
-utils/npm/README.md
-
-utils/jsdoc/*.json
-
-examples/deps/FileSaver.js
-
-examples/oscilloscope.html
-
-.idea
-
wiki
-test/performance
-examples/crashes.html
-
-examples/style/examples.css.map
-
-examples/deps/Tone.dat.gui.js
-
-examples/deps/dat.gui.js
-
-test/mainTest.js
-
-test/Main.js
-
-build/p5.Tone.min.js
-build/p5.Tone.js
-
-.DS_Store
+examples/scratch.html
+examples/deps/FileSaver.js
+examples/oscilloscope.html
examples/graph.html
-*.asd
-
+test/performance
+test/mainTest.js
+test/Main.js
test/supports.html
-
test/coverage/
+
+build/*
diff --git a/.travis.yml b/.travis.yml
index d3e99e79..225eb773 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,46 @@
-sudo: false
-dist: trusty
-language: node_js
-node_js:
- - "8"
-
+sudo: false
+dist: trusty
+language: node_js
+node_js:
+- '8'
addons:
chrome: stable
before_script:
- - cd gulp
- - npm install -g karma
- - npm install -g gulp
- - npm install
- - git config --global user.email "travis@travis-ci.org"
- - git config --global user.name "Travis CI"
+- cd gulp
+- npm install -g jsdoc
+- npm install -g karma
+- npm install -g gulp
+- npm install
+- git config --global user.email "travis@travis-ci.org"
+- git config --global user.name "Travis CI"
script: gulp travis-test
-after_success:
- - sh success.sh
\ No newline at end of file
+after_success:
+- sh success.sh
+before_deploy:
+- node increment_version.js
+- cd ../
+deploy:
+- provider: npm
+ skip_cleanup: true
+ email: yotammann@gmail.com
+ api_key: $NPM_TOKEN
+ tag: next
+ on:
+ repo: Tonejs/Tone.js
+ branch: dev
+# publish without @next when pushing on master
+- provider: npm
+ skip_cleanup: true
+ email: yotammann@gmail.com
+ api_key: $NPM_TOKEN
+ on:
+ repo: Tonejs/Tone.js
+ branch: master
+# publish build files for releases
+- provider: releases
+ api-key: $GH_TOKEN
+ file_glob: true
+ file: build/*
+ skip_cleanup: true
+ on:
+ tags: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50232c43..bbca8406 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,12 +3,16 @@
* [Code coverage](https://coveralls.io/github/Tonejs/Tone.js) analysis
* [Dev build](https://tonejs.github.io/build/dev/Tone.js) with each successful commit
* [Versioned docs](https://tonejs.github.io/docs/Tone) plus a [dev build of the docs](https://tonejs.github.io/docs/dev/Tone) on successful commits
-* Tone.AudioNode is base class for all classes which generate or process audio
+* [Tone.AudioNode](https://tonejs.github.io/docs/AudioNode) is base class for all classes which generate or process audio
* [Tone.Sampler](https://tonejs.github.io/docs/Sampler) simplifies creating multisampled instruments
* [Tone.Solo](https://tonejs.github.io/docs/Solo) makes it easier to mute/solo audio
* [Mixer](https://tonejs.github.io/examples/#mixer) and [sampler](https://tonejs.github.io/examples/#sampler) examples
* Making type-checking methods static
* [Tone.TransportTimelineSignal](https://tonejs.github.io/docs/TransportTimelineSignal) is a signal which can be scheduled along the Transport
+* [Tone.FFT](https://tonejs.github.io/docs/FFT) and [Tone.Waveform](https://tonejs.github.io/docs/Waveform) abstract Tone.Analyser
+* [Tone.Meter](https://tonejs.github.io/docs/Meter) returns decibels
+* [Tone.Envelope](https://tonejs.github.io/docs/Envelope) uses exponential approach instead of exponential curve for decay and release curves
+* [Tone.BufferSource](https://tonejs.github.io/docs/BufferSource) fadeIn/Out can be either "linear" or "exponential" curve
### r10
diff --git a/README.md b/README.md
index 09623e0c..a4c955b4 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ Tone.js is a Web Audio framework for creating interactive music in the browser.
* download [full](https://tonejs.github.io/build/Tone.js) | [min](https://tonejs.github.io/build/Tone.min.js)
* `npm install tone`
+* dev -> `npm install tone@next`
[Full Installation Instruction](https://github.com/Tonejs/Tone.js/wiki/Installation).
@@ -30,7 +31,7 @@ synth.triggerAttackRelease("C4", "8n");
#### Tone.Synth
-[Tone.Synth](https://tonejs.github.io/docs/#Synth) is a basic synthesizer with a single [oscillator](https://tonejs.github.io/docs/#OmniOscillator) and an [ADSR envelope](https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope).
+[Tone.Synth](https://tonejs.github.io/docs/#Synth) is a basic synthesizer with a single [oscillator](https://tonejs.github.io/docs/#OmniOscillator) and an [ADSR envelope](https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope).
#### triggerAttackRelease
@@ -48,7 +49,7 @@ Tone.js abstracts away the AudioContext time. Instead of defining all values in
### Transport
-[Tone.Transport](https://tonejs.github.io/docs/#Transport) is the master timekeeper, allowing for application-wide synchronization and scheduling of sources, signals and events along a shared timeline. Time expressions (like the ones above) are evaluated against the Transport's BPM which can be set like this: `Tone.Transport.bpm.value = 120`.
+[Tone.Transport](https://tonejs.github.io/docs/#Transport) is the master timekeeper, allowing for application-wide synchronization and scheduling of sources, signals and events along a shared timeline. Time expressions (like the ones above) are evaluated against the Transport's BPM which can be set like this: `Tone.Transport.bpm.value = 120`.
### Loops
@@ -101,7 +102,7 @@ var synth = new Tone.Synth({
synth.triggerAttack("D3", "+1");
```
-All instruments are monophonic (one voice) but can be made polyphonic when the constructor is passed in as the second argument to [Tone.PolySynth](https://tonejs.github.io/docs/#PolySynth).
+All instruments are monophonic (one voice) but can be made polyphonic when the constructor is passed in as the second argument to [Tone.PolySynth](https://tonejs.github.io/docs/#PolySynth).
```javascript
//a 4 voice Synth
@@ -114,7 +115,7 @@ polySynth.triggerAttackRelease(["C4", "E4", "G4", "B4"], "2n");
# Effects
-In the above examples, the synthesizer was always connected directly to the [master output](https://tonejs.github.io/docs/#Master), but the output of the synth could also be routed through one (or more) effects before going to the speakers.
+In the above examples, the synthesizer was always connected directly to the [master output](https://tonejs.github.io/docs/#Master), but the output of the synth could also be routed through one (or more) effects before going to the speakers.
```javascript
//create a distortion effect
@@ -138,7 +139,7 @@ var pwm = new Tone.PWMOscillator("Bb3").toMaster().start();
# Signals
-Like the underlying Web Audio API, Tone.js is built with audio-rate signal control over nearly everything. This is a powerful feature which allows for sample-accurate synchronization and scheduling of parameters.
+Like the underlying Web Audio API, Tone.js is built with audio-rate signal control over nearly everything. This is a powerful feature which allows for sample-accurate synchronization and scheduling of parameters.
[Read more](https://github.com/Tonejs/Tone.js/wiki/Signals).
@@ -158,7 +159,7 @@ Tone.js makes extensive use of the native Web Audio Nodes such as the GainNode a
# Contributing
-There are many ways to contribute to Tone.js. Check out [this wiki](https://github.com/Tonejs/Tone.js/wiki/Contributing) if you're interested.
+There are many ways to contribute to Tone.js. Check out [this wiki](https://github.com/Tonejs/Tone.js/wiki/Contributing) if you're interested.
If you have questions (or answers) that are not necessarily bugs/issues, please post them to the [forum](https://groups.google.com/forum/#!forum/tonejs).
diff --git a/Tone/component/Analyser.js b/Tone/component/Analyser.js
index 0f7c02a1..f0e5661a 100644
--- a/Tone/component/Analyser.js
+++ b/Tone/component/Analyser.js
@@ -72,7 +72,7 @@ define(["Tone/core/Tone", "Tone/core/AudioNode"], function (Tone) {
};
/**
- * Possible return types of Tone.Analyser.analyse()
+ * Possible return types of analyser.getValue()
* @enum {String}
*/
Tone.Analyser.Type = {
@@ -85,7 +85,7 @@ define(["Tone/core/Tone", "Tone/core/AudioNode"], function (Tone) {
* result as a TypedArray.
* @returns {TypedArray}
*/
- Tone.Analyser.prototype.analyse = function(){
+ Tone.Analyser.prototype.getValue = function(){
if (this._type === Tone.Analyser.Type.FFT){
this._analyser.getFloatFrequencyData(this._buffer);
} else if (this._type === Tone.Analyser.Type.Waveform){
@@ -111,7 +111,7 @@ define(["Tone/core/Tone", "Tone/core/AudioNode"], function (Tone) {
});
/**
- * The analysis function returned by Tone.Analyser.analyse(), either "fft" or "waveform".
+ * The analysis function returned by analyser.getValue(), either "fft" or "waveform".
* @memberOf Tone.Analyser#
* @type {String}
* @name type
diff --git a/Tone/component/Envelope.js b/Tone/component/Envelope.js
index 508733cd..0014d15d 100644
--- a/Tone/component/Envelope.js
+++ b/Tone/component/Envelope.js
@@ -245,9 +245,9 @@ define(["Tone/core/Tone", "Tone/signal/TimelineSignal",
}
//attack
if (this._attackCurve === "linear"){
- this._sig.linearRampToValue(velocity, attack, time);
+ this._sig.linearRampTo(velocity, attack, time);
} else if (this._attackCurve === "exponential"){
- this._sig.exponentialRampToValue(velocity, attack, time);
+ this._sig.targetRampTo(velocity, attack, time);
} else if (attack > 0){
this._sig.setRampPoint(time);
var curve = this._attackCurve;
@@ -262,7 +262,7 @@ define(["Tone/core/Tone", "Tone/signal/TimelineSignal",
this._sig.setValueCurveAtTime(curve, time, attack, velocity);
}
//decay
- this._sig.exponentialRampToValue(velocity * this.sustain, decay, attack + time);
+ this._sig.targetRampTo(velocity * this.sustain, decay, attack + time);
return this;
};
@@ -280,9 +280,9 @@ define(["Tone/core/Tone", "Tone/signal/TimelineSignal",
if (currentValue > 0){
var release = this.toSeconds(this.release);
if (this._releaseCurve === "linear"){
- this._sig.linearRampToValue(0, release, time);
+ this._sig.linearRampTo(0, release, time);
} else if (this._releaseCurve === "exponential"){
- this._sig.exponentialRampToValue(0, release, time);
+ this._sig.targetRampTo(0, release, time);
} else{
var curve = this._releaseCurve;
if (Tone.isArray(curve)){
diff --git a/Tone/component/FFT.js b/Tone/component/FFT.js
new file mode 100644
index 00000000..8e7bf324
--- /dev/null
+++ b/Tone/component/FFT.js
@@ -0,0 +1,69 @@
+define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], function (Tone) {
+
+ /**
+ * @class Get the current waveform data of the connected audio source.
+ * @extends {Tone.AudioNode}
+ * @param {Number=} size The size of the FFT. Value must be a power of
+ * two in the range 32 to 32768.
+ */
+ Tone.FFT = function(){
+
+ var options = Tone.defaults(arguments, ["size"], Tone.FFT);
+ options.type = Tone.Analyser.Type.FFT;
+ Tone.AudioNode.call(this);
+
+ /**
+ * The analyser node.
+ * @private
+ * @type {Tone.Analyser}
+ */
+ this._analyser = this.input = this.output = new Tone.Analyser(options);
+ };
+
+ Tone.extend(Tone.FFT, Tone.AudioNode);
+
+ /**
+ * The default values.
+ * @type {Object}
+ * @const
+ */
+ Tone.FFT.defaults = {
+ "size" : 1024
+ };
+
+ /**
+ * Gets the waveform of the audio source. Returns the waveform data
+ * of length [size](#size) as a Float32Array with values between -1 and 1.
+ * @returns {TypedArray}
+ */
+ Tone.FFT.prototype.getValue = function(){
+ return this._analyser.getValue();
+ };
+
+ /**
+ * The size of analysis. This must be a power of two in the range 32 to 32768.
+ * @memberOf Tone.FFT#
+ * @type {Number}
+ * @name size
+ */
+ Object.defineProperty(Tone.FFT.prototype, "size", {
+ get : function(){
+ return this._analyser.size;
+ },
+ set : function(size){
+ this._analyser.size = size;
+ }
+ });
+
+ /**
+ * Clean up.
+ * @return {Tone.FFT} this
+ */
+ Tone.FFT.prototype.dispose = function(){
+ Tone.AudioNode.prototype.dispose.call(this);
+ this._analyser.dispose();
+ this._analyser = null;
+ };
+
+ return Tone.FFT;
+});
diff --git a/Tone/component/Meter.js b/Tone/component/Meter.js
index 5b402acd..cd366fbe 100644
--- a/Tone/component/Meter.js
+++ b/Tone/component/Meter.js
@@ -9,36 +9,26 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
*
* @constructor
* @extends {Tone.AudioNode}
- * @param {String} type Either "level" or "signal".
* @param {Number} smoothing The amount of smoothing applied between frames.
* @example
* var meter = new Tone.Meter();
* var mic = new Tone.UserMedia().open();
* //connect mic to the meter
* mic.connect(meter);
- * //the current level of the mic input
- * var level = meter.value;
+ * //the current level of the mic input in decibels
+ * var level = meter.getValue();
*/
Tone.Meter = function(){
- var options = Tone.defaults(arguments, ["type", "smoothing"], Tone.Meter);
+ var options = Tone.defaults(arguments, ["smoothing"], Tone.Meter);
Tone.AudioNode.call(this);
- /**
- * The type of the meter, either "level" or "signal".
- * A "level" meter will return the volume level (rms) of the
- * input signal and a "signal" meter will return
- * the signal value of the input.
- * @type {String}
- */
- this.type = options.type;
-
/**
* The analyser node which computes the levels.
* @private
* @type {Tone.Analyser}
*/
- this.input = this.output = this._analyser = new Tone.Analyser("waveform", 512);
+ this.input = this.output = this._analyser = new Tone.Analyser("waveform", 1024);
/**
* The amount of carryover between the current and last frame.
@@ -46,26 +36,10 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
* @type {Number}
*/
this.smoothing = options.smoothing;
-
- /**
- * The last computed value
- * @type {Number}
- * @private
- */
- this._lastValue = 0;
};
Tone.extend(Tone.Meter, Tone.AudioNode);
- /**
- * @private
- * @enum {String}
- */
- Tone.Meter.Type = {
- Level : "level",
- Signal : "signal"
- };
-
/**
* The defaults
* @type {Object}
@@ -73,39 +47,44 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
* @const
*/
Tone.Meter.defaults = {
- "smoothing" : 0.8,
- "type" : Tone.Meter.Type.Level
+ "smoothing" : 0.8
};
/**
- * The current value of the meter. A value of 1 is
- * "unity".
+ * Get the current decibel value of the incoming signal
+ * @returns {Decibels}
+ */
+ Tone.Meter.prototype.getLevel = function(){
+ this._analyser.type = "fft";
+ var values = this._analyser.getValue();
+ var offset = 28; // normalizes most signal levels
+ // TODO: compute loudness from FFT
+ return Math.max.apply(this, values) + offset;
+ };
+
+ /**
+ * Get the signal value of the incoming signal
+ * @returns {Number}
+ */
+ Tone.Meter.prototype.getValue = function(){
+ this._analyser.type = "waveform";
+ var value = this._analyser.getValue();
+ return value[0];
+ };
+
+ /**
+ * A value from 0 -> 1 where 0 represents no time averaging with the last analysis frame.
* @memberOf Tone.Meter#
* @type {Number}
- * @name value
+ * @name smoothing
* @readOnly
*/
- Object.defineProperty(Tone.Meter.prototype, "value", {
+ Object.defineProperty(Tone.Meter.prototype, "smoothing", {
get : function(){
- var signal = this._analyser.analyse();
- if (this.type === Tone.Meter.Type.Level){
- //rms
- var sum = 0;
- for (var i = 0; i < signal.length; i++){
- sum += Math.pow(signal[i], 2);
- }
- var rms = Math.sqrt(sum / signal.length);
- //smooth it
- rms = Math.max(rms, this._lastValue * this.smoothing);
- this._lastValue = rms;
- //scale it
- var unity = 0.35;
- var val = rms / unity;
- //scale the output curve
- return Math.sqrt(val);
- } else {
- return signal[0];
- }
+ return this._analyser.smoothing;
+ },
+ set : function(val){
+ this._analyser.smoothing = val;
},
});
diff --git a/Tone/component/Waveform.js b/Tone/component/Waveform.js
new file mode 100644
index 00000000..b165e2a8
--- /dev/null
+++ b/Tone/component/Waveform.js
@@ -0,0 +1,68 @@
+define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], function (Tone) {
+
+ /**
+ * @class Get the current waveform data of the connected audio source.
+ * @extends {Tone.AudioNode}
+ * @param {Number=} size The size of the FFT. Value must be a power of
+ * two in the range 32 to 32768.
+ */
+ Tone.Waveform = function(){
+
+ var options = Tone.defaults(arguments, ["size"], Tone.Waveform);
+ options.type = Tone.Analyser.Type.Waveform;
+ Tone.AudioNode.call(this);
+
+ /**
+ * The analyser node.
+ * @private
+ * @type {Tone.Analyser}
+ */
+ this._analyser = this.input = this.output = new Tone.Analyser(options);
+ };
+
+ Tone.extend(Tone.Waveform, Tone.AudioNode);
+
+ /**
+ * The default values.
+ * @type {Object}
+ * @const
+ */
+ Tone.Waveform.defaults = {
+ "size" : 1024
+ };
+
+ /**
+ * Gets the waveform of the audio source. Returns the waveform data
+ * of length [size](#size) as a Float32Array with values between -1 and 1.
+ * @returns {TypedArray}
+ */
+ Tone.Waveform.prototype.getValue = function(){
+ return this._analyser.getValue();
+ };
+
+ /**
+ * The size of analysis. This must be a power of two in the range 32 to 32768.
+ * @memberOf Tone.Waveform#
+ * @type {Number}
+ * @name size
+ */
+ Object.defineProperty(Tone.Waveform.prototype, "size", {
+ get : function(){
+ return this._analyser.size;
+ },
+ set : function(size){
+ this._analyser.size = size;
+ }
+ });
+ /**
+ * Clean up.
+ * @return {Tone.Waveform} this
+ */
+ Tone.Waveform.prototype.dispose = function(){
+ Tone.AudioNode.prototype.dispose.call(this);
+ this._analyser.dispose();
+ this._analyser = null;
+ };
+
+ return Tone.Waveform;
+});
diff --git a/Tone/core/AudioNode.js b/Tone/core/AudioNode.js
index f3a64dfb..01f71245 100644
--- a/Tone/core/AudioNode.js
+++ b/Tone/core/AudioNode.js
@@ -1,7 +1,7 @@
define(["Tone/core/Tone", "Tone/core/Context"], function (Tone) {
/**
- * @class Tone.AudioNode is a base class for classes which process audio.
+ * @class Tone.AudioNode is the base class for classes which process audio.
* AudioNodes have inputs and outputs.
* @param {AudioContext=} context The audio context to use with the class
* @extends {Tone}
@@ -26,7 +26,7 @@ define(["Tone/core/Tone", "Tone/core/Context"], function (Tone) {
/**
* Get the audio context belonging to this instance.
- * @type {AudioNode}
+ * @type {Tone.Context}
* @memberOf Tone.AudioNode#
* @name context
* @readOnly
@@ -144,6 +144,7 @@ define(["Tone/core/Tone", "Tone/core/Context"], function (Tone) {
* node.chain(effect, panVol, Tone.Master);
* @param {...AudioParam|Tone|AudioNode} nodes
* @returns {Tone.AudioNode} this
+ * @private
*/
Tone.AudioNode.prototype.chain = function(){
var currentUnit = this;
@@ -159,6 +160,7 @@ define(["Tone/core/Tone", "Tone/core/Context"], function (Tone) {
* connect the output of this node to the rest of the nodes in parallel.
* @param {...AudioParam|Tone|AudioNode} nodes
* @returns {Tone.AudioNode} this
+ * @private
*/
Tone.AudioNode.prototype.fan = function(){
for (var i = 0; i < arguments.length; i++){
diff --git a/Tone/core/Param.js b/Tone/core/Param.js
index 2f5b32d3..924f0d16 100644
--- a/Tone/core/Param.js
+++ b/Tone/core/Param.js
@@ -168,7 +168,9 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
* freq.setValueAtTime("G4", "+1");
*/
Tone.Param.prototype.setValueAtTime = function(value, time){
- this._param.setValueAtTime(this._fromUnits(value), this.toSeconds(time));
+ time = this.toSeconds(time);
+ Tone.isPast(time);
+ this._param.setValueAtTime(this._fromUnits(value), time);
return this;
};
@@ -182,12 +184,12 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
*/
Tone.Param.prototype.setRampPoint = function(now){
now = Tone.defaultArg(now, this.now());
+ this.cancelAndHoldAtTime(this.context.currentTime);
var currentVal = this._param.value;
- // exponentialRampToValueAt cannot ever ramp from or to 0
- // More info: https://bugzilla.mozilla.org/show_bug.cgi?id=1125600#c2
if (currentVal === 0){
currentVal = this._minOutput;
}
+ // cancel and hold at the given time
this._param.setValueAtTime(currentVal, now);
return this;
};
@@ -202,7 +204,9 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
*/
Tone.Param.prototype.linearRampToValueAtTime = function(value, endTime){
value = this._fromUnits(value);
- this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
+ endTime = this.toSeconds(endTime);
+ Tone.isPast(endTime);
+ this._param.linearRampToValueAtTime(value, endTime);
return this;
};
@@ -217,7 +221,9 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
Tone.Param.prototype.exponentialRampToValueAtTime = function(value, endTime){
value = this._fromUnits(value);
value = Math.max(this._minOutput, value);
- this._param.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
+ endTime = this.toSeconds(endTime);
+ Tone.isPast(endTime);
+ this._param.exponentialRampToValueAtTime(value, endTime);
return this;
};
@@ -233,9 +239,9 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
* @returns {Tone.Param} this
* @example
* //exponentially ramp to the value 2 over 4 seconds.
- * signal.exponentialRampToValue(2, 4);
+ * signal.exponentialRampTo(2, 4);
*/
- Tone.Param.prototype.exponentialRampToValue = function(value, rampTime, startTime){
+ Tone.Param.prototype.exponentialRampTo = function(value, rampTime, startTime){
startTime = this.toSeconds(startTime);
this.setRampPoint(startTime);
this.exponentialRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
@@ -254,15 +260,46 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
* @returns {Tone.Param} this
* @example
* //linearly ramp to the value 4 over 3 seconds.
- * signal.linearRampToValue(4, 3);
+ * signal.linearRampTo(4, 3);
*/
- Tone.Param.prototype.linearRampToValue = function(value, rampTime, startTime){
+ Tone.Param.prototype.linearRampTo = function(value, rampTime, startTime){
startTime = this.toSeconds(startTime);
this.setRampPoint(startTime);
this.linearRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
return this;
};
+ /**
+ * Convert between Time and time constant. The time
+ * constant returned can be used in setTargetAtTime.
+ * @param {Time} time The time to convert
+ * @return {Number} The time constant to get an exponentially approaching
+ * curve to over 99% of towards the target value.
+ */
+ Tone.Param.prototype.getTimeConstant = function(time){
+ return Math.log(this.toSeconds(time)+1)/Math.log(200);
+ };
+
+ /**
+ * Start exponentially approaching the target value at the given time. Since it
+ * is an exponential approach it will continue approaching after the ramp duration. The
+ * rampTime is the time that it takes to reach over 99% of the way towards the value.
+ * @param {number} value The value to ramp to.
+ * @param {Time} rampTime the time that it takes the
+ * value to ramp from it's current value
+ * @param {Time} [startTime=now] When the ramp should start.
+ * @returns {Tone.Param} this
+ * @example
+ * //exponentially ramp to the value 2 over 4 seconds.
+ * signal.exponentialRampTo(2, 4);
+ */
+ Tone.Param.prototype.targetRampTo = function(value, rampTime, startTime){
+ startTime = this.toSeconds(startTime);
+ this.setRampPoint(startTime);
+ this.setTargetAtTime(value, startTime, this.getTimeConstant(rampTime));
+ return this;
+ };
+
/**
* Start exponentially approaching the target value at the given time with
* a rate having the given time constant.
@@ -314,6 +351,31 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
return this;
};
+ /**
+ * This is similar to [cancelScheduledValues](#cancelScheduledValues) except
+ * it holds the automated value at cancelTime until the next automated event.
+ * @param {Time} cancelTime
+ * @returns {Tone.Param} this
+ */
+ Tone.Param.prototype.cancelAndHoldAtTime = function(cancelTime){
+ cancelTime = this.toSeconds(cancelTime);
+ if (this._param.cancelAndHoldAtTime){
+ this._param.cancelAndHoldAtTime(cancelTime);
+ } else {
+ //fallback for unsupported browsers
+ //can't cancel and hold at any time in the future
+ //just do it immediately for gapless automation curves
+ var now = this.context.currentTime;
+ this._param.cancelScheduledValues(now);
+ var currentVal = this._param.value;
+ if (currentVal === 0){
+ currentVal = this._minOutput;
+ }
+ this._param.setValueAtTime(currentVal, now + this.sampleTime);
+ }
+ return this;
+ };
+
/**
* Ramps to the given value over the duration of the rampTime.
* Automatically selects the best ramp type (exponential or linear)
@@ -333,11 +395,11 @@ define(["Tone/core/Tone", "Tone/type/Type"], function(Tone){
* signal.rampTo(0, 10, 5)
*/
Tone.Param.prototype.rampTo = function(value, rampTime, startTime){
- rampTime = Tone.defaultArg(rampTime, 0);
+ rampTime = Tone.defaultArg(rampTime, 0.1);
if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM || this.units === Tone.Type.Decibels){
- this.exponentialRampToValue(value, rampTime, startTime);
+ this.exponentialRampTo(value, rampTime, startTime);
} else {
- this.linearRampToValue(value, rampTime, startTime);
+ this.linearRampTo(value, rampTime, startTime);
}
return this;
};
diff --git a/Tone/core/Timeline.js b/Tone/core/Timeline.js
index 37a90449..38b42274 100644
--- a/Tone/core/Timeline.js
+++ b/Tone/core/Timeline.js
@@ -4,8 +4,8 @@ define(["Tone/core/Tone"], function (Tone) {
/**
* @class A Timeline class for scheduling and maintaining state
- * along a timeline. All events must have a "time" property.
- * Internally, events are stored in time order for fast
+ * along a timeline. All events must have a "time" property.
+ * Internally, events are stored in time order for fast
* retrieval.
* @extends {Tone}
* @param {Positive} [memory=Infinity] The number of previous events that are retained.
@@ -23,12 +23,19 @@ define(["Tone/core/Tone"], function (Tone) {
this._timeline = [];
/**
- * An array of items to remove from the list.
+ * An array of items to remove from the list.
* @type {Array}
* @private
*/
this._toRemove = [];
+ /**
+ * An array of items to add from the list (once it's done iterating)
+ * @type {Array}
+ * @private
+ */
+ this._toAdd = [];
+
/**
* Flag if the timeline is mid iteration
* @private
@@ -70,8 +77,8 @@ define(["Tone/core/Tone"], function (Tone) {
/**
* Insert an event object onto the timeline. Events must have a "time" attribute.
- * @param {Object} event The event object to insert into the
- * timeline.
+ * @param {Object} event The event object to insert into the
+ * timeline.
* @returns {Tone.Timeline} this
*/
Tone.Timeline.prototype.add = function(event){
@@ -79,16 +86,16 @@ define(["Tone/core/Tone"], function (Tone) {
if (Tone.isUndef(event.time)){
throw new Error("Tone.Timeline: events must have a time attribute");
}
- if (this._timeline.length){
+ if (this._iterating){
+ this._toAdd.push(event);
+ } else {
var index = this._search(event.time);
this._timeline.splice(index + 1, 0, event);
- } else {
- this._timeline.push(event);
- }
- //if the length is more than the memory, remove the previous ones
- if (this.length > this.memory){
- var diff = this.length - this.memory;
- this._timeline.splice(0, diff);
+ //if the length is more than the memory, remove the previous ones
+ if (this.length > this.memory){
+ var diff = this.length - this.memory;
+ this._timeline.splice(0, diff);
+ }
}
return this;
};
@@ -113,12 +120,12 @@ define(["Tone/core/Tone"], function (Tone) {
/**
* Get the nearest event whose time is less than or equal to the given time.
* @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
+ * @param {String} comparator Which value in the object to compare
* @returns {Object} The event object set after that time.
*/
- Tone.Timeline.prototype.get = function(time, comparitor){
- comparitor = Tone.defaultArg(comparitor, "time");
- var index = this._search(time, comparitor);
+ Tone.Timeline.prototype.get = function(time, comparator){
+ comparator = Tone.defaultArg(comparator, "time");
+ var index = this._search(time, comparator);
if (index !== -1){
return this._timeline[index];
} else {
@@ -145,12 +152,12 @@ define(["Tone/core/Tone"], function (Tone) {
/**
* Get the event which is scheduled after the given time.
* @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
+ * @param {String} comparator Which value in the object to compare
* @returns {Object} The event object after the given time
*/
- Tone.Timeline.prototype.getAfter = function(time, comparitor){
- comparitor = Tone.defaultArg(comparitor, "time");
- var index = this._search(time, comparitor);
+ Tone.Timeline.prototype.getAfter = function(time, comparator){
+ comparator = Tone.defaultArg(comparator, "time");
+ var index = this._search(time, comparator);
if (index + 1 < this._timeline.length){
return this._timeline[index + 1];
} else {
@@ -161,17 +168,17 @@ define(["Tone/core/Tone"], function (Tone) {
/**
* Get the event before the event at the given time.
* @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
+ * @param {String} comparator Which value in the object to compare
* @returns {Object} The event object before the given time
*/
- Tone.Timeline.prototype.getBefore = function(time, comparitor){
- comparitor = Tone.defaultArg(comparitor, "time");
+ Tone.Timeline.prototype.getBefore = function(time, comparator){
+ comparator = Tone.defaultArg(comparator, "time");
var len = this._timeline.length;
//if it's after the last item, return the last item
- if (len > 0 && this._timeline[len - 1][comparitor] < time){
+ if (len > 0 && this._timeline[len - 1][comparator] < time){
return this._timeline[len - 1];
}
- var index = this._search(time, comparitor);
+ var index = this._search(time, comparator);
if (index - 1 >= 0){
return this._timeline[index - 1];
} else {
@@ -219,11 +226,9 @@ define(["Tone/core/Tone"], function (Tone) {
* @returns {Tone.Timeline} this
*/
Tone.Timeline.prototype.cancelBefore = function(time){
- if (this._timeline.length){
- var index = this._search(time);
- if (index >= 0){
- this._timeline = this._timeline.slice(index + 1);
- }
+ var index = this._search(time);
+ if (index >= 0){
+ this._timeline = this._timeline.slice(index + 1);
}
return this;
};
@@ -243,21 +248,24 @@ define(["Tone/core/Tone"], function (Tone) {
};
/**
- * Does a binary serach on the timeline array and returns the
+ * Does a binary search on the timeline array and returns the
* nearest event index whose time is after or equal to the given time.
* If a time is searched before the first index in the timeline, -1 is returned.
* If the time is after the end, the index of the last item is returned.
- * @param {Number} time
- * @param {String} comparitor Which value in the object to compare
- * @return {Number} the index in the timeline array
+ * @param {Number} time
+ * @param {String} comparator Which value in the object to compare
+ * @return {Number} the index in the timeline array
* @private
*/
- Tone.Timeline.prototype._search = function(time, comparitor){
- comparitor = Tone.defaultArg(comparitor, "time");
+ Tone.Timeline.prototype._search = function(time, comparator){
+ if (this._timeline.length === 0){
+ return -1;
+ }
+ comparator = Tone.defaultArg(comparator, "time");
var beginning = 0;
var len = this._timeline.length;
var end = len;
- if (len > 0 && this._timeline[len - 1][comparitor] <= time){
+ if (len > 0 && this._timeline[len - 1][comparator] <= time){
return len - 1;
}
while (beginning < end){
@@ -265,34 +273,34 @@ define(["Tone/core/Tone"], function (Tone) {
var midPoint = Math.floor(beginning + (end - beginning) / 2);
var event = this._timeline[midPoint];
var nextEvent = this._timeline[midPoint + 1];
- if (event[comparitor] === time){
+ if (event[comparator] === time){
//choose the last one that has the same time
for (var i = midPoint; i < this._timeline.length; i++){
var testEvent = this._timeline[i];
- if (testEvent[comparitor] === time){
+ if (testEvent[comparator] === time){
midPoint = i;
}
}
return midPoint;
- } else if (event[comparitor] < time && nextEvent[comparitor] > time){
+ } else if (event[comparator] < time && nextEvent[comparator] > time){
return midPoint;
- } else if (event[comparitor] > time){
+ } else if (event[comparator] > time){
//search lower
end = midPoint;
} else {
//search upper
beginning = midPoint + 1;
- }
+ }
}
return -1;
};
/**
- * Internal iterator. Applies extra safety checks for
- * removing items from the array.
- * @param {Function} callback
- * @param {Number=} lowerBound
- * @param {Number=} upperBound
+ * Internal iterator. Applies extra safety checks for
+ * removing items from the array.
+ * @param {Function} callback
+ * @param {Number=} lowerBound
+ * @param {Number=} upperBound
* @private
*/
Tone.Timeline.prototype._iterate = function(callback, lowerBound, upperBound){
@@ -303,15 +311,14 @@ define(["Tone/core/Tone"], function (Tone) {
callback.call(this, this._timeline[i]);
}
this._iterating = false;
- if (this._toRemove.length > 0){
- for (var j = 0; j < this._toRemove.length; j++){
- var index = this._timeline.indexOf(this._toRemove[j]);
- if (index !== -1){
- this._timeline.splice(index, 1);
- }
- }
- this._toRemove = [];
- }
+ this._toRemove.forEach(function(event){
+ this.remove(event);
+ }.bind(this));
+ this._toRemove = [];
+ this._toAdd.forEach(function(event){
+ this.add(event);
+ }.bind(this));
+ this._toAdd = [];
};
/**
@@ -353,7 +360,7 @@ define(["Tone/core/Tone"], function (Tone) {
};
/**
- * Iterate over everything in the array at or after the given time. Similar to
+ * Iterate over everything in the array at or after the given time. Similar to
* forEachAfter, but includes the item(s) at the given time.
* @param {Number} time The time to check if items are before
* @param {Function} callback The callback to invoke with every item
@@ -383,7 +390,7 @@ define(["Tone/core/Tone"], function (Tone) {
this._iterate(function(event){
if (event.time === time){
callback.call(this, event);
- }
+ }
}, 0, upperBound);
}
return this;
@@ -397,8 +404,9 @@ define(["Tone/core/Tone"], function (Tone) {
Tone.prototype.dispose.call(this);
this._timeline = null;
this._toRemove = null;
+ this._toAdd = null;
return this;
};
return Tone.Timeline;
-});
\ No newline at end of file
+});
diff --git a/Tone/core/Tone.js b/Tone/core/Tone.js
index 149ffc29..fd4086bf 100644
--- a/Tone/core/Tone.js
+++ b/Tone/core/Tone.js
@@ -508,6 +508,16 @@ define(function(){
return Tone.context.now();
};
+ /**
+ * Adds warning in the console if the scheduled time has passed.
+ * @type {Time}
+ */
+ Tone.isPast = function(time){
+ if (time < Tone.context.currentTime){
+ console.warn("Time '" + time + "' is in the past. Scheduled time must be ≥ AudioContext.currentTime");
+ }
+ };
+
///////////////////////////////////////////////////////////////////////////
// INHERITANCE
///////////////////////////////////////////////////////////////////////////
@@ -680,7 +690,7 @@ define(function(){
* @type {String}
* @static
*/
- Tone.version = "r11";
+ Tone.version = "r11-dev";
// allow optional silencing of this log
if (!window.TONE_SILENCE_VERSION_LOGGING) {
diff --git a/Tone/core/Transport.js b/Tone/core/Transport.js
index 42d9cf4e..715ddcf4 100644
--- a/Tone/core/Transport.js
+++ b/Tone/core/Transport.js
@@ -1,5 +1,6 @@
-define(["Tone/core/Tone", "Tone/core/Clock", "Tone/type/Type", "Tone/core/Timeline",
- "Tone/core/Emitter", "Tone/core/Gain", "Tone/core/IntervalTimeline"],
+define(["Tone/core/Tone", "Tone/core/Clock", "Tone/type/Type", "Tone/core/Timeline",
+ "Tone/core/Emitter", "Tone/core/Gain", "Tone/core/IntervalTimeline",
+ "Tone/core/TransportRepeatEvent", "Tone/core/TransportEvent"],
function(Tone){
"use strict";
@@ -10,10 +11,10 @@ function(Tone){
* Tone.Transport timing events pass in the exact time of the scheduled event
* in the argument of the callback function. Pass that time value to the object
* you're scheduling.
- * A single transport is created for you when the library is initialized.
+ * A single transport is created for you when the library is initialized.
*
* The transport emits the events: "start", "stop", "pause", and "loop" which are
- * called with the time of that event as the argument.
+ * called with the time of that event as the argument.
*
* @extends {Tone.Emitter}
* @singleton
@@ -38,20 +39,20 @@ function(Tone){
// LOOPING
//////////////////////////////////////////////////////////////////////
- /**
+ /**
* If the transport loops or not.
* @type {boolean}
*/
this.loop = false;
- /**
+ /**
* The loop start position in ticks
* @type {Ticks}
* @private
*/
this._loopStart = 0;
- /**
+ /**
* The loop end position in ticks
* @type {Ticks}
* @private
@@ -76,14 +77,14 @@ function(Tone){
* @type {Tone.Clock}
*/
this._clock = new Tone.Clock({
- "callback" : this._processTick.bind(this),
+ "callback" : this._processTick.bind(this),
"frequency" : 0,
});
this._bindClockEvents();
/**
- * The Beats Per Minute of the Transport.
+ * The Beats Per Minute of the Transport.
* @type {BPM}
* @signal
* @example
@@ -100,7 +101,7 @@ function(Tone){
/**
* The time signature, or more accurately the numerator
- * of the time signature over a denominator of 4.
+ * of the time signature over a denominator of 4.
* @type {Number}
* @private
*/
@@ -117,13 +118,6 @@ function(Tone){
*/
this._scheduledEvents = {};
- /**
- * The event ID counter
- * @type {Number}
- * @private
- */
- this._eventID = 0;
-
/**
* The scheduled events.
* @type {Tone.Timeline}
@@ -139,15 +133,8 @@ function(Tone){
this._repeatedEvents = new Tone.IntervalTimeline();
/**
- * Events that occur once
- * @type {Array}
- * @private
- */
- this._onceEvents = new Tone.Timeline();
-
- /**
* All of the synced Signals
- * @private
+ * @private
* @type {Array}
*/
this._syncedSignals = [];
@@ -171,7 +158,6 @@ function(Tone){
this._swingAmount = 0;
}.bind(this));
-
};
Tone.extend(Tone.Transport, Tone.Emitter);
@@ -204,14 +190,14 @@ function(Tone){
Tone.Transport.prototype._processTick = function(tickTime){
var ticks = this._clock.ticks;
//handle swing
- if (this._swingAmount > 0 &&
+ if (this._swingAmount > 0 &&
ticks % this._ppq !== 0 && //not on a downbeat
ticks % (this._swingTicks * 2) !== 0){
//add some swing
var progress = (ticks % (this._swingTicks * 2)) / (this._swingTicks * 2);
var amount = Math.sin((progress) * Math.PI) * this._swingAmount;
tickTime += Tone.Time(this._swingTicks * 2/3, "i") * amount;
- }
+ }
//do the loop test
if (this.loop){
if (ticks >= this._loopEnd){
@@ -222,23 +208,9 @@ function(Tone){
this.emit("loop", tickTime);
}
}
- //process the single occurrence events
- this._onceEvents.forEachBefore(ticks, function(event){
- event.callback(tickTime);
- //remove the event
- delete this._scheduledEvents[event.id.toString()];
- }.bind(this));
- //and clear the single occurrence timeline
- this._onceEvents.cancelBefore(ticks);
- //fire the next tick events if their time has come
+ //invoke the timeline events scheduled on this tick
this._timeline.forEachAtTime(ticks, function(event){
- event.callback(tickTime);
- });
- //process the repeated events
- this._repeatedEvents.forEachAtTime(ticks, function(event){
- if ((ticks - event.time) % event.interval === 0){
- event.callback(tickTime);
- }
+ event.invoke(tickTime);
});
};
@@ -250,7 +222,7 @@ function(Tone){
* Schedule an event along the timeline.
* @param {Function} callback The callback to be invoked at the time.
* @param {TransportTime} time The time to invoke the callback at.
- * @return {Number} The id of the event which can be used for canceling the event.
+ * @return {Number} The id of the event which can be used for canceling the event.
* @example
* //trigger the callback when the Transport reaches the desired time
* Tone.Transport.schedule(function(time){
@@ -258,75 +230,55 @@ function(Tone){
* }, "128i");
*/
Tone.Transport.prototype.schedule = function(callback, time){
- var event = {
+ var event = new Tone.TransportEvent(this, {
"time" : this.toTicks(time),
"callback" : callback
- };
- var id = this._eventID++;
- this._scheduledEvents[id.toString()] = {
- "event" : event,
- "timeline" : this._timeline
- };
- this._timeline.add(event);
- return id;
+ });
+ return this._addEvent(event, this._timeline);
};
/**
* Schedule a repeated event along the timeline. The event will fire
* at the `interval` starting at the `startTime` and for the specified
- * `duration`.
+ * `duration`.
* @param {Function} callback The callback to invoke.
* @param {Time} interval The duration between successive
- * callbacks.
+ * callbacks. Must be a positive number.
* @param {TimelinePosition=} startTime When along the timeline the events should
* start being invoked.
- * @param {Time} [duration=Infinity] How long the event should repeat.
+ * @param {Time} [duration=Infinity] How long the event should repeat.
* @return {Number} The ID of the scheduled event. Use this to cancel
- * the event.
+ * the event.
* @example
* //a callback invoked every eighth note after the first measure
* Tone.Transport.scheduleRepeat(callback, "8n", "1m");
*/
Tone.Transport.prototype.scheduleRepeat = function(callback, interval, startTime, duration){
- if (interval <= 0){
- throw new Error("Tone.Transport: repeat events must have an interval larger than 0");
- }
- var event = {
+ var event = new Tone.TransportRepeatEvent(this, {
+ "callback" : callback,
+ "interval" : this.toTicks(interval),
"time" : this.toTicks(startTime),
"duration" : this.toTicks(Tone.defaultArg(duration, Infinity)),
- "interval" : this.toTicks(interval),
- "callback" : callback
- };
- var id = this._eventID++;
- this._scheduledEvents[id.toString()] = {
- "event" : event,
- "timeline" : this._repeatedEvents
- };
- this._repeatedEvents.add(event);
- return id;
+ });
+ //kick it off if the Transport is started
+ return this._addEvent(event, this._repeatedEvents);
};
/**
- * Schedule an event that will be removed after it is invoked.
- * Note that if the given time is less than the current transport time,
- * the event will be invoked immediately.
+ * Schedule an event that will be removed after it is invoked.
+ * Note that if the given time is less than the current transport time,
+ * the event will be invoked immediately.
* @param {Function} callback The callback to invoke once.
* @param {TransportTime} time The time the callback should be invoked.
- * @returns {Number} The ID of the scheduled event.
+ * @returns {Number} The ID of the scheduled event.
*/
Tone.Transport.prototype.scheduleOnce = function(callback, time){
- var id = this._eventID++;
- var event = {
+ var event = new Tone.TransportEvent(this, {
"time" : this.toTicks(time),
"callback" : callback,
- "id" : id
- };
- this._scheduledEvents[id.toString()] = {
- "event" : event,
- "timeline" : this._onceEvents
- };
- this._onceEvents.add(event);
- return id;
+ "once" : true
+ });
+ return this._addEvent(event, this._timeline);
};
/**
@@ -338,24 +290,41 @@ function(Tone){
if (this._scheduledEvents.hasOwnProperty(eventId)){
var item = this._scheduledEvents[eventId.toString()];
item.timeline.remove(item.event);
+ item.event.dispose();
delete this._scheduledEvents[eventId.toString()];
}
return this;
};
+ /**
+ * Add an event to the correct timeline. Keep track of the
+ * timeline it was added to.
+ * @param {Tone.TransportEvent} event
+ * @param {Tone.Timeline} timeline
+ * @returns {Number} the event id which was just added
+ * @private
+ */
+ Tone.Transport.prototype._addEvent = function(event, timeline){
+ this._scheduledEvents[event.id.toString()] = {
+ "event" : event,
+ "timeline" : timeline
+ };
+ timeline.add(event);
+ return event.id;
+ };
+
/**
* Remove scheduled events from the timeline after
* the given time. Repeated events will be removed
* if their startTime is after the given time
* @param {TransportTime} [after=0] Clear all events after
- * this time.
+ * this time.
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.cancel = function(after){
after = Tone.defaultArg(after, 0);
after = this.toTicks(after);
this._timeline.cancel(after);
- this._onceEvents.cancel(after);
this._repeatedEvents.cancel(after);
return this;
};
@@ -402,7 +371,7 @@ function(Tone){
* @param {TransportTime=} offset The timeline offset to start the transport.
* @returns {Tone.Transport} this
* @example
- * //start the transport in one second starting at beginning of the 5th measure.
+ * //start the transport in one second starting at beginning of the 5th measure.
* Tone.Transport.start("+1", "4:0:0");
*/
Tone.Transport.prototype.start = function(time, offset){
@@ -416,7 +385,7 @@ function(Tone){
/**
* Stop the transport and all sources synced to the transport.
- * @param {Time} [time=now] The time when the transport should stop.
+ * @param {Time} [time=now] The time when the transport should stop.
* @returns {Tone.Transport} this
* @example
* Tone.Transport.stop();
@@ -457,7 +426,7 @@ function(Tone){
///////////////////////////////////////////////////////////////////////////////
/**
- * The time signature as just the numerator over 4.
+ * The time signature as just the numerator over 4.
* For example 4/4 would be just 4 and 6/8 would be 3.
* @memberOf Tone.Transport#
* @type {Number|Array}
@@ -514,9 +483,9 @@ function(Tone){
});
/**
- * Set the loop start and stop at the same time.
- * @param {TransportTime} startPosition
- * @param {TransportTime} endPosition
+ * Set the loop start and stop at the same time.
+ * @param {TransportTime} startPosition
+ * @param {TransportTime} endPosition
* @returns {Tone.Transport} this
* @example
* //loop over the first measure
@@ -530,7 +499,7 @@ function(Tone){
};
/**
- * The swing value. Between 0-1 where 1 equal to
+ * The swing value. Between 0-1 where 1 equal to
* the note + half the subdivision.
* @memberOf Tone.Transport#
* @type {NormalRange}
@@ -547,10 +516,10 @@ function(Tone){
});
/**
- * Set the subdivision which the swing will be applied to.
- * The default value is an 8th note. Value must be less
+ * Set the subdivision which the swing will be applied to.
+ * The default value is an 8th note. Value must be less
* than a quarter note.
- *
+ *
* @memberOf Tone.Transport#
* @type {Time}
* @name swingSubdivision
@@ -566,7 +535,7 @@ function(Tone){
/**
* The Transport's position in Bars:Beats:Sixteenths.
- * Setting the value will jump to that position right away.
+ * Setting the value will jump to that position right away.
* @memberOf Tone.Transport#
* @type {BarsBeatsSixteenths}
* @name position
@@ -583,7 +552,7 @@ function(Tone){
/**
* The Transport's position in seconds
- * Setting the value will jump to that position right away.
+ * Setting the value will jump to that position right away.
* @memberOf Tone.Transport#
* @type {Seconds}
* @name seconds
@@ -600,7 +569,7 @@ function(Tone){
/**
* The Transport's loop position as a normalized value. Always
- * returns 0 if the transport if loop is not true.
+ * returns 0 if the transport if loop is not true.
* @memberOf Tone.Transport#
* @name progress
* @type {NormalRange}
@@ -617,7 +586,7 @@ function(Tone){
/**
* The transports current tick position.
- *
+ *
* @memberOf Tone.Transport#
* @type {Ticks}
* @name ticks
@@ -645,9 +614,9 @@ function(Tone){
/**
* Pulses Per Quarter note. This is the smallest resolution
* the Transport timing supports. This should be set once
- * on initialization and not set again. Changing this value
- * after other objects have been created can cause problems.
- *
+ * on initialization and not set again. Changing this value
+ * after other objects have been created can cause problems.
+ *
* @memberOf Tone.Transport#
* @type {Number}
* @name PPQ
@@ -716,14 +685,14 @@ function(Tone){
};
/**
- * Attaches the signal to the tempo control signal so that
+ * Attaches the signal to the tempo control signal so that
* any changes in the tempo will change the signal in the same
- * ratio.
- *
- * @param {Tone.Signal} signal
+ * ratio.
+ *
+ * @param {Tone.Signal} signal
* @param {number=} ratio Optionally pass in the ratio between
* the two signals. Otherwise it will be computed
- * based on their current values.
+ * based on their current values.
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.syncSignal = function(signal, ratio){
@@ -747,9 +716,9 @@ function(Tone){
};
/**
- * Unsyncs a previously synced signal from the transport's control.
+ * Unsyncs a previously synced signal from the transport's control.
* See Tone.Transport.syncSignal.
- * @param {Tone.Signal} signal
+ * @param {Tone.Signal} signal
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.unsyncSignal = function(signal){
@@ -765,7 +734,7 @@ function(Tone){
};
/**
- * Clean up.
+ * Clean up.
* @returns {Tone.Transport} this
* @private
*/
@@ -777,8 +746,6 @@ function(Tone){
this.bpm = null;
this._timeline.dispose();
this._timeline = null;
- this._onceEvents.dispose();
- this._onceEvents = null;
this._repeatedEvents.dispose();
this._repeatedEvents = null;
return this;
diff --git a/Tone/core/TransportEvent.js b/Tone/core/TransportEvent.js
new file mode 100644
index 00000000..ac41a1ec
--- /dev/null
+++ b/Tone/core/TransportEvent.js
@@ -0,0 +1,92 @@
+define(["Tone/core/Tone"], function(Tone){
+
+ /**
+ * @class Tone.TransportEvent is an internal class used by (Tone.Transport)[Transport]
+ * to schedule events. Do no invoke this class directly, it is
+ * handled from within Tone.Transport.
+ * @extends {Tone}
+ * @param {Object} options
+ */
+ Tone.TransportEvent = function(Transport, options){
+
+ options = Tone.defaultArg(options, Tone.TransportEvent.defaults);
+ Tone.call(this);
+
+ /**
+ * Reference to the Transport that created it
+ * @type {Tone.Transport}
+ */
+ this.Transport = Transport;
+
+ /**
+ * The unique id of the event
+ * @type {Number}
+ */
+ this.id = Tone.TransportEvent._eventId++;
+
+ /**
+ * The time the event starts
+ * @type {Ticks}
+ */
+ this.time = options.time;
+
+ /**
+ * The callback to invoke
+ * @type {Function}
+ */
+ this.callback = options.callback;
+
+ /**
+ * If the event should be removed after being created.
+ * @type {Boolean}
+ * @private
+ */
+ this._once = options.once;
+ };
+
+ Tone.extend(Tone.TransportEvent);
+
+ /**
+ * The defaults
+ * @static
+ * @type {Object}
+ */
+ Tone.TransportEvent.defaults = {
+ "once" : false,
+ "callback" : Tone.noOp,
+ };
+
+ /**
+ * Current ID counter
+ * @private
+ * @static
+ * @type {Number}
+ */
+ Tone.TransportEvent._eventId = 0;
+
+ /**
+ * Invoke the callback even callback.
+ * @param {Time} time The AudioContext time in seconds of the event
+ */
+ Tone.TransportEvent.prototype.invoke = function(time){
+ if (this.callback){
+ this.callback(time);
+ if (this._once && this.Transport){
+ this.Transport.clear(this.id);
+ }
+ }
+ };
+
+ /**
+ * Clean up
+ * @return {Tone.TransportEvent} this
+ */
+ Tone.TransportEvent.prototype.dispose = function(){
+ Tone.prototype.dispose.call(this);
+ this.Transport = null;
+ this.callback = null;
+ return this;
+ };
+
+ return Tone.TransportEvent;
+});
diff --git a/Tone/core/TransportRepeatEvent.js b/Tone/core/TransportRepeatEvent.js
new file mode 100644
index 00000000..997280fa
--- /dev/null
+++ b/Tone/core/TransportRepeatEvent.js
@@ -0,0 +1,130 @@
+define(["Tone/core/Tone", "Tone/core/TransportEvent"], function(Tone){
+
+ /**
+ * @class Tone.TransportRepeatEvent is an internal class used by Tone.Transport
+ * to schedule repeat events. This class should not be instantiated directly.
+ * @extends {Tone.TransportEvent}
+ * @param {Object} options
+ */
+ Tone.TransportRepeatEvent = function(Transport, options){
+
+ Tone.TransportEvent.call(this, Transport, options);
+ options = Tone.defaultArg(options, Tone.TransportRepeatEvent.defaults);
+
+
+ /**
+ * When the event should stop repeating
+ * @type {Ticks}
+ * @private
+ */
+ this.duration = options.duration;
+
+ /**
+ * The interval of the repeated event
+ * @type {Ticks}
+ * @private
+ */
+ this._interval = options.interval;
+
+ /**
+ * The ID of the current timeline event
+ * @type {Number}
+ * @private
+ */
+ this._currentId = -1;
+
+ /**
+ * The ID of the next timeline event
+ * @type {Number}
+ * @private
+ */
+ this._nextId = -1;
+
+ /**
+ * The time of the next event
+ * @type {Ticks}
+ * @private
+ */
+ this._nextTick = this.time;
+
+ /**
+ * a reference to the bound start method
+ * @type {Function}
+ * @private
+ */
+ this._boundRestart = this._restart.bind(this);
+ this.Transport.on("start loopStart", this._boundRestart);
+ this._restart();
+ };
+
+ Tone.extend(Tone.TransportRepeatEvent, Tone.TransportEvent);
+
+ /**
+ * The defaults
+ * @static
+ * @type {Object}
+ */
+ Tone.TransportRepeatEvent.defaults = {
+ "duration" : Infinity,
+ "interval" : 1
+ };
+
+ /**
+ * Invoke the callback. Returns the tick time which
+ * the next event should be scheduled at.
+ * @param {Number} time The AudioContext time in seconds of the event
+ */
+ Tone.TransportRepeatEvent.prototype.invoke = function(time){
+ //create more events if necessary
+ this._createEvents();
+ //call the super class
+ Tone.TransportEvent.prototype.invoke.call(this, time);
+ };
+
+ /**
+ * Push more events onto the timeline to keep up with the position of the timeline
+ * @private
+ */
+ Tone.TransportRepeatEvent.prototype._createEvents = function(){
+ // schedule the next event
+ var ticks = this.Transport.ticks;
+ if (ticks >= this.time && ticks >= this._nextTick &&
+ this._nextTick + this._interval < this.time + this.duration){
+ this._nextTick += this._interval;
+ this._currentId = this._nextId;
+ this._nextId = this.Transport.scheduleOnce(this.invoke.bind(this), Tone.TransportTime(this._nextTick, "i"));
+ }
+ };
+
+ /**
+ * Push more events onto the timeline to keep up with the position of the timeline
+ * @private
+ */
+ Tone.TransportRepeatEvent.prototype._restart = function(){
+ this.Transport.clear(this._currentId);
+ this.Transport.clear(this._nextId);
+ var ticks = this.Transport.ticks;
+ this._nextTick = this.time;
+ if (ticks > this.time){
+ this._nextTick = this.time + Math.ceil((ticks - this.time) / this._interval) * this._interval;
+ }
+ this._currentId = this.Transport.scheduleOnce(this.invoke.bind(this), Tone.TransportTime(this._nextTick, "i"));
+ this._nextTick += this._interval;
+ this._nextId = this.Transport.scheduleOnce(this.invoke.bind(this), Tone.TransportTime(this._nextTick, "i"));
+ };
+
+ /**
+ * Clean up
+ * @return {Tone.TransportRepeatEvent} this
+ */
+ Tone.TransportRepeatEvent.prototype.dispose = function(){
+ this.Transport.clear(this._currentId);
+ this.Transport.clear(this._nextId);
+ this.Transport.off("start loopStart", this._boundRestart);
+ this._boundCreateEvents = null;
+ Tone.TransportEvent.prototype.dispose.call(this);
+ return this;
+ };
+
+ return Tone.TransportRepeatEvent;
+});
diff --git a/Tone/instrument/MetalSynth.js b/Tone/instrument/MetalSynth.js
index b796b148..398a4bff 100644
--- a/Tone/instrument/MetalSynth.js
+++ b/Tone/instrument/MetalSynth.js
@@ -102,7 +102,7 @@ define(["Tone/core/Tone", "Tone/instrument/Instrument", "Tone/source/FMOscillato
"harmonicity" : options.harmonicity,
"modulationIndex" : options.modulationIndex
});
- osc.connect(this._highpass).start(0);
+ osc.connect(this._highpass).start();
this._oscillators[i] = osc;
var mult = new Tone.Multiply(inharmRatios[i]);
diff --git a/Tone/instrument/Sampler.js b/Tone/instrument/Sampler.js
index 12d6a770..b4cc0c0e 100644
--- a/Tone/instrument/Sampler.js
+++ b/Tone/instrument/Sampler.js
@@ -121,7 +121,8 @@ define(["Tone/core/Tone", "Tone/instrument/Instrument", "Tone/core/Buffers", "To
"buffer" : buffer,
"playbackRate" : Tone.intervalToFrequencyRatio(difference),
"fadeIn" : this.attack,
- "fadeOut" : this.release
+ "fadeOut" : this.release,
+ "curve" : "exponential",
}).connect(this.output);
source.start(time, 0, buffer.duration, velocity);
// add it to the active sources
diff --git a/Tone/signal/TimelineSignal.js b/Tone/signal/TimelineSignal.js
index c6f4a5d4..f677634c 100644
--- a/Tone/signal/TimelineSignal.js
+++ b/Tone/signal/TimelineSignal.js
@@ -3,7 +3,7 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
"use strict";
/**
- * @class A signal which adds the method getValueAtTime.
+ * @class A signal which adds the method getValueAtTime.
* Code and inspiration from https://github.com/jsantell/web-audio-automation-timeline
* @extends {Tone.Signal}
* @param {Number=} value The initial value of the signal
@@ -13,7 +13,7 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
var options = Tone.defaults(arguments, ["value", "units"], Tone.Signal);
Tone.Signal.call(this, options);
-
+
/**
* The scheduled events
* @type {Tone.Timeline}
@@ -48,7 +48,7 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
/**
- * The current value of the signal.
+ * The current value of the signal.
* @memberOf Tone.TimelineSignal#
* @type {Number}
* @name value
@@ -79,7 +79,7 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
* @param {Time} time The time when the change should occur.
* @returns {Tone.TimelineSignal} this
* @example
- * //set the frequency to "G4" in exactly 1 second from now.
+ * //set the frequency to "G4" in exactly 1 second from now.
* freq.setValueAtTime("G4", "+1");
*/
Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
@@ -96,11 +96,11 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
/**
- * Schedules a linear continuous change in parameter value from the
+ * Schedules a linear continuous change in parameter value from the
* previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
+ *
+ * @param {number} value
+ * @param {Time} endTime
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
@@ -116,11 +116,11 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
/**
- * Schedules an exponential continuous change in parameter value from
+ * Schedules an exponential continuous change in parameter value from
* the previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
+ *
+ * @param {number} value
+ * @param {Time} endTime
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
@@ -151,10 +151,10 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
/**
* Start exponentially approaching the target value at the given time with
* a rate having the given time constant.
- * @param {number} value
- * @param {Time} startTime
- * @param {number} timeConstant
- * @returns {Tone.TimelineSignal} this
+ * @param {number} value
+ * @param {Time} startTime
+ * @param {number} timeConstant
+ * @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
value = this._fromUnits(value);
@@ -173,11 +173,11 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
/**
* Set an array of arbitrary values starting at the given time for the given duration.
- * @param {Float32Array} values
- * @param {Time} startTime
+ * @param {Float32Array} values
+ * @param {Time} startTime
* @param {Time} duration
* @param {NormalRange} [scaling=1] If the values in the curve should be scaled by some value
- * @returns {Tone.TimelineSignal} this
+ * @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.setValueCurveAtTime = function (values, startTime, duration, scaling) {
scaling = Tone.defaultArg(scaling, 1);
@@ -192,9 +192,8 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
/**
- * Cancels all scheduled parameter changes with times greater than or
+ * Cancels all scheduled parameter changes with times greater than or
* equal to startTime.
- *
* @param {Time} startTime
* @returns {Tone.TimelineSignal} this
*/
@@ -205,13 +204,25 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
return this;
};
+ /**
+ * Cancels all scheduled parameter changes with times greater than or
+ * equal to cancelTime and sets the output of the signal to be the value
+ * at cancelTime. Similar to (cancelScheduledValues)[#cancelscheduledvalues].
+ * @param {Time} cancelTime
+ * @returns {Tone.TimelineSignal} this
+ */
+ Tone.TimelineSignal.prototype.cancelAndHoldAtTime = function (cancelTime) {
+ this.setRampPoint(this.toSeconds(cancelTime));
+ return this;
+ };
+
/**
* Sets the computed value at the given time. This provides
* a point from which a linear or exponential curve
- * can be scheduled after. Will cancel events after
+ * can be scheduled after. Will cancel events after
* the given time and shorten the currently scheduled
* linear or exponential ramp so that it ends at `time` .
- * This is to avoid discontinuities and clicks in envelopes.
+ * This is to avoid discontinuities and clicks in envelopes.
* @param {Time} time When to set the ramp point
* @returns {Tone.TimelineSignal} this
*/
@@ -237,8 +248,8 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
this.exponentialRampToValueAtTime(val, time);
}
}
- this.setValueAtTime(val, time);
}
+ this.setValueAtTime(val, time);
return this;
};
@@ -310,13 +321,13 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
value = this._initial;
} else if (before.type === Tone.TimelineSignal.Type.Target){
var previous = this._events.getBefore(before.time);
- var previouVal;
+ var previousVal;
if (previous === null){
- previouVal = this._initial;
+ previousVal = this._initial;
} else {
- previouVal = previous.value;
+ previousVal = previous.value;
}
- value = this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
+ value = this._exponentialApproach(before.time, previousVal, before.value, before.constant, time);
} else if (after === null){
value = before.value;
} else if (after.type === Tone.TimelineSignal.Type.Linear){
@@ -330,12 +341,12 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
/**
- * When signals connect to other signals or AudioParams,
- * they take over the output value of that signal or AudioParam.
- * For all other nodes, the behavior is the same as a default connect
.
+ * When signals connect to other signals or AudioParams,
+ * they take over the output value of that signal or AudioParam.
+ * For all other nodes, the behavior is the same as a default connect
.
*
* @override
- * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
+ * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
* @param {number} [outputNumber=0] The output number to connect from.
* @param {number} [inputNumber=0] The input number to connect to.
* @returns {Tone.TimelineSignal} this
@@ -385,4 +396,4 @@ define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function
};
return Tone.TimelineSignal;
-});
\ No newline at end of file
+});
diff --git a/Tone/source/BufferSource.js b/Tone/source/BufferSource.js
index aa6637a2..8e0f8297 100644
--- a/Tone/source/BufferSource.js
+++ b/Tone/source/BufferSource.js
@@ -82,6 +82,12 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
*/
this.fadeOut = options.fadeOut;
+ /**
+ * The curve applied to the fades, either "linear" or "exponential"
+ * @type {String}
+ */
+ this.curve = options.curve;
+
/**
* The value that the buffer ramps to
* @type {Gain}
@@ -96,6 +102,7 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
*/
this._onendedTimeout = -1;
+ //set some values initially
this.loop = options.loop;
this.loopStart = options.loopStart;
this.loopEnd = options.loopEnd;
@@ -117,6 +124,7 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
"loopEnd" : 0,
"fadeIn" : 0,
"fadeOut" : 0,
+ "curve" : "linear",
"playbackRate" : 1
};
@@ -165,30 +173,27 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
offset = Tone.defaultArg(offset, 0);
}
offset = this.toSeconds(offset);
- //the values in seconds
- time = this.toSeconds(time);
gain = Tone.defaultArg(gain, 1);
this._gain = gain;
- //the fadeIn time
- if (Tone.isUndef(fadeInTime)){
- fadeInTime = this.toSeconds(this.fadeIn);
- } else {
- fadeInTime = this.toSeconds(fadeInTime);
- }
+ fadeInTime = this.toSeconds(Tone.defaultArg(fadeInTime, this.fadeIn));
+ this.fadeIn = fadeInTime;
if (fadeInTime > 0){
this._gainNode.gain.setValueAtTime(0, time);
- this._gainNode.gain.linearRampToValueAtTime(this._gain, time + fadeInTime);
+ if (this.curve === "linear"){
+ this._gainNode.gain.linearRampToValueAtTime(this._gain, time + fadeInTime);
+ } else {
+ this._gainNode.gain.setTargetAtTime(this._gain, time, this._gainNode.gain.getTimeConstant(fadeInTime));
+ }
} else {
this._gainNode.gain.setValueAtTime(gain, time);
}
- this._startTime = time + fadeInTime;
+ this._startTime = time;
- var computedDur = Tone.defaultArg(duration, this.buffer.duration - offset);
- computedDur = this.toSeconds(computedDur);
+ var computedDur = this.toSeconds(Tone.defaultArg(duration, this.buffer.duration - offset));
computedDur = Math.max(computedDur, 0);
if (!this.loop || (this.loop && !Tone.isUndef(duration))){
@@ -196,7 +201,7 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
if (!this.loop){
computedDur = Math.min(computedDur, this.buffer.duration - offset);
}
- this.stop(time + computedDur + fadeInTime, this.fadeOut);
+ this.stop(time + computedDur, this.fadeOut);
}
//start the buffer source
@@ -212,6 +217,7 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
}
this._source.buffer = this.buffer.get();
this._source.loopEnd = this.loopEnd || this.buffer.duration;
+ Tone.isPast(time);
this._source.start(time, offset);
} else {
throw new Error("Tone.BufferSource: buffer is either not set or not loaded.");
@@ -232,26 +238,35 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
time = this.toSeconds(time);
- //the fadeOut time
- if (Tone.isUndef(fadeOutTime)){
- fadeOutTime = this.toSeconds(this.fadeOut);
- } else {
- fadeOutTime = this.toSeconds(fadeOutTime);
- }
-
- //only stop if the last stop was scheduled later
+ //if this is before the previous stop
if (this._stopTime === -1 || this._stopTime > time){
+
+ //stop if it's schedule before the start time
+ if (time <= this._startTime){
+ this._gainNode.gain.cancelScheduledValues(time);
+ this._gainNode.gain.value = 0;
+ return this;
+ }
+
+ time = Math.max(this._startTime + this.fadeIn + this.sampleTime, time);
+ //cancel the previous curve
+ this._gainNode.gain.cancelScheduledValues(time);
this._stopTime = time;
- //cancel the end curve
- this._gainNode.gain.cancelScheduledValues(this._startTime + this.sampleTime);
- time = Math.max(this._startTime, time);
+ //the fadeOut time
+ fadeOutTime = this.toSeconds(Tone.defaultArg(fadeOutTime, this.fadeOut));
//set a new one
- if (fadeOutTime > 0){
- var startFade = Math.max(this._startTime, time - fadeOutTime);
+ var heldDuration = Math.min(time - this._startTime - this.fadeIn - this.sampleTime, this.buffer.duration);
+ fadeOutTime = Math.min(heldDuration, fadeOutTime);
+ var startFade = time - fadeOutTime;
+ if (fadeOutTime > this.sampleTime){
this._gainNode.gain.setValueAtTime(this._gain, startFade);
- this._gainNode.gain.linearRampToValueAtTime(0, time);
+ if (this.curve === "linear"){
+ this._gainNode.gain.linearRampToValueAtTime(0, time);
+ } else {
+ this._gainNode.gain.setTargetAtTime(0, startFade, this._gainNode.gain.getTimeConstant(fadeOutTime));
+ }
} else {
this._gainNode.gain.setValueAtTime(0, time);
}
diff --git a/Tone/source/Oscillator.js b/Tone/source/Oscillator.js
index 3157e17d..65c6ff06 100644
--- a/Tone/source/Oscillator.js
+++ b/Tone/source/Oscillator.js
@@ -1,4 +1,4 @@
-define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/source/Source", "Tone/core/Transport"],
+define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/source/Source", "Tone/core/Transport"],
function(Tone){
"use strict";
@@ -9,7 +9,7 @@ function(Tone){
*/
if (window.OscillatorNode && !OscillatorNode.prototype.start){
OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn;
- OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
+ OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
if (!OscillatorNode.prototype.setPeriodicWave){
OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable;
}
@@ -20,7 +20,7 @@ function(Tone){
/**
* @class Tone.Oscillator supports a number of features including
- * phase rotation, multiple oscillator types (see Tone.Oscillator.type),
+ * phase rotation, multiple oscillator types (see Tone.Oscillator.type),
* and Transport syncing (see Tone.Oscillator.syncFrequency).
*
* @constructor
@@ -32,7 +32,7 @@ function(Tone){
* var osc = new Tone.Oscillator(440, "sine").toMaster().start();
*/
Tone.Oscillator = function(){
-
+
var options = Tone.defaults(arguments, ["frequency", "type"], Tone.Oscillator);
Tone.Source.call(this, options);
@@ -42,7 +42,7 @@ function(Tone){
* @private
*/
this._oscillator = null;
-
+
/**
* The frequency control.
* @type {Frequency}
@@ -85,7 +85,7 @@ function(Tone){
* @private
*/
this._type = null;
-
+
//setup
this.type = options.type;
this.phase = this._phase;
@@ -120,7 +120,7 @@ function(Tone){
/**
* start the oscillator
- * @param {Time} [time=now]
+ * @param {Time} [time=now]
* @private
*/
Tone.Oscillator.prototype._start = function(time){
@@ -132,7 +132,9 @@ function(Tone){
this.frequency.connect(this._oscillator.frequency);
this.detune.connect(this._oscillator.detune);
//start the oscillator
- this._oscillator.start(this.toSeconds(time));
+ time = this.toSeconds(time);
+ Tone.isPast(time);
+ this._oscillator.start(time);
};
/**
@@ -143,7 +145,9 @@ function(Tone){
*/
Tone.Oscillator.prototype._stop = function(time){
if (this._oscillator){
- this._oscillator.stop(this.toSeconds(time));
+ time = this.toSeconds(time);
+ Tone.isPast(time);
+ this._oscillator.stop(time);
this._oscillator = null;
}
return this;
@@ -151,14 +155,14 @@ function(Tone){
/**
* Sync the signal to the Transport's bpm. Any changes to the transports bpm,
- * will also affect the oscillators frequency.
+ * will also affect the oscillators frequency.
* @returns {Tone.Oscillator} this
* @example
* Tone.Transport.bpm.value = 120;
* osc.frequency.value = 440;
* //the ration between the bpm and the frequency will be maintained
* osc.syncFrequency();
- * Tone.Transport.bpm.value = 240;
+ * Tone.Transport.bpm.value = 240;
* // the frequency of the oscillator is doubled to 880
*/
Tone.Oscillator.prototype.syncFrequency = function(){
@@ -167,7 +171,7 @@ function(Tone){
};
/**
- * Unsync the oscillator's frequency from the Transport.
+ * Unsync the oscillator's frequency from the Transport.
* See Tone.Oscillator.syncFrequency
* @returns {Tone.Oscillator} this
*/
@@ -181,11 +185,11 @@ function(Tone){
* setting the first x number of partials of the oscillator. For example: "sine4" would
* set be the first 4 partials of the sine wave and "triangle8" would set the first
* 8 partials of the triangle wave.
- *
- * Uses PeriodicWave internally even for native types so that it can set the phase.
- * PeriodicWave equations are from the
+ *
+ * Uses PeriodicWave internally even for native types so that it can set the phase.
+ * PeriodicWave equations are from the
* [Webkit Web Audio implementation](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/modules/webaudio/PeriodicWave.cpp&sq=package:chromium).
- *
+ *
* @memberOf Tone.Oscillator#
* @type {string}
* @name type
@@ -212,7 +216,7 @@ function(Tone){
});
/**
- * Returns the real and imaginary components based
+ * Returns the real and imaginary components based
* on the oscillator type.
* @returns {Array} [real, imaginary]
* @private
@@ -223,7 +227,7 @@ function(Tone){
var real = new Float32Array(periodicWaveSize);
var imag = new Float32Array(periodicWaveSize);
-
+
var partialCount = 1;
if (type === Tone.Oscillator.Type.Custom){
partialCount = this._partials.length + 1;
@@ -240,9 +244,9 @@ function(Tone){
for (var n = 1; n < periodicWaveSize; ++n) {
var piFactor = 2 / (n * Math.PI);
- var b;
+ var b;
switch (type) {
- case Tone.Oscillator.Type.Sine:
+ case Tone.Oscillator.Type.Sine:
b = (n <= partialCount) ? 1 : 0;
break;
case Tone.Oscillator.Type.Square:
@@ -258,7 +262,7 @@ function(Tone){
b = 0;
}
break;
- case Tone.Oscillator.Type.Custom:
+ case Tone.Oscillator.Type.Custom:
b = this._partials[n - 1];
break;
default:
@@ -276,10 +280,10 @@ function(Tone){
};
/**
- * Compute the inverse FFT for a given phase.
+ * Compute the inverse FFT for a given phase.
* @param {Float32Array} real
- * @param {Float32Array} imag
- * @param {NormalRange} phase
+ * @param {Float32Array} imag
+ * @param {NormalRange} phase
* @return {AudioRange}
* @private
*/
@@ -311,12 +315,12 @@ function(Tone){
};
/**
- * The partials of the waveform. A partial represents
- * the amplitude at a harmonic. The first harmonic is the
+ * 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".
+ * 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".
* @memberOf Tone.Oscillator#
* @type {Array}
* @name partials
@@ -330,7 +334,7 @@ function(Tone){
} else {
return this._partials;
}
- },
+ },
set : function(partials){
this._partials = partials;
this.type = Tone.Oscillator.Type.Custom;
@@ -338,7 +342,7 @@ function(Tone){
});
/**
- * The phase of the oscillator in degrees.
+ * The phase of the oscillator in degrees.
* @memberOf Tone.Oscillator#
* @type {Degrees}
* @name phase
@@ -348,7 +352,7 @@ function(Tone){
Object.defineProperty(Tone.Oscillator.prototype, "phase", {
get : function(){
return this._phase * (180 / Math.PI);
- },
+ },
set : function(phase){
this._phase = phase * Math.PI / 180;
//reset the type
@@ -377,4 +381,4 @@ function(Tone){
};
return Tone.Oscillator;
-});
\ No newline at end of file
+});
diff --git a/build/Tone.js b/build/Tone.js
deleted file mode 100644
index f5afca74..00000000
--- a/build/Tone.js
+++ /dev/null
@@ -1,23365 +0,0 @@
-(function(root, factory){
-
- //UMD
- if ( typeof define === "function" && define.amd ) {
- define(function() {
- return factory();
- });
- } else if (typeof module === "object") {
- module.exports = factory();
- } else {
- root.Tone = factory();
- }
-
-}(this, function(){
-
- "use strict";
-
- var Tone;
- //constructs the main Tone object
- function Main(func){
- Tone = func();
- }
- //invokes each of the modules with the main Tone object as the argument
- function Module(func){
- func(Tone);
- } /**
- * Tone.js
- * @author Yotam Mann
- * @license http://opensource.org/licenses/MIT MIT License
- * @copyright 2014-2017 Yotam Mann
- */
- Main(function () {
-
- ///////////////////////////////////////////////////////////////////////////
- // TONE
- ///////////////////////////////////////////////////////////////////////////
- /**
- * @class Tone is the base class of all other classes.
- * @constructor
- */
- var Tone = function () {
- };
- /**
- * @memberOf Tone#
- * @returns {string} returns the name of the class as a string
- */
- Tone.prototype.toString = function () {
- for (var className in Tone) {
- var isLetter = className[0].match(/^[A-Z]$/);
- var sameConstructor = Tone[className] === this.constructor;
- if (Tone.isFunction(Tone[className]) && isLetter && sameConstructor) {
- return className;
- }
- }
- return 'Tone';
- };
- /**
- * @memberOf Tone#
- * disconnect and dispose
- * @returns {Tone} this
- */
- Tone.prototype.dispose = function () {
- return this;
- };
- ///////////////////////////////////////////////////////////////////////////
- // GET/SET
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Set the parameters at once. Either pass in an
- * object mapping parameters to values, or to set a
- * single parameter, by passing in a string and value.
- * The last argument is an optional ramp time which
- * will ramp any signal values to their destination value
- * over the duration of the rampTime.
- * @param {Object|string} params
- * @param {number=} value
- * @param {Time=} rampTime
- * @returns {Tone} this
- * @memberOf Tone#
- * @example
- * //set values using an object
- * filter.set({
- * "frequency" : 300,
- * "type" : highpass
- * });
- * @example
- * filter.set("type", "highpass");
- * @example
- * //ramp to the value 220 over 3 seconds.
- * oscillator.set({
- * "frequency" : 220
- * }, 3);
- */
- Tone.prototype.set = function (params, value, rampTime) {
- if (Tone.isObject(params)) {
- rampTime = value;
- } else if (Tone.isString(params)) {
- var tmpObj = {};
- tmpObj[params] = value;
- params = tmpObj;
- }
- paramLoop:
- for (var attr in params) {
- value = params[attr];
- var parent = this;
- if (attr.indexOf('.') !== -1) {
- var attrSplit = attr.split('.');
- for (var i = 0; i < attrSplit.length - 1; i++) {
- parent = parent[attrSplit[i]];
- if (parent instanceof Tone) {
- attrSplit.splice(0, i + 1);
- var innerParam = attrSplit.join('.');
- parent.set(innerParam, value);
- continue paramLoop;
- }
- }
- attr = attrSplit[attrSplit.length - 1];
- }
- var param = parent[attr];
- if (Tone.isUndef(param)) {
- continue;
- }
- if (Tone.Signal && param instanceof Tone.Signal || Tone.Param && param instanceof Tone.Param) {
- if (param.value !== value) {
- if (Tone.isUndef(rampTime)) {
- param.value = value;
- } else {
- param.rampTo(value, rampTime);
- }
- }
- } else if (param instanceof AudioParam) {
- if (param.value !== value) {
- param.value = value;
- }
- } else if (param instanceof Tone) {
- param.set(value);
- } else if (param !== value) {
- parent[attr] = value;
- }
- }
- 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.
- * @memberOf Tone#
- * @example
- * osc.get();
- * //returns {"type" : "sine", "frequency" : 440, ...etc}
- * @example
- * osc.get("type");
- * //returns { "type" : "sine"}
- * @example
- * //use dot notation to access deep properties
- * synth.get(["envelope.attack", "envelope.release"]);
- * //returns {"envelope" : {"attack" : 0.2, "release" : 0.4}}
- * @param {Array=|string|undefined} params the parameters to get, otherwise will return
- * all available.
- * @returns {Object}
- */
- Tone.prototype.get = function (params) {
- if (Tone.isUndef(params)) {
- params = this._collectDefaults(this.constructor);
- } else if (Tone.isString(params)) {
- params = [params];
- }
- var ret = {};
- for (var i = 0; i < params.length; i++) {
- var attr = params[i];
- var parent = this;
- var subRet = ret;
- if (attr.indexOf('.') !== -1) {
- var attrSplit = attr.split('.');
- for (var j = 0; j < attrSplit.length - 1; j++) {
- var subAttr = attrSplit[j];
- subRet[subAttr] = subRet[subAttr] || {};
- subRet = subRet[subAttr];
- parent = parent[subAttr];
- }
- attr = attrSplit[attrSplit.length - 1];
- }
- var param = parent[attr];
- if (Tone.isObject(params[attr])) {
- subRet[attr] = param.get();
- } else if (Tone.Signal && param instanceof Tone.Signal) {
- subRet[attr] = param.value;
- } else if (Tone.Param && param instanceof Tone.Param) {
- subRet[attr] = param.value;
- } else if (param instanceof AudioParam) {
- subRet[attr] = param.value;
- } else if (param instanceof Tone) {
- subRet[attr] = param.get();
- } else if (!Tone.isFunction(param) && !Tone.isUndef(param)) {
- subRet[attr] = param;
- }
- }
- return ret;
- };
- /**
- * collect all of the default attributes in one
- * @private
- * @param {function} constr the constructor to find the defaults from
- * @return {Array} all of the attributes which belong to the class
- */
- Tone.prototype._collectDefaults = function (constr) {
- var ret = [];
- if (!Tone.isUndef(constr.defaults)) {
- ret = Object.keys(constr.defaults);
- }
- if (!Tone.isUndef(constr._super)) {
- var superDefs = this._collectDefaults(constr._super);
- //filter out repeats
- for (var i = 0; i < superDefs.length; i++) {
- if (ret.indexOf(superDefs[i]) === -1) {
- ret.push(superDefs[i]);
- }
- }
- }
- return ret;
- };
- ///////////////////////////////////////////////////////////////////////////
- // DEFAULTS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * @memberOf Tone
- * @param {Array} values The arguments array
- * @param {Array} keys The names of the arguments
- * @param {Function|Object} constr The class constructor
- * @return {Object} An object composed of the defaults between the class' defaults
- * and the passed in arguments.
- */
- Tone.defaults = function (values, keys, constr) {
- var options = {};
- if (values.length === 1 && Tone.isObject(values[0])) {
- options = values[0];
- } else {
- for (var i = 0; i < keys.length; i++) {
- options[keys[i]] = values[i];
- }
- }
- if (!Tone.isUndef(constr.defaults)) {
- return Tone.defaultArg(options, constr.defaults);
- } else if (Tone.isObject(constr)) {
- return Tone.defaultArg(options, constr);
- } else {
- return options;
- }
- };
- /**
- * If the `given` parameter is undefined, use the `fallback`.
- * If both `given` and `fallback` are object literals, it will
- * return a deep copy which includes all of the parameters from both
- * objects. If a parameter is undefined in given, it will return
- * the fallback property.
- *
- * WARNING: if object is self referential, it will go into an an
- * infinite recursive loop.
- * @memberOf Tone
- * @param {*} given
- * @param {*} fallback
- * @return {*}
- */
- Tone.defaultArg = function (given, fallback) {
- if (Tone.isObject(given) && Tone.isObject(fallback)) {
- var ret = {};
- //make a deep copy of the given object
- for (var givenProp in given) {
- ret[givenProp] = Tone.defaultArg(fallback[givenProp], given[givenProp]);
- }
- for (var fallbackProp in fallback) {
- ret[fallbackProp] = Tone.defaultArg(given[fallbackProp], fallback[fallbackProp]);
- }
- return ret;
- } else {
- return Tone.isUndef(given) ? fallback : given;
- }
- };
- ///////////////////////////////////////////////////////////////////////////
- // CONNECTIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * connect together all of the arguments in series
- * @param {...AudioParam|Tone|AudioNode} nodes
- * @returns {Tone}
- * @memberOf Tone
- * @static
- */
- Tone.connectSeries = function () {
- var currentUnit = arguments[0];
- for (var i = 1; i < arguments.length; i++) {
- var toUnit = arguments[i];
- currentUnit.connect(toUnit);
- currentUnit = toUnit;
- }
- return Tone;
- };
- ///////////////////////////////////////////////////////////////////////////
- // TYPE CHECKING
- ///////////////////////////////////////////////////////////////////////////
- /**
- * test if the arg is undefined
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is undefined
- * @static
- * @memberOf Tone
- */
- Tone.isUndef = function (val) {
- return typeof val === 'undefined';
- };
- /**
- * test if the arg is a function
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is a function
- * @static
- * @memberOf Tone
- */
- Tone.isFunction = function (val) {
- return typeof val === 'function';
- };
- /**
- * Test if the argument is a number.
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is a number
- * @static
- * @memberOf Tone
- */
- Tone.isNumber = function (arg) {
- return typeof arg === 'number';
- };
- /**
- * Test if the given argument is an object literal (i.e. `{}`);
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is an object literal.
- * @static
- * @memberOf Tone
- */
- Tone.isObject = function (arg) {
- return Object.prototype.toString.call(arg) === '[object Object]' && arg.constructor === Object;
- };
- /**
- * Test if the argument is a boolean.
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is a boolean
- * @static
- * @memberOf Tone
- */
- Tone.isBoolean = function (arg) {
- return typeof arg === 'boolean';
- };
- /**
- * Test if the argument is an Array
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is an array
- * @static
- * @memberOf Tone
- */
- Tone.isArray = function (arg) {
- return Array.isArray(arg);
- };
- /**
- * Test if the argument is a string.
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is a string
- * @static
- * @memberOf Tone
- */
- Tone.isString = function (arg) {
- return typeof arg === 'string';
- };
- /**
- * Test if the argument is in the form of a note in scientific pitch notation.
- * e.g. "C4"
- * @param {*} arg the argument to test
- * @returns {boolean} true if the arg is a string
- * @static
- * @memberOf Tone
- */
- Tone.isNote = function (arg) {
- return Tone.isString(arg) && /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i.test(arg);
- };
- /**
- * An empty function.
- * @static
- */
- Tone.noOp = function () {
- };
- /**
- * Make the property not writable. Internal use only.
- * @private
- * @param {string} property the property to make not writable
- */
- Tone.prototype._readOnly = function (property) {
- if (Array.isArray(property)) {
- for (var i = 0; i < property.length; i++) {
- this._readOnly(property[i]);
- }
- } else {
- Object.defineProperty(this, property, {
- writable: false,
- enumerable: true
- });
- }
- };
- /**
- * Make an attribute writeable. Interal use only.
- * @private
- * @param {string} property the property to make writable
- */
- Tone.prototype._writable = function (property) {
- if (Array.isArray(property)) {
- for (var i = 0; i < property.length; i++) {
- this._writable(property[i]);
- }
- } else {
- Object.defineProperty(this, property, { writable: true });
- }
- };
- /**
- * Possible play states.
- * @enum {string}
- */
- Tone.State = {
- Started: 'started',
- Stopped: 'stopped',
- Paused: 'paused'
- };
- ///////////////////////////////////////////////////////////////////////////
- // CONVERSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Equal power gain scale. Good for cross-fading.
- * @param {NormalRange} percent (0-1)
- * @return {Number} output gain (0-1)
- * @static
- * @memberOf Tone
- */
- Tone.equalPowerScale = function (percent) {
- var piFactor = 0.5 * Math.PI;
- return Math.sin(percent * piFactor);
- };
- /**
- * Convert decibels into gain.
- * @param {Decibels} db
- * @return {Number}
- * @static
- * @memberOf Tone
- */
- Tone.dbToGain = function (db) {
- return Math.pow(2, db / 6);
- };
- /**
- * Convert gain to decibels.
- * @param {Number} gain (0-1)
- * @return {Decibels}
- * @static
- * @memberOf Tone
- */
- Tone.gainToDb = function (gain) {
- return 20 * (Math.log(gain) / Math.LN10);
- };
- /**
- * Convert an interval (in semitones) to a frequency ratio.
- * @param {Interval} interval the number of semitones above the base note
- * @return {number} the frequency ratio
- * @static
- * @memberOf Tone
- * @example
- * tone.intervalToFrequencyRatio(0); // 1
- * tone.intervalToFrequencyRatio(12); // 2
- * tone.intervalToFrequencyRatio(-12); // 0.5
- */
- Tone.intervalToFrequencyRatio = function (interval) {
- return Math.pow(2, interval / 12);
- };
- ///////////////////////////////////////////////////////////////////////////
- // TIMING
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Return the current time of the AudioContext clock.
- * @return {Number} the currentTime from the AudioContext
- * @memberOf Tone#
- */
- Tone.prototype.now = function () {
- return Tone.context.now();
- };
- /**
- * Return the current time of the AudioContext clock.
- * @return {Number} the currentTime from the AudioContext
- * @static
- * @memberOf Tone
- */
- Tone.now = function () {
- return Tone.context.now();
- };
- ///////////////////////////////////////////////////////////////////////////
- // INHERITANCE
- ///////////////////////////////////////////////////////////////////////////
- /**
- * have a child inherit all of Tone's (or a parent's) prototype
- * to inherit the parent's properties, make sure to call
- * Parent.call(this) in the child's constructor
- *
- * based on closure library's inherit function
- *
- * @memberOf Tone
- * @static
- * @param {function} child
- * @param {function=} parent (optional) parent to inherit from
- * if no parent is supplied, the child
- * will inherit from Tone
- */
- Tone.extend = function (child, parent) {
- if (Tone.isUndef(parent)) {
- parent = Tone;
- }
- function TempConstructor() {
- }
- TempConstructor.prototype = parent.prototype;
- child.prototype = new TempConstructor();
- /** @override */
- child.prototype.constructor = child;
- child._super = parent;
- };
- ///////////////////////////////////////////////////////////////////////////
- // CONTEXT
- ///////////////////////////////////////////////////////////////////////////
- /**
- * The private audio context shared by all Tone Nodes.
- * @private
- * @type {Tone.Context}
- */
- var audioContext = null;
- /**
- * A static pointer to the audio context accessible as Tone.context.
- * @type {Tone.Context}
- * @name context
- * @memberOf Tone
- */
- Object.defineProperty(Tone, 'context', {
- get: function () {
- return audioContext;
- },
- set: function (context) {
- if (Tone.Context && context instanceof Tone.Context) {
- audioContext = context;
- } else {
- audioContext = new Tone.Context(context);
- }
- //initialize the new audio context
- Tone.Context.emit('init', audioContext);
- }
- });
- /**
- * The AudioContext
- * @type {Tone.Context}
- * @name context
- * @memberOf Tone#
- * @readOnly
- */
- Object.defineProperty(Tone.prototype, 'context', {
- get: function () {
- return Tone.context;
- }
- });
- /**
- * Tone automatically creates a context on init, but if you are working
- * with other libraries which also create an AudioContext, it can be
- * useful to set your own. If you are going to set your own context,
- * be sure to do it at the start of your code, before creating any objects.
- * @static
- * @param {AudioContext} ctx The new audio context to set
- */
- Tone.setContext = function (ctx) {
- Tone.context = ctx;
- };
- ///////////////////////////////////////////////////////////////////////////
- // ATTRIBUTES
- ///////////////////////////////////////////////////////////////////////////
- /**
- * The number of seconds of 1 processing block (128 samples)
- * @type {Number}
- * @name blockTime
- * @memberOf Tone
- * @static
- * @readOnly
- */
- Object.defineProperty(Tone.prototype, 'blockTime', {
- get: function () {
- return 128 / this.context.sampleRate;
- }
- });
- /**
- * The duration in seconds of one sample.
- * @type {Number}
- * @name sampleTime
- * @memberOf Tone
- * @static
- * @readOnly
- */
- Object.defineProperty(Tone.prototype, 'sampleTime', {
- get: function () {
- return 1 / this.context.sampleRate;
- }
- });
- /**
- * Whether or not all the technologies that Tone.js relies on are supported by the current browser.
- * @type {Boolean}
- * @name supported
- * @memberOf Tone
- * @readOnly
- * @static
- */
- Object.defineProperty(Tone, 'supported', {
- get: function () {
- var hasAudioContext = window.hasOwnProperty('AudioContext') || window.hasOwnProperty('webkitAudioContext');
- var hasPromises = window.hasOwnProperty('Promise');
- var hasWorkers = window.hasOwnProperty('Worker');
- return hasAudioContext && hasPromises && hasWorkers;
- }
- });
- /**
- * Boolean value if the audio context has been initialized.
- * @type {Boolean}
- * @memberOf Tone
- * @static
- * @name initialized
- */
- Object.defineProperty(Tone, 'initialized', {
- get: function () {
- return audioContext !== null;
- }
- });
- /**
- * Get the context when it becomes available
- * @param {Function} resolve Callback when the context is initialized
- * @return {Tone}
- */
- Tone.getContext = function (resolve) {
- if (Tone.initialized) {
- resolve(Tone.context);
- } else {
- var resCallback = function () {
- resolve(Tone.context);
- Tone.Context.off('init', resCallback);
- };
- Tone.Context.on('init', resCallback);
- }
- return Tone;
- };
- /**
- * The version number
- * @type {String}
- * @static
- */
- Tone.version = 'r11';
- // allow optional silencing of this log
- if (!window.TONE_SILENCE_VERSION_LOGGING) {
- console.log('%c * Tone.js ' + Tone.version + ' * ', 'background: #000; color: #fff');
- }
- return Tone;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Emitter gives classes which extend it
- * the ability to listen for and emit events.
- * Inspiration and reference from Jerome Etienne's [MicroEvent](https://github.com/jeromeetienne/microevent.js).
- * MIT (c) 2011 Jerome Etienne.
- *
- * @extends {Tone}
- */
- Tone.Emitter = function () {
- Tone.call(this);
- /**
- * Contains all of the events.
- * @private
- * @type {Object}
- */
- this._events = {};
- };
- Tone.extend(Tone.Emitter);
- /**
- * Bind a callback to a specific event.
- * @param {String} event The name of the event to listen for.
- * @param {Function} callback The callback to invoke when the
- * event is emitted
- * @return {Tone.Emitter} this
- */
- Tone.Emitter.prototype.on = function (event, callback) {
- //split the event
- var events = event.split(/\W+/);
- for (var i = 0; i < events.length; i++) {
- var eventName = events[i];
- if (!this._events.hasOwnProperty(eventName)) {
- this._events[eventName] = [];
- }
- this._events[eventName].push(callback);
- }
- return this;
- };
- /**
- * Remove the event listener.
- * @param {String} event The event to stop listening to.
- * @param {Function=} callback The callback which was bound to
- * the event with Tone.Emitter.on.
- * If no callback is given, all callbacks
- * events are removed.
- * @return {Tone.Emitter} this
- */
- Tone.Emitter.prototype.off = function (event, callback) {
- var events = event.split(/\W+/);
- for (var ev = 0; ev < events.length; ev++) {
- event = events[ev];
- if (this._events.hasOwnProperty(event)) {
- if (Tone.isUndef(callback)) {
- this._events[event] = [];
- } else {
- var eventList = this._events[event];
- for (var i = 0; i < eventList.length; i++) {
- if (eventList[i] === callback) {
- eventList.splice(i, 1);
- }
- }
- }
- }
- }
- return this;
- };
- /**
- * Invoke all of the callbacks bound to the event
- * with any arguments passed in.
- * @param {String} event The name of the event.
- * @param {*} args... The arguments to pass to the functions listening.
- * @return {Tone.Emitter} this
- */
- Tone.Emitter.prototype.emit = function (event) {
- if (this._events) {
- var args = Array.apply(null, arguments).slice(1);
- if (this._events.hasOwnProperty(event)) {
- var eventList = this._events[event];
- for (var i = 0, len = eventList.length; i < len; i++) {
- eventList[i].apply(this, args);
- }
- }
- }
- return this;
- };
- /**
- * Add Emitter functions (on/off/emit) to the object
- * @param {Object|Function} object The object or class to extend.
- * @returns {Tone.Emitter}
- */
- Tone.Emitter.mixin = function (object) {
- var functions = [
- 'on',
- 'off',
- 'emit'
- ];
- object._events = {};
- for (var i = 0; i < functions.length; i++) {
- var func = functions[i];
- var emitterFunc = Tone.Emitter.prototype[func];
- object[func] = emitterFunc;
- }
- return Tone.Emitter;
- };
- /**
- * Clean up
- * @return {Tone.Emitter} this
- */
- Tone.Emitter.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._events = null;
- return this;
- };
- return Tone.Emitter;
- });
- Module(function (Tone) {
-
- /**
- * @class A Timeline class for scheduling and maintaining state
- * along a timeline. All events must have a "time" property.
- * Internally, events are stored in time order for fast
- * retrieval.
- * @extends {Tone}
- * @param {Positive} [memory=Infinity] The number of previous events that are retained.
- */
- Tone.Timeline = function () {
- var options = Tone.defaults(arguments, ['memory'], Tone.Timeline);
- Tone.call(this);
- /**
- * The array of scheduled timeline events
- * @type {Array}
- * @private
- */
- this._timeline = [];
- /**
- * An array of items to remove from the list.
- * @type {Array}
- * @private
- */
- this._toRemove = [];
- /**
- * Flag if the timeline is mid iteration
- * @private
- * @type {Boolean}
- */
- this._iterating = false;
- /**
- * The memory of the timeline, i.e.
- * how many events in the past it will retain
- * @type {Positive}
- */
- this.memory = options.memory;
- };
- Tone.extend(Tone.Timeline);
- /**
- * the default parameters
- * @static
- * @const
- */
- Tone.Timeline.defaults = { 'memory': Infinity };
- /**
- * The number of items in the timeline.
- * @type {Number}
- * @memberOf Tone.Timeline#
- * @name length
- * @readOnly
- */
- Object.defineProperty(Tone.Timeline.prototype, 'length', {
- get: function () {
- return this._timeline.length;
- }
- });
- /**
- * Insert an event object onto the timeline. Events must have a "time" attribute.
- * @param {Object} event The event object to insert into the
- * timeline.
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.add = function (event) {
- //the event needs to have a time attribute
- if (Tone.isUndef(event.time)) {
- throw new Error('Tone.Timeline: events must have a time attribute');
- }
- if (this._timeline.length) {
- var index = this._search(event.time);
- this._timeline.splice(index + 1, 0, event);
- } else {
- this._timeline.push(event);
- }
- //if the length is more than the memory, remove the previous ones
- if (this.length > this.memory) {
- var diff = this.length - this.memory;
- this._timeline.splice(0, diff);
- }
- return this;
- };
- /**
- * Remove an event from the timeline.
- * @param {Object} event The event object to remove from the list.
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.remove = function (event) {
- if (this._iterating) {
- this._toRemove.push(event);
- } else {
- var index = this._timeline.indexOf(event);
- if (index !== -1) {
- this._timeline.splice(index, 1);
- }
- }
- return this;
- };
- /**
- * Get the nearest event whose time is less than or equal to the given time.
- * @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
- * @returns {Object} The event object set after that time.
- */
- Tone.Timeline.prototype.get = function (time, comparitor) {
- comparitor = Tone.defaultArg(comparitor, 'time');
- var index = this._search(time, comparitor);
- if (index !== -1) {
- return this._timeline[index];
- } else {
- return null;
- }
- };
- /**
- * Return the first event in the timeline without removing it
- * @returns {Object} The first event object
- */
- Tone.Timeline.prototype.peek = function () {
- return this._timeline[0];
- };
- /**
- * Return the first event in the timeline and remove it
- * @returns {Object} The first event object
- */
- Tone.Timeline.prototype.shift = function () {
- return this._timeline.shift();
- };
- /**
- * Get the event which is scheduled after the given time.
- * @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
- * @returns {Object} The event object after the given time
- */
- Tone.Timeline.prototype.getAfter = function (time, comparitor) {
- comparitor = Tone.defaultArg(comparitor, 'time');
- var index = this._search(time, comparitor);
- if (index + 1 < this._timeline.length) {
- return this._timeline[index + 1];
- } else {
- return null;
- }
- };
- /**
- * Get the event before the event at the given time.
- * @param {Number} time The time to query.
- * @param {String} comparitor Which value in the object to compare
- * @returns {Object} The event object before the given time
- */
- Tone.Timeline.prototype.getBefore = function (time, comparitor) {
- comparitor = Tone.defaultArg(comparitor, 'time');
- var len = this._timeline.length;
- //if it's after the last item, return the last item
- if (len > 0 && this._timeline[len - 1][comparitor] < time) {
- return this._timeline[len - 1];
- }
- var index = this._search(time, comparitor);
- if (index - 1 >= 0) {
- return this._timeline[index - 1];
- } else {
- return null;
- }
- };
- /**
- * Cancel events after the given time
- * @param {Number} time The time to query.
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.cancel = function (after) {
- if (this._timeline.length > 1) {
- var index = this._search(after);
- if (index >= 0) {
- if (this._timeline[index].time === after) {
- //get the first item with that time
- for (var i = index; i >= 0; i--) {
- if (this._timeline[i].time === after) {
- index = i;
- } else {
- break;
- }
- }
- this._timeline = this._timeline.slice(0, index);
- } else {
- this._timeline = this._timeline.slice(0, index + 1);
- }
- } else {
- this._timeline = [];
- }
- } else if (this._timeline.length === 1) {
- //the first item's time
- if (this._timeline[0].time >= after) {
- this._timeline = [];
- }
- }
- return this;
- };
- /**
- * Cancel events before or equal to the given time.
- * @param {Number} time The time to cancel before.
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.cancelBefore = function (time) {
- if (this._timeline.length) {
- var index = this._search(time);
- if (index >= 0) {
- this._timeline = this._timeline.slice(index + 1);
- }
- }
- return this;
- };
- /**
- * Returns the previous event if there is one. null otherwise
- * @param {Object} event The event to find the previous one of
- * @return {Object} The event right before the given event
- */
- Tone.Timeline.prototype.previousEvent = function (event) {
- var index = this._timeline.indexOf(event);
- if (index > 0) {
- return this._timeline[index - 1];
- } else {
- return null;
- }
- };
- /**
- * Does a binary serach on the timeline array and returns the
- * nearest event index whose time is after or equal to the given time.
- * If a time is searched before the first index in the timeline, -1 is returned.
- * If the time is after the end, the index of the last item is returned.
- * @param {Number} time
- * @param {String} comparitor Which value in the object to compare
- * @return {Number} the index in the timeline array
- * @private
- */
- Tone.Timeline.prototype._search = function (time, comparitor) {
- comparitor = Tone.defaultArg(comparitor, 'time');
- var beginning = 0;
- var len = this._timeline.length;
- var end = len;
- if (len > 0 && this._timeline[len - 1][comparitor] <= time) {
- return len - 1;
- }
- while (beginning < end) {
- // calculate the midpoint for roughly equal partition
- var midPoint = Math.floor(beginning + (end - beginning) / 2);
- var event = this._timeline[midPoint];
- var nextEvent = this._timeline[midPoint + 1];
- if (event[comparitor] === time) {
- //choose the last one that has the same time
- for (var i = midPoint; i < this._timeline.length; i++) {
- var testEvent = this._timeline[i];
- if (testEvent[comparitor] === time) {
- midPoint = i;
- }
- }
- return midPoint;
- } else if (event[comparitor] < time && nextEvent[comparitor] > time) {
- return midPoint;
- } else if (event[comparitor] > time) {
- //search lower
- end = midPoint;
- } else {
- //search upper
- beginning = midPoint + 1;
- }
- }
- return -1;
- };
- /**
- * Internal iterator. Applies extra safety checks for
- * removing items from the array.
- * @param {Function} callback
- * @param {Number=} lowerBound
- * @param {Number=} upperBound
- * @private
- */
- Tone.Timeline.prototype._iterate = function (callback, lowerBound, upperBound) {
- this._iterating = true;
- lowerBound = Tone.defaultArg(lowerBound, 0);
- upperBound = Tone.defaultArg(upperBound, this._timeline.length - 1);
- for (var i = lowerBound; i <= upperBound; i++) {
- callback.call(this, this._timeline[i]);
- }
- this._iterating = false;
- if (this._toRemove.length > 0) {
- for (var j = 0; j < this._toRemove.length; j++) {
- var index = this._timeline.indexOf(this._toRemove[j]);
- if (index !== -1) {
- this._timeline.splice(index, 1);
- }
- }
- this._toRemove = [];
- }
- };
- /**
- * Iterate over everything in the array
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.forEach = function (callback) {
- this._iterate(callback);
- return this;
- };
- /**
- * Iterate over everything in the array at or before the given time.
- * @param {Number} time The time to check if items are before
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.forEachBefore = function (time, callback) {
- //iterate over the items in reverse so that removing an item doesn't break things
- var upperBound = this._search(time);
- if (upperBound !== -1) {
- this._iterate(callback, 0, upperBound);
- }
- return this;
- };
- /**
- * Iterate over everything in the array after the given time.
- * @param {Number} time The time to check if items are before
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.forEachAfter = function (time, callback) {
- //iterate over the items in reverse so that removing an item doesn't break things
- var lowerBound = this._search(time);
- this._iterate(callback, lowerBound + 1);
- return this;
- };
- /**
- * Iterate over everything in the array at or after the given time. Similar to
- * forEachAfter, but includes the item(s) at the given time.
- * @param {Number} time The time to check if items are before
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.forEachFrom = function (time, callback) {
- //iterate over the items in reverse so that removing an item doesn't break things
- var lowerBound = this._search(time);
- //work backwards until the event time is less than time
- while (lowerBound >= 0 && this._timeline[lowerBound].time >= time) {
- lowerBound--;
- }
- this._iterate(callback, lowerBound + 1);
- return this;
- };
- /**
- * Iterate over everything in the array at the given time
- * @param {Number} time The time to check if items are before
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.Timeline} this
- */
- Tone.Timeline.prototype.forEachAtTime = function (time, callback) {
- //iterate over the items in reverse so that removing an item doesn't break things
- var upperBound = this._search(time);
- if (upperBound !== -1) {
- this._iterate(function (event) {
- if (event.time === time) {
- callback.call(this, event);
- }
- }, 0, upperBound);
- }
- return this;
- };
- /**
- * Clean up.
- * @return {Tone.Timeline} this
- */
- Tone.Timeline.prototype.dispose = function () {
- Tone.prototype.dispose.call(this);
- this._timeline = null;
- this._toRemove = null;
- return this;
- };
- return Tone.Timeline;
- });
- Module(function (Tone) {
- /**
- * shim
- * @private
- */
- if (!window.hasOwnProperty('AudioContext') && window.hasOwnProperty('webkitAudioContext')) {
- window.AudioContext = window.webkitAudioContext;
- }
- /**
- * @class Wrapper around the native AudioContext.
- * @extends {Tone.Emitter}
- * @param {AudioContext=} context optionally pass in a context
- */
- Tone.Context = function () {
- Tone.Emitter.call(this);
- var options = Tone.defaults(arguments, ['context'], Tone.Context);
- if (!options.context) {
- options.context = new window.AudioContext();
- }
- this._context = options.context;
- // extend all of the methods
- for (var prop in this._context) {
- this._defineProperty(this._context, prop);
- }
- /**
- * The default latency hint
- * @type {String}
- * @private
- */
- this._latencyHint = options.latencyHint;
- /**
- * An object containing all of the constants AudioBufferSourceNodes
- * @type {Object}
- * @private
- */
- this._constants = {};
- ///////////////////////////////////////////////////////////////////////
- // WORKER
- ///////////////////////////////////////////////////////////////////////
- /**
- * The amount of time events are scheduled
- * into the future
- * @type {Number}
- * @private
- */
- this.lookAhead = options.lookAhead;
- /**
- * A reference to the actual computed update interval
- * @type {Number}
- * @private
- */
- this._computedUpdateInterval = 0;
- /**
- * A reliable callback method
- * @private
- * @type {Ticker}
- */
- this._ticker = new Ticker(this.emit.bind(this, 'tick'), options.clockSource, options.updateInterval);
- ///////////////////////////////////////////////////////////////////////
- // TIMEOUTS
- ///////////////////////////////////////////////////////////////////////
- /**
- * All of the setTimeout events.
- * @type {Tone.Timeline}
- * @private
- */
- this._timeouts = new Tone.Timeline();
- /**
- * The timeout id counter
- * @private
- * @type {Number}
- */
- this._timeoutIds = 0;
- this.on('tick', this._timeoutLoop.bind(this));
- };
- Tone.extend(Tone.Context, Tone.Emitter);
- Tone.Emitter.mixin(Tone.Context);
- /**
- * defaults
- * @static
- * @type {Object}
- */
- Tone.Context.defaults = {
- 'clockSource': 'worker',
- 'latencyHint': 'interactive',
- 'lookAhead': 0.1,
- 'updateInterval': 0.03
- };
- /**
- * Define a property on this Tone.Context.
- * This is used to extend the native AudioContext
- * @param {AudioContext} context
- * @param {String} prop
- * @private
- */
- Tone.Context.prototype._defineProperty = function (context, prop) {
- if (Tone.isUndef(this[prop])) {
- Object.defineProperty(this, prop, {
- get: function () {
- if (typeof context[prop] === 'function') {
- return context[prop].bind(context);
- } else {
- return context[prop];
- }
- },
- set: function (val) {
- context[prop] = val;
- }
- });
- }
- };
- /**
- * The current audio context time
- * @return {Number}
- */
- Tone.Context.prototype.now = function () {
- return this._context.currentTime + this.lookAhead;
- };
- /**
- * Generate a looped buffer at some constant value.
- * @param {Number} val
- * @return {BufferSourceNode}
- */
- Tone.Context.prototype.getConstant = function (val) {
- if (this._constants[val]) {
- return this._constants[val];
- } else {
- var buffer = this._context.createBuffer(1, 128, this._context.sampleRate);
- var arr = buffer.getChannelData(0);
- for (var i = 0; i < arr.length; i++) {
- arr[i] = val;
- }
- var constant = this._context.createBufferSource();
- constant.channelCount = 1;
- constant.channelCountMode = 'explicit';
- constant.buffer = buffer;
- constant.loop = true;
- constant.start(0);
- this._constants[val] = constant;
- return constant;
- }
- };
- /**
- * The private loop which keeps track of the context scheduled timeouts
- * Is invoked from the clock source
- * @private
- */
- Tone.Context.prototype._timeoutLoop = function () {
- var now = this.now();
- while (this._timeouts && this._timeouts.length && this._timeouts.peek().time <= now) {
- this._timeouts.shift().callback();
- }
- };
- /**
- * A setTimeout which is gaurenteed by the clock source.
- * Also runs in the offline context.
- * @param {Function} fn The callback to invoke
- * @param {Seconds} timeout The timeout in seconds
- * @returns {Number} ID to use when invoking Tone.Context.clearTimeout
- */
- Tone.Context.prototype.setTimeout = function (fn, timeout) {
- this._timeoutIds++;
- var now = this.now();
- this._timeouts.add({
- callback: fn,
- time: now + timeout,
- id: this._timeoutIds
- });
- return this._timeoutIds;
- };
- /**
- * Clears a previously scheduled timeout with Tone.context.setTimeout
- * @param {Number} id The ID returned from setTimeout
- * @return {Tone.Context} this
- */
- Tone.Context.prototype.clearTimeout = function (id) {
- this._timeouts.forEach(function (event) {
- if (event.id === id) {
- this.remove(event);
- }
- });
- return this;
- };
- /**
- * How often the Web Worker callback is invoked.
- * This number corresponds to how responsive the scheduling
- * can be. Context.updateInterval + Context.lookAhead gives you the
- * total latency between scheduling an event and hearing it.
- * @type {Number}
- * @memberOf Tone.Context#
- * @name updateInterval
- */
- Object.defineProperty(Tone.Context.prototype, 'updateInterval', {
- get: function () {
- return this._ticker.updateInterval;
- },
- set: function (interval) {
- this._ticker.updateInterval = interval;
- }
- });
- /**
- * What the source of the clock is, either "worker" (Web Worker [default]),
- * "timeout" (setTimeout), or "offline" (none).
- * @type {String}
- * @memberOf Tone.Context#
- * @name clockSource
- */
- Object.defineProperty(Tone.Context.prototype, 'clockSource', {
- get: function () {
- return this._ticker.type;
- },
- set: function (type) {
- this._ticker.type = type;
- }
- });
- /**
- * The type of playback, which affects tradeoffs between audio
- * output latency and responsiveness.
- *
- * In addition to setting the value in seconds, the latencyHint also
- * accepts the strings "interactive" (prioritizes low latency),
- * "playback" (prioritizes sustained playback), "balanced" (balances
- * latency and performance), and "fastest" (lowest latency, might glitch more often).
- * @type {String|Seconds}
- * @memberOf Tone.Context#
- * @name latencyHint
- * @example
- * //set the lookAhead to 0.3 seconds
- * Tone.context.latencyHint = 0.3;
- */
- Object.defineProperty(Tone.Context.prototype, 'latencyHint', {
- get: function () {
- return this._latencyHint;
- },
- set: function (hint) {
- var lookAhead = hint;
- this._latencyHint = hint;
- if (Tone.isString(hint)) {
- switch (hint) {
- case 'interactive':
- lookAhead = 0.1;
- this._context.latencyHint = hint;
- break;
- case 'playback':
- lookAhead = 0.8;
- this._context.latencyHint = hint;
- break;
- case 'balanced':
- lookAhead = 0.25;
- this._context.latencyHint = hint;
- break;
- case 'fastest':
- this._context.latencyHint = 'interactive';
- lookAhead = 0.01;
- break;
- }
- }
- this.lookAhead = lookAhead;
- this.updateInterval = lookAhead / 3;
- }
- });
- /**
- * Clean up
- * @returns {Tone.Context} this
- */
- Tone.Context.prototype.dispose = function () {
- Tone.Context.emit('close', this);
- Tone.Emitter.prototype.dispose.call(this);
- this._ticker.dispose();
- this._ticker = null;
- this._timeouts.dispose();
- this._timeouts = null;
- for (var con in this._constants) {
- this._constants[con].disconnect();
- }
- this._constants = null;
- this.close();
- return this;
- };
- /**
- * @class A class which provides a reliable callback using either
- * a Web Worker, or if that isn't supported, falls back to setTimeout.
- * @private
- */
- var Ticker = function (callback, type, updateInterval) {
- /**
- * Either "worker" or "timeout"
- * @type {String}
- * @private
- */
- this._type = type;
- /**
- * The update interval of the worker
- * @private
- * @type {Number}
- */
- this._updateInterval = updateInterval;
- /**
- * The callback to invoke at regular intervals
- * @type {Function}
- * @private
- */
- this._callback = Tone.defaultArg(callback, Tone.noOp);
- //create the clock source for the first time
- this._createClock();
- };
- /**
- * The possible ticker types
- * @private
- * @type {Object}
- */
- Ticker.Type = {
- Worker: 'worker',
- Timeout: 'timeout',
- Offline: 'offline'
- };
- /**
- * Generate a web worker
- * @return {WebWorker}
- * @private
- */
- Ticker.prototype._createWorker = function () {
- //URL Shim
- window.URL = window.URL || window.webkitURL;
- var blob = new Blob([//the initial timeout time
- 'var timeoutTime = ' + (this._updateInterval * 1000).toFixed(1) + ';' + //onmessage callback
- 'self.onmessage = function(msg){' + '\ttimeoutTime = parseInt(msg.data);' + '};' + //the tick function which posts a message
- //and schedules a new tick
- 'function tick(){' + '\tsetTimeout(tick, timeoutTime);' + '\tself.postMessage(\'tick\');' + '}' + //call tick initially
- 'tick();']);
- var blobUrl = URL.createObjectURL(blob);
- var worker = new Worker(blobUrl);
- worker.onmessage = this._callback.bind(this);
- this._worker = worker;
- };
- /**
- * Create a timeout loop
- * @private
- */
- Ticker.prototype._createTimeout = function () {
- this._timeout = setTimeout(function () {
- this._createTimeout();
- this._callback();
- }.bind(this), this._updateInterval * 1000);
- };
- /**
- * Create the clock source.
- * @private
- */
- Ticker.prototype._createClock = function () {
- if (this._type === Ticker.Type.Worker) {
- try {
- this._createWorker();
- } catch (e) {
- // workers not supported, fallback to timeout
- this._type = Ticker.Type.Timeout;
- this._createClock();
- }
- } else if (this._type === Ticker.Type.Timeout) {
- this._createTimeout();
- }
- };
- /**
- * @memberOf Ticker#
- * @type {Number}
- * @name updateInterval
- * @private
- */
- Object.defineProperty(Ticker.prototype, 'updateInterval', {
- get: function () {
- return this._updateInterval;
- },
- set: function (interval) {
- this._updateInterval = Math.max(interval, 128 / 44100);
- if (this._type === Ticker.Type.Worker) {
- this._worker.postMessage(Math.max(interval * 1000, 1));
- }
- }
- });
- /**
- * The type of the ticker, either a worker or a timeout
- * @memberOf Ticker#
- * @type {Number}
- * @name type
- * @private
- */
- Object.defineProperty(Ticker.prototype, 'type', {
- get: function () {
- return this._type;
- },
- set: function (type) {
- this._disposeClock();
- this._type = type;
- this._createClock();
- }
- });
- /**
- * Clean up the current clock source
- * @private
- */
- Ticker.prototype._disposeClock = function () {
- if (this._timeout) {
- clearTimeout(this._timeout);
- this._timeout = null;
- }
- if (this._worker) {
- this._worker.terminate();
- this._worker.onmessage = null;
- this._worker = null;
- }
- };
- /**
- * Clean up
- * @private
- */
- Ticker.prototype.dispose = function () {
- this._disposeClock();
- this._callback = null;
- };
- /**
- * Shim all connect/disconnect and some deprecated methods which are still in
- * some older implementations.
- * @private
- */
- Tone.getContext(function () {
- var nativeConnect = AudioNode.prototype.connect;
- var nativeDisconnect = AudioNode.prototype.disconnect;
- //replace the old connect method
- function toneConnect(B, outNum, inNum) {
- if (B.input) {
- inNum = Tone.defaultArg(inNum, 0);
- if (Tone.isArray(B.input)) {
- this.connect(B.input[inNum]);
- } else {
- this.connect(B.input, outNum, inNum);
- }
- } else {
- try {
- if (B instanceof AudioNode) {
- nativeConnect.call(this, B, outNum, inNum);
- } else {
- nativeConnect.call(this, B, outNum);
- }
- } catch (e) {
- throw new Error('error connecting to node: ' + B + '\n' + e);
- }
- }
- }
- //replace the old disconnect method
- function toneDisconnect(B, outNum, inNum) {
- if (B && B.input && Tone.isArray(B.input)) {
- inNum = Tone.defaultArg(inNum, 0);
- this.disconnect(B.input[inNum], outNum, 0);
- } else if (B && B.input) {
- this.disconnect(B.input, outNum, inNum);
- } else {
- try {
- nativeDisconnect.apply(this, arguments);
- } catch (e) {
- throw new Error('error disconnecting node: ' + B + '\n' + e);
- }
- }
- }
- if (AudioNode.prototype.connect !== toneConnect) {
- AudioNode.prototype.connect = toneConnect;
- AudioNode.prototype.disconnect = toneDisconnect;
- }
- });
- // set the audio context initially
- if (Tone.supported) {
- Tone.context = new Tone.Context();
- } else {
- console.warn('This browser does not support Tone.js');
- }
- return Tone.Context;
- });
- Module(function (Tone) {
- /**
- * @class Tone.AudioNode is a base class for classes which process audio.
- * AudioNodes have inputs and outputs.
- * @param {AudioContext=} context The audio context to use with the class
- * @extends {Tone}
- */
- Tone.AudioNode = function () {
- Tone.call(this);
- //use the default context if one is not passed in
- var options = Tone.defaults(arguments, ['context'], { 'context': Tone.context });
- /**
- * The AudioContext of this instance
- * @private
- * @type {AudioContext}
- */
- this._context = options.context;
- };
- Tone.extend(Tone.AudioNode);
- /**
- * Get the audio context belonging to this instance.
- * @type {AudioNode}
- * @memberOf Tone.AudioNode#
- * @name context
- * @readOnly
- */
- Object.defineProperty(Tone.AudioNode.prototype, 'context', {
- get: function () {
- return this._context;
- }
- });
- /**
- * Create input and outputs for this object.
- * @param {Number} [input=0] The number of inputs
- * @param {Number} [outputs=0] The number of outputs
- * @return {Tone.AudioNode} this
- * @private
- */
- Tone.AudioNode.prototype.createInsOuts = function (inputs, outputs) {
- if (inputs === 1) {
- this.input = this.context.createGain();
- } else if (inputs > 1) {
- this.input = new Array(inputs);
- }
- if (outputs === 1) {
- this.output = this.context.createGain();
- } else if (outputs > 1) {
- this.output = new Array(outputs);
- }
- };
- /**
- * The number of inputs feeding into the AudioNode.
- * For source nodes, this will be 0.
- * @type {Number}
- * @name numberOfInputs
- * @readOnly
- */
- Object.defineProperty(Tone.AudioNode.prototype, 'numberOfInputs', {
- get: function () {
- if (this.input) {
- if (Tone.isArray(this.input)) {
- return this.input.length;
- } else {
- return 1;
- }
- } else {
- return 0;
- }
- }
- });
- /**
- * The number of outputs coming out of the AudioNode.
- * @type {Number}
- * @name numberOfOutputs
- * @readOnly
- */
- Object.defineProperty(Tone.AudioNode.prototype, 'numberOfOutputs', {
- get: function () {
- if (this.output) {
- if (Tone.isArray(this.output)) {
- return this.output.length;
- } else {
- return 1;
- }
- } else {
- return 0;
- }
- }
- });
- /**
- * connect the output of a ToneNode to an AudioParam, AudioNode, or ToneNode
- * @param {Tone | AudioParam | AudioNode} unit
- * @param {number} [outputNum=0] optionally which output to connect from
- * @param {number} [inputNum=0] optionally which input to connect to
- * @returns {Tone.AudioNode} this
- */
- Tone.AudioNode.prototype.connect = function (unit, outputNum, inputNum) {
- if (Tone.isArray(this.output)) {
- outputNum = Tone.defaultArg(outputNum, 0);
- this.output[outputNum].connect(unit, 0, inputNum);
- } else {
- this.output.connect(unit, outputNum, inputNum);
- }
- return this;
- };
- /**
- * disconnect the output
- * @param {Number|AudioNode} output Either the output index to disconnect
- * if the output is an array, or the
- * node to disconnect from.
- * @returns {Tone.AudioNode} this
- */
- Tone.AudioNode.prototype.disconnect = function (destination, outputNum, inputNum) {
- if (Tone.isArray(this.output)) {
- if (Tone.isNumber(destination)) {
- this.output[destination].disconnect();
- } else {
- outputNum = Tone.defaultArg(outputNum, 0);
- this.output[outputNum].disconnect(destination, 0, inputNum);
- }
- } else {
- this.output.disconnect.apply(this.output, arguments);
- }
- };
- /**
- * Connect the output of this node to the rest of the nodes in series.
- * @example
- * //connect a node to an effect, panVol and then to the master output
- * node.chain(effect, panVol, Tone.Master);
- * @param {...AudioParam|Tone|AudioNode} nodes
- * @returns {Tone.AudioNode} this
- */
- Tone.AudioNode.prototype.chain = function () {
- var currentUnit = this;
- for (var i = 0; i < arguments.length; i++) {
- var toUnit = arguments[i];
- currentUnit.connect(toUnit);
- currentUnit = toUnit;
- }
- return this;
- };
- /**
- * connect the output of this node to the rest of the nodes in parallel.
- * @param {...AudioParam|Tone|AudioNode} nodes
- * @returns {Tone.AudioNode} this
- */
- Tone.AudioNode.prototype.fan = function () {
- for (var i = 0; i < arguments.length; i++) {
- this.connect(arguments[i]);
- }
- return this;
- };
- if (window.AudioNode) {
- //give native nodes chain and fan methods
- AudioNode.prototype.chain = Tone.AudioNode.prototype.chain;
- AudioNode.prototype.fan = Tone.AudioNode.prototype.fan;
- }
- /**
- * Dispose and disconnect
- * @return {Tone.AudioNode} this
- */
- Tone.AudioNode.prototype.dispose = function () {
- if (!Tone.isUndef(this.input)) {
- if (this.input instanceof AudioNode) {
- this.input.disconnect();
- }
- this.input = null;
- }
- if (!Tone.isUndef(this.output)) {
- if (this.output instanceof AudioNode) {
- this.output.disconnect();
- }
- this.output = null;
- }
- this._context = null;
- return this;
- };
- return Tone.AudioNode;
- });
- Module(function (Tone) {
-
- /**
- * @class Base class for all Signals. Used Internally.
- *
- * @constructor
- * @extends {Tone}
- */
- Tone.SignalBase = function () {
- Tone.AudioNode.call(this);
- };
- Tone.extend(Tone.SignalBase, Tone.AudioNode);
- /**
- * When signals connect to other signals or AudioParams,
- * they take over the output value of that signal or AudioParam.
- * For all other nodes, the behavior is the same as a default connect
.
- *
- * @override
- * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
- * @param {number} [outputNumber=0] The output number to connect from.
- * @param {number} [inputNumber=0] The input number to connect to.
- * @returns {Tone.SignalBase} this
- */
- Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
- //zero it out so that the signal can have full control
- if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) {
- //cancel changes
- node._param.cancelScheduledValues(0);
- //reset the value
- node._param.value = 0;
- //mark the value as overridden
- node.overridden = true;
- } else if (node instanceof AudioParam) {
- node.cancelScheduledValues(0);
- node.value = 0;
- }
- Tone.AudioNode.prototype.connect.call(this, node, outputNumber, inputNumber);
- return this;
- };
- return Tone.SignalBase;
- });
- Module(function (Tone) {
-
- /**
- * @class Wraps the native Web Audio API
- * [WaveShaperNode](http://webaudio.github.io/web-audio-api/#the-waveshapernode-interface).
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @param {function|Array|Number} mapping The function used to define the values.
- * The mapping function should take two arguments:
- * the first is the value at the current position
- * and the second is the array position.
- * If the argument is an array, that array will be
- * set as the wave shaping function. The input
- * signal is an AudioRange [-1, 1] value and the output
- * signal can take on any numerical values.
- *
- * @param {Number} [bufferLen=1024] The length of the WaveShaperNode buffer.
- * @example
- * var timesTwo = new Tone.WaveShaper(function(val){
- * return val * 2;
- * }, 2048);
- * @example
- * //a waveshaper can also be constructed with an array of values
- * var invert = new Tone.WaveShaper([1, -1]);
- */
- Tone.WaveShaper = function (mapping, bufferLen) {
- Tone.SignalBase.call(this);
- /**
- * the waveshaper
- * @type {WaveShaperNode}
- * @private
- */
- this._shaper = this.input = this.output = this.context.createWaveShaper();
- /**
- * the waveshapers curve
- * @type {Float32Array}
- * @private
- */
- this._curve = null;
- if (Array.isArray(mapping)) {
- this.curve = mapping;
- } else if (isFinite(mapping) || Tone.isUndef(mapping)) {
- this._curve = new Float32Array(Tone.defaultArg(mapping, 1024));
- } else if (Tone.isFunction(mapping)) {
- this._curve = new Float32Array(Tone.defaultArg(bufferLen, 1024));
- this.setMap(mapping);
- }
- };
- Tone.extend(Tone.WaveShaper, Tone.SignalBase);
- /**
- * Uses a mapping function to set the value of the curve.
- * @param {function} mapping The function used to define the values.
- * The mapping function take two arguments:
- * the first is the value at the current position
- * which goes from -1 to 1 over the number of elements
- * in the curve array. The second argument is the array position.
- * @returns {Tone.WaveShaper} this
- * @example
- * //map the input signal from [-1, 1] to [0, 10]
- * shaper.setMap(function(val, index){
- * return (val + 1) * 5;
- * })
- */
- Tone.WaveShaper.prototype.setMap = function (mapping) {
- for (var i = 0, len = this._curve.length; i < len; i++) {
- var normalized = i / (len - 1) * 2 - 1;
- this._curve[i] = mapping(normalized, i);
- }
- this._shaper.curve = this._curve;
- return this;
- };
- /**
- * The array to set as the waveshaper curve. For linear curves
- * array length does not make much difference, but for complex curves
- * longer arrays will provide smoother interpolation.
- * @memberOf Tone.WaveShaper#
- * @type {Array}
- * @name curve
- */
- Object.defineProperty(Tone.WaveShaper.prototype, 'curve', {
- get: function () {
- return this._shaper.curve;
- },
- set: function (mapping) {
- this._curve = new Float32Array(mapping);
- this._shaper.curve = this._curve;
- }
- });
- /**
- * Specifies what type of oversampling (if any) should be used when
- * applying the shaping curve. Can either be "none", "2x" or "4x".
- * @memberOf Tone.WaveShaper#
- * @type {string}
- * @name oversample
- */
- Object.defineProperty(Tone.WaveShaper.prototype, 'oversample', {
- get: function () {
- return this._shaper.oversample;
- },
- set: function (oversampling) {
- if ([
- 'none',
- '2x',
- '4x'
- ].indexOf(oversampling) !== -1) {
- this._shaper.oversample = oversampling;
- } else {
- throw new RangeError('Tone.WaveShaper: oversampling must be either \'none\', \'2x\', or \'4x\'');
- }
- }
- });
- /**
- * Clean up.
- * @returns {Tone.WaveShaper} this
- */
- Tone.WaveShaper.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._shaper.disconnect();
- this._shaper = null;
- this._curve = null;
- return this;
- };
- return Tone.WaveShaper;
- });
- Module(function (Tone) {
- /**
- * @class Tone.TimeBase is a flexible encoding of time
- * which can be evaluated to and from a string.
- * Parsing code modified from https://code.google.com/p/tapdigit/
- * Copyright 2011 2012 Ariya Hidayat, New BSD License
- * @extends {Tone}
- * @param {Time} val The time value as a number or string
- * @param {String=} units Unit values
- * @example
- * Tone.TimeBase(4, "n")
- * Tone.TimeBase(2, "t")
- * Tone.TimeBase("2t").add("1m")
- * Tone.TimeBase("2t + 1m");
- */
- Tone.TimeBase = function (val, units) {
- //allows it to be constructed with or without 'new'
- if (this instanceof Tone.TimeBase) {
- /**
- * Any expressions parsed from the Time
- * @type {Array}
- * @private
- */
- this._expr = this._noOp;
- if (val instanceof Tone.TimeBase) {
- this.copy(val);
- } else if (!Tone.isUndef(units) || Tone.isNumber(val)) {
- //default units
- units = Tone.defaultArg(units, this._defaultUnits);
- var method = this._primaryExpressions[units].method;
- this._expr = method.bind(this, val);
- } else if (Tone.isString(val)) {
- this.set(val);
- } else if (Tone.isUndef(val)) {
- //default expression
- this._expr = this._defaultExpr();
- }
- } else {
- return new Tone.TimeBase(val, units);
- }
- };
- Tone.extend(Tone.TimeBase);
- /**
- * Repalce the current time value with the value
- * given by the expression string.
- * @param {String} exprString
- * @return {Tone.TimeBase} this
- */
- Tone.TimeBase.prototype.set = function (exprString) {
- this._expr = this._parseExprString(exprString);
- return this;
- };
- /**
- * Return a clone of the TimeBase object.
- * @return {Tone.TimeBase} The new cloned Tone.TimeBase
- */
- Tone.TimeBase.prototype.clone = function () {
- var instance = new this.constructor();
- instance.copy(this);
- return instance;
- };
- /**
- * Copies the value of time to this Time
- * @param {Tone.TimeBase} time
- * @return {TimeBase}
- */
- Tone.TimeBase.prototype.copy = function (time) {
- var val = time._expr();
- return this.set(val);
- };
- ///////////////////////////////////////////////////////////////////////////
- // ABSTRACT SYNTAX TREE PARSER
- ///////////////////////////////////////////////////////////////////////////
- /**
- * All the primary expressions.
- * @private
- * @type {Object}
- */
- Tone.TimeBase.prototype._primaryExpressions = {
- 'n': {
- regexp: /^(\d+)n/i,
- method: function (value) {
- value = parseInt(value);
- if (value === 1) {
- return this._beatsToUnits(this._timeSignature());
- } else {
- return this._beatsToUnits(4 / value);
- }
- }
- },
- 't': {
- regexp: /^(\d+)t/i,
- method: function (value) {
- value = parseInt(value);
- return this._beatsToUnits(8 / (parseInt(value) * 3));
- }
- },
- 'm': {
- regexp: /^(\d+)m/i,
- method: function (value) {
- return this._beatsToUnits(parseInt(value) * this._timeSignature());
- }
- },
- 'i': {
- regexp: /^(\d+)i/i,
- method: function (value) {
- return this._ticksToUnits(parseInt(value));
- }
- },
- 'hz': {
- regexp: /^(\d+(?:\.\d+)?)hz/i,
- method: function (value) {
- return this._frequencyToUnits(parseFloat(value));
- }
- },
- 'tr': {
- regexp: /^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,
- method: function (m, q, s) {
- var total = 0;
- if (m && m !== '0') {
- total += this._beatsToUnits(this._timeSignature() * parseFloat(m));
- }
- if (q && q !== '0') {
- total += this._beatsToUnits(parseFloat(q));
- }
- if (s && s !== '0') {
- total += this._beatsToUnits(parseFloat(s) / 4);
- }
- return total;
- }
- },
- 's': {
- regexp: /^(\d+(?:\.\d+)?s)/,
- method: function (value) {
- return this._secondsToUnits(parseFloat(value));
- }
- },
- 'samples': {
- regexp: /^(\d+)samples/,
- method: function (value) {
- return parseInt(value) / this.context.sampleRate;
- }
- },
- 'default': {
- regexp: /^(\d+(?:\.\d+)?)/,
- method: function (value) {
- return this._primaryExpressions[this._defaultUnits].method.call(this, value);
- }
- }
- };
- /**
- * All the binary expressions that TimeBase can accept.
- * @private
- * @type {Object}
- */
- Tone.TimeBase.prototype._binaryExpressions = {
- '+': {
- regexp: /^\+/,
- precedence: 2,
- method: function (lh, rh) {
- return lh() + rh();
- }
- },
- '-': {
- regexp: /^\-/,
- precedence: 2,
- method: function (lh, rh) {
- return lh() - rh();
- }
- },
- '*': {
- regexp: /^\*/,
- precedence: 1,
- method: function (lh, rh) {
- return lh() * rh();
- }
- },
- '/': {
- regexp: /^\//,
- precedence: 1,
- method: function (lh, rh) {
- return lh() / rh();
- }
- }
- };
- /**
- * All the unary expressions.
- * @private
- * @type {Object}
- */
- Tone.TimeBase.prototype._unaryExpressions = {
- 'neg': {
- regexp: /^\-/,
- method: function (lh) {
- return -lh();
- }
- }
- };
- /**
- * Syntactic glue which holds expressions together
- * @private
- * @type {Object}
- */
- Tone.TimeBase.prototype._syntaxGlue = {
- '(': { regexp: /^\(/ },
- ')': { regexp: /^\)/ }
- };
- /**
- * tokenize the expression based on the Expressions object
- * @param {string} expr
- * @return {Object} returns two methods on the tokenized list, next and peek
- * @private
- */
- Tone.TimeBase.prototype._tokenize = function (expr) {
- var position = -1;
- var tokens = [];
- while (expr.length > 0) {
- expr = expr.trim();
- var token = getNextToken(expr, this);
- tokens.push(token);
- expr = expr.substr(token.value.length);
- }
- function getNextToken(expr, context) {
- var expressions = [
- '_binaryExpressions',
- '_unaryExpressions',
- '_primaryExpressions',
- '_syntaxGlue'
- ];
- for (var i = 0; i < expressions.length; i++) {
- var group = context[expressions[i]];
- for (var opName in group) {
- var op = group[opName];
- var reg = op.regexp;
- var match = expr.match(reg);
- if (match !== null) {
- return {
- method: op.method,
- precedence: op.precedence,
- regexp: op.regexp,
- value: match[0]
- };
- }
- }
- }
- throw new SyntaxError('Tone.TimeBase: Unexpected token ' + expr);
- }
- return {
- next: function () {
- return tokens[++position];
- },
- peek: function () {
- return tokens[position + 1];
- }
- };
- };
- /**
- * Given a token, find the value within the groupName
- * @param {Object} token
- * @param {String} groupName
- * @param {Number} precedence
- * @private
- */
- Tone.TimeBase.prototype._matchGroup = function (token, group, prec) {
- var ret = false;
- if (!Tone.isUndef(token)) {
- for (var opName in group) {
- var op = group[opName];
- if (op.regexp.test(token.value)) {
- if (!Tone.isUndef(prec)) {
- if (op.precedence === prec) {
- return op;
- }
- } else {
- return op;
- }
- }
- }
- }
- return ret;
- };
- /**
- * Match a binary expression given the token and the precedence
- * @param {Lexer} lexer
- * @param {Number} precedence
- * @private
- */
- Tone.TimeBase.prototype._parseBinary = function (lexer, precedence) {
- if (Tone.isUndef(precedence)) {
- precedence = 2;
- }
- var expr;
- if (precedence < 0) {
- expr = this._parseUnary(lexer);
- } else {
- expr = this._parseBinary(lexer, precedence - 1);
- }
- var token = lexer.peek();
- while (token && this._matchGroup(token, this._binaryExpressions, precedence)) {
- token = lexer.next();
- expr = token.method.bind(this, expr, this._parseBinary(lexer, precedence - 1));
- token = lexer.peek();
- }
- return expr;
- };
- /**
- * Match a unary expression.
- * @param {Lexer} lexer
- * @private
- */
- Tone.TimeBase.prototype._parseUnary = function (lexer) {
- var token, expr;
- token = lexer.peek();
- var op = this._matchGroup(token, this._unaryExpressions);
- if (op) {
- token = lexer.next();
- expr = this._parseUnary(lexer);
- return op.method.bind(this, expr);
- }
- return this._parsePrimary(lexer);
- };
- /**
- * Match a primary expression (a value).
- * @param {Lexer} lexer
- * @private
- */
- Tone.TimeBase.prototype._parsePrimary = function (lexer) {
- var token, expr;
- token = lexer.peek();
- if (Tone.isUndef(token)) {
- throw new SyntaxError('Tone.TimeBase: Unexpected end of expression');
- }
- if (this._matchGroup(token, this._primaryExpressions)) {
- token = lexer.next();
- var matching = token.value.match(token.regexp);
- return token.method.bind(this, matching[1], matching[2], matching[3]);
- }
- if (token && token.value === '(') {
- lexer.next();
- expr = this._parseBinary(lexer);
- token = lexer.next();
- if (!(token && token.value === ')')) {
- throw new SyntaxError('Expected )');
- }
- return expr;
- }
- throw new SyntaxError('Tone.TimeBase: Cannot process token ' + token.value);
- };
- /**
- * Recursively parse the string expression into a syntax tree.
- * @param {string} expr
- * @return {Function} the bound method to be evaluated later
- * @private
- */
- Tone.TimeBase.prototype._parseExprString = function (exprString) {
- if (!Tone.isString(exprString)) {
- exprString = exprString.toString();
- }
- var lexer = this._tokenize(exprString);
- var tree = this._parseBinary(lexer);
- return tree;
- };
- ///////////////////////////////////////////////////////////////////////////
- // DEFAULTS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * The initial expression value
- * @return {Number} The initial value 0
- * @private
- */
- Tone.TimeBase.prototype._noOp = function () {
- return 0;
- };
- /**
- * The default expression value if no arguments are given
- * @private
- */
- Tone.TimeBase.prototype._defaultExpr = function () {
- return this._noOp;
- };
- /**
- * The default units if none are given.
- * @private
- */
- Tone.TimeBase.prototype._defaultUnits = 's';
- ///////////////////////////////////////////////////////////////////////////
- // UNIT CONVERSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Returns the value of a frequency in the current units
- * @param {Frequency} freq
- * @return {Number}
- * @private
- */
- Tone.TimeBase.prototype._frequencyToUnits = function (freq) {
- return 1 / freq;
- };
- /**
- * Return the value of the beats in the current units
- * @param {Number} beats
- * @return {Number}
- * @private
- */
- Tone.TimeBase.prototype._beatsToUnits = function (beats) {
- return 60 / Tone.Transport.bpm.value * beats;
- };
- /**
- * Returns the value of a second in the current units
- * @param {Seconds} seconds
- * @return {Number}
- * @private
- */
- Tone.TimeBase.prototype._secondsToUnits = function (seconds) {
- return seconds;
- };
- /**
- * Returns the value of a tick in the current time units
- * @param {Ticks} ticks
- * @return {Number}
- * @private
- */
- Tone.TimeBase.prototype._ticksToUnits = function (ticks) {
- return ticks * (this._beatsToUnits(1) / Tone.Transport.PPQ);
- };
- /**
- * Return the time signature.
- * @return {Number}
- * @private
- */
- Tone.TimeBase.prototype._timeSignature = function () {
- return Tone.Transport.timeSignature;
- };
- ///////////////////////////////////////////////////////////////////////////
- // EXPRESSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Push an expression onto the expression list
- * @param {Time} val
- * @param {String} type
- * @param {String} units
- * @return {Tone.TimeBase}
- * @private
- */
- Tone.TimeBase.prototype._pushExpr = function (val, name, units) {
- //create the expression
- if (!(val instanceof Tone.TimeBase)) {
- val = new this.constructor(val, units);
- }
- this._expr = this._binaryExpressions[name].method.bind(this, this._expr, val._expr);
- return this;
- };
- /**
- * Add to the current value.
- * @param {Time} val The value to add
- * @param {String=} units Optional units to use with the value.
- * @return {Tone.TimeBase} this
- * @example
- * Tone.TimeBase("2m").add("1m"); //"3m"
- */
- Tone.TimeBase.prototype.add = function (val, units) {
- return this._pushExpr(val, '+', units);
- };
- /**
- * Subtract the value from the current time.
- * @param {Time} val The value to subtract
- * @param {String=} units Optional units to use with the value.
- * @return {Tone.TimeBase} this
- * @example
- * Tone.TimeBase("2m").sub("1m"); //"1m"
- */
- Tone.TimeBase.prototype.sub = function (val, units) {
- return this._pushExpr(val, '-', units);
- };
- /**
- * Multiply the current value by the given time.
- * @param {Time} val The value to multiply
- * @param {String=} units Optional units to use with the value.
- * @return {Tone.TimeBase} this
- * @example
- * Tone.TimeBase("2m").mult("2"); //"4m"
- */
- Tone.TimeBase.prototype.mult = function (val, units) {
- return this._pushExpr(val, '*', units);
- };
- /**
- * Divide the current value by the given time.
- * @param {Time} val The value to divide by
- * @param {String=} units Optional units to use with the value.
- * @return {Tone.TimeBase} this
- * @example
- * Tone.TimeBase("2m").div(2); //"1m"
- */
- Tone.TimeBase.prototype.div = function (val, units) {
- return this._pushExpr(val, '/', units);
- };
- /**
- * Evaluate the time value. Returns the time
- * in seconds.
- * @return {Seconds}
- */
- Tone.TimeBase.prototype.valueOf = function () {
- return this._expr();
- };
- /**
- * Clean up
- * @return {Tone.TimeBase} this
- */
- Tone.TimeBase.prototype.dispose = function () {
- this._expr = null;
- };
- return Tone.TimeBase;
- });
- Module(function (Tone) {
- /**
- * @class Tone.Time is a primitive type for encoding Time values.
- * Eventually all time values are evaluated to seconds
- * using the `eval` method. Tone.Time can be constructed
- * with or without the `new` keyword. Tone.Time can be passed
- * into the parameter of any method which takes time as an argument.
- * @constructor
- * @extends {Tone.TimeBase}
- * @param {String|Number} val The time value.
- * @param {String=} units The units of the value.
- * @example
- * var t = Tone.Time("4n");//encodes a quarter note
- * t.mult(4); // multiply that value by 4
- * t.toNotation(); //returns "1m"
- */
- Tone.Time = function (val, units) {
- if (this instanceof Tone.Time) {
- /**
- * If the current clock time should
- * be added to the output
- * @type {Boolean}
- * @private
- */
- this._plusNow = false;
- Tone.TimeBase.call(this, val, units);
- } else {
- return new Tone.Time(val, units);
- }
- };
- Tone.extend(Tone.Time, Tone.TimeBase);
- //clone the expressions so that
- //we can add more without modifying the original
- Tone.Time.prototype._unaryExpressions = Object.create(Tone.TimeBase.prototype._unaryExpressions);
- /*
- * Adds an additional unary expression
- * which quantizes values to the next subdivision
- * @type {Object}
- * @private
- */
- Tone.Time.prototype._unaryExpressions.quantize = {
- regexp: /^@/,
- method: function (rh) {
- return Tone.Transport.nextSubdivision(rh());
- }
- };
- /*
- * Adds an additional unary expression
- * which adds the current clock time.
- * @type {Object}
- * @private
- */
- Tone.Time.prototype._unaryExpressions.now = {
- regexp: /^\+/,
- method: function (lh) {
- this._plusNow = true;
- return lh();
- }
- };
- /**
- * Quantize the time by the given subdivision. Optionally add a
- * percentage which will move the time value towards the ideal
- * quantized value by that percentage.
- * @param {Number|Time} val The subdivision to quantize to
- * @param {NormalRange} [percent=1] Move the time value
- * towards the quantized value by
- * a percentage.
- * @return {Tone.Time} this
- * @example
- * Tone.Time(21).quantize(2) //returns 22
- * Tone.Time(0.6).quantize("4n", 0.5) //returns 0.55
- */
- Tone.Time.prototype.quantize = function (subdiv, percent) {
- percent = Tone.defaultArg(percent, 1);
- this._expr = function (expr, subdivision, percent) {
- expr = expr();
- subdivision = subdivision.toSeconds();
- var multiple = Math.round(expr / subdivision);
- var ideal = multiple * subdivision;
- var diff = ideal - expr;
- return expr + diff * percent;
- }.bind(this, this._expr, new this.constructor(subdiv), percent);
- return this;
- };
- /**
- * Adds the clock time to the time expression at the
- * moment of evaluation.
- * @return {Tone.Time} this
- */
- Tone.Time.prototype.addNow = function () {
- this._plusNow = true;
- return this;
- };
- /**
- * Override the default value return when no arguments are passed in.
- * The default value is 'now'
- * @override
- * @private
- */
- Tone.Time.prototype._defaultExpr = function () {
- this._plusNow = true;
- return this._noOp;
- };
- /**
- * Copies the value of time to this Time
- * @param {Tone.Time} time
- * @return {Time}
- */
- Tone.Time.prototype.copy = function (time) {
- Tone.TimeBase.prototype.copy.call(this, time);
- this._plusNow = time._plusNow;
- return this;
- };
- //CONVERSIONS//////////////////////////////////////////////////////////////
- /**
- * Convert a Time to Notation. Values will be thresholded to the nearest 128th note.
- * @return {Notation}
- * @example
- * //if the Transport is at 120bpm:
- * Tone.Time(2).toNotation();//returns "1m"
- */
- Tone.Time.prototype.toNotation = function () {
- var time = this.toSeconds();
- var testNotations = [
- '1m',
- '2n',
- '4n',
- '8n',
- '16n',
- '32n',
- '64n',
- '128n'
- ];
- var retNotation = this._toNotationHelper(time, testNotations);
- //try the same thing but with tripelets
- var testTripletNotations = [
- '1m',
- '2n',
- '2t',
- '4n',
- '4t',
- '8n',
- '8t',
- '16n',
- '16t',
- '32n',
- '32t',
- '64n',
- '64t',
- '128n'
- ];
- var retTripletNotation = this._toNotationHelper(time, testTripletNotations);
- //choose the simpler expression of the two
- if (retTripletNotation.split('+').length < retNotation.split('+').length) {
- return retTripletNotation;
- } else {
- return retNotation;
- }
- };
- /**
- * Helper method for Tone.toNotation
- * @param {Number} units
- * @param {Array} testNotations
- * @return {String}
- * @private
- */
- Tone.Time.prototype._toNotationHelper = function (units, testNotations) {
- //the threshold is the last value in the array
- var threshold = this._notationToUnits(testNotations[testNotations.length - 1]);
- var retNotation = '';
- for (var i = 0; i < testNotations.length; i++) {
- var notationTime = this._notationToUnits(testNotations[i]);
- //account for floating point errors (i.e. round up if the value is 0.999999)
- var multiple = units / notationTime;
- var floatingPointError = 0.000001;
- if (1 - multiple % 1 < floatingPointError) {
- multiple += floatingPointError;
- }
- multiple = Math.floor(multiple);
- if (multiple > 0) {
- if (multiple === 1) {
- retNotation += testNotations[i];
- } else {
- retNotation += multiple.toString() + '*' + testNotations[i];
- }
- units -= multiple * notationTime;
- if (units < threshold) {
- break;
- } else {
- retNotation += ' + ';
- }
- }
- }
- if (retNotation === '') {
- retNotation = '0';
- }
- return retNotation;
- };
- /**
- * Convert a notation value to the current units
- * @param {Notation} notation
- * @return {Number}
- * @private
- */
- Tone.Time.prototype._notationToUnits = function (notation) {
- var primaryExprs = this._primaryExpressions;
- var notationExprs = [
- primaryExprs.n,
- primaryExprs.t,
- primaryExprs.m
- ];
- for (var i = 0; i < notationExprs.length; i++) {
- var expr = notationExprs[i];
- var match = notation.match(expr.regexp);
- if (match) {
- return expr.method.call(this, match[1]);
- }
- }
- };
- /**
- * Return the time encoded as Bars:Beats:Sixteenths.
- * @return {BarsBeatsSixteenths}
- */
- Tone.Time.prototype.toBarsBeatsSixteenths = function () {
- var quarterTime = this._beatsToUnits(1);
- var quarters = this.toSeconds() / quarterTime;
- var measures = Math.floor(quarters / this._timeSignature());
- var sixteenths = quarters % 1 * 4;
- quarters = Math.floor(quarters) % this._timeSignature();
- sixteenths = sixteenths.toString();
- if (sixteenths.length > 3) {
- // the additional parseFloat removes insignificant trailing zeroes
- sixteenths = parseFloat(parseFloat(sixteenths).toFixed(3));
- }
- var progress = [
- measures,
- quarters,
- sixteenths
- ];
- return progress.join(':');
- };
- /**
- * Return the time in ticks.
- * @return {Ticks}
- */
- Tone.Time.prototype.toTicks = function () {
- var quarterTime = this._beatsToUnits(1);
- var quarters = this.valueOf() / quarterTime;
- return Math.floor(quarters * Tone.Transport.PPQ);
- };
- /**
- * Return the time in samples
- * @return {Samples}
- */
- Tone.Time.prototype.toSamples = function () {
- return this.toSeconds() * this.context.sampleRate;
- };
- /**
- * Return the time as a frequency value
- * @return {Frequency}
- * @example
- * Tone.Time(2).toFrequency(); //0.5
- */
- Tone.Time.prototype.toFrequency = function () {
- return 1 / this.toSeconds();
- };
- /**
- * Return the time in seconds.
- * @return {Seconds}
- */
- Tone.Time.prototype.toSeconds = function () {
- return this.valueOf();
- };
- /**
- * Return the time in milliseconds.
- * @return {Milliseconds}
- */
- Tone.Time.prototype.toMilliseconds = function () {
- return this.toSeconds() * 1000;
- };
- /**
- * Return the time in seconds.
- * @return {Seconds}
- */
- Tone.Time.prototype.valueOf = function () {
- var val = this._expr();
- return val + (this._plusNow ? this.now() : 0);
- };
- return Tone.Time;
- });
- Module(function (Tone) {
- /**
- * @class Tone.Frequency is a primitive type for encoding Frequency values.
- * Eventually all time values are evaluated to hertz
- * using the `eval` method.
- * @constructor
- * @extends {Tone.TimeBase}
- * @param {String|Number} val The time value.
- * @param {String=} units The units of the value.
- * @example
- * Tone.Frequency("C3") // 261
- * Tone.Frequency(38, "midi") //
- * Tone.Frequency("C3").transpose(4);
- */
- Tone.Frequency = function (val, units) {
- if (this instanceof Tone.Frequency) {
- Tone.TimeBase.call(this, val, units);
- } else {
- return new Tone.Frequency(val, units);
- }
- };
- Tone.extend(Tone.Frequency, Tone.TimeBase);
- ///////////////////////////////////////////////////////////////////////////
- // AUGMENT BASE EXPRESSIONS
- ///////////////////////////////////////////////////////////////////////////
- //clone the expressions so that
- //we can add more without modifying the original
- Tone.Frequency.prototype._primaryExpressions = Object.create(Tone.TimeBase.prototype._primaryExpressions);
- /*
- * midi type primary expression
- * @type {Object}
- * @private
- */
- Tone.Frequency.prototype._primaryExpressions.midi = {
- regexp: /^(\d+(?:\.\d+)?midi)/,
- method: function (value) {
- return this.midiToFrequency(value);
- }
- };
- /*
- * note type primary expression
- * @type {Object}
- * @private
- */
- Tone.Frequency.prototype._primaryExpressions.note = {
- regexp: /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i,
- method: function (pitch, octave) {
- var index = noteToScaleIndex[pitch.toLowerCase()];
- var noteNumber = index + (parseInt(octave) + 1) * 12;
- return this.midiToFrequency(noteNumber);
- }
- };
- /*
- * BeatsBarsSixteenths type primary expression
- * @type {Object}
- * @private
- */
- Tone.Frequency.prototype._primaryExpressions.tr = {
- regexp: /^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,
- method: function (m, q, s) {
- var total = 1;
- if (m && m !== '0') {
- total *= this._beatsToUnits(this._timeSignature() * parseFloat(m));
- }
- if (q && q !== '0') {
- total *= this._beatsToUnits(parseFloat(q));
- }
- if (s && s !== '0') {
- total *= this._beatsToUnits(parseFloat(s) / 4);
- }
- return total;
- }
- };
- ///////////////////////////////////////////////////////////////////////////
- // EXPRESSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Transposes the frequency by the given number of semitones.
- * @param {Interval} interval
- * @return {Tone.Frequency} this
- * @example
- * Tone.Frequency("A4").transpose(3); //"C5"
- */
- Tone.Frequency.prototype.transpose = function (interval) {
- this._expr = function (expr, interval) {
- var val = expr();
- return val * Tone.intervalToFrequencyRatio(interval);
- }.bind(this, this._expr, interval);
- return this;
- };
- /**
- * Takes an array of semitone intervals and returns
- * an array of frequencies transposed by those intervals.
- * @param {Array} intervals
- * @return {Tone.Frequency} this
- * @example
- * Tone.Frequency("A4").harmonize([0, 3, 7]); //["A4", "C5", "E5"]
- */
- Tone.Frequency.prototype.harmonize = function (intervals) {
- this._expr = function (expr, intervals) {
- var val = expr();
- var ret = [];
- for (var i = 0; i < intervals.length; i++) {
- ret[i] = val * Tone.intervalToFrequencyRatio(intervals[i]);
- }
- return ret;
- }.bind(this, this._expr, intervals);
- return this;
- };
- ///////////////////////////////////////////////////////////////////////////
- // UNIT CONVERSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Return the value of the frequency as a MIDI note
- * @return {MIDI}
- * @example
- * Tone.Frequency("C4").toMidi(); //60
- */
- Tone.Frequency.prototype.toMidi = function () {
- return this.frequencyToMidi(this.valueOf());
- };
- /**
- * Return the value of the frequency in Scientific Pitch Notation
- * @return {Note}
- * @example
- * Tone.Frequency(69, "midi").toNote(); //"A4"
- */
- Tone.Frequency.prototype.toNote = function () {
- var freq = this.valueOf();
- var log = Math.log(freq / Tone.Frequency.A4) / Math.LN2;
- var noteNumber = Math.round(12 * log) + 57;
- var octave = Math.floor(noteNumber / 12);
- if (octave < 0) {
- noteNumber += -12 * octave;
- }
- var noteName = scaleIndexToNote[noteNumber % 12];
- return noteName + octave.toString();
- };
- /**
- * Return the duration of one cycle in seconds.
- * @return {Seconds}
- */
- Tone.Frequency.prototype.toSeconds = function () {
- return 1 / this.valueOf();
- };
- /**
- * Return the value in Hertz
- * @return {Frequency}
- */
- Tone.Frequency.prototype.toFrequency = function () {
- return this.valueOf();
- };
- /**
- * Return the duration of one cycle in ticks
- * @return {Ticks}
- */
- Tone.Frequency.prototype.toTicks = function () {
- var quarterTime = this._beatsToUnits(1);
- var quarters = this.valueOf() / quarterTime;
- return Math.floor(quarters * Tone.Transport.PPQ);
- };
- ///////////////////////////////////////////////////////////////////////////
- // UNIT CONVERSIONS HELPERS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Returns the value of a frequency in the current units
- * @param {Frequency} freq
- * @return {Number}
- * @private
- */
- Tone.Frequency.prototype._frequencyToUnits = function (freq) {
- return freq;
- };
- /**
- * Returns the value of a tick in the current time units
- * @param {Ticks} ticks
- * @return {Number}
- * @private
- */
- Tone.Frequency.prototype._ticksToUnits = function (ticks) {
- return 1 / (ticks * 60 / (Tone.Transport.bpm.value * Tone.Transport.PPQ));
- };
- /**
- * Return the value of the beats in the current units
- * @param {Number} beats
- * @return {Number}
- * @private
- */
- Tone.Frequency.prototype._beatsToUnits = function (beats) {
- return 1 / Tone.TimeBase.prototype._beatsToUnits.call(this, beats);
- };
- /**
- * Returns the value of a second in the current units
- * @param {Seconds} seconds
- * @return {Number}
- * @private
- */
- Tone.Frequency.prototype._secondsToUnits = function (seconds) {
- return 1 / seconds;
- };
- /**
- * The default units if none are given.
- * @private
- */
- Tone.Frequency.prototype._defaultUnits = 'hz';
- ///////////////////////////////////////////////////////////////////////////
- // FREQUENCY CONVERSIONS
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Note to scale index
- * @type {Object}
- */
- var noteToScaleIndex = {
- 'cbb': -2,
- 'cb': -1,
- 'c': 0,
- 'c#': 1,
- 'cx': 2,
- 'dbb': 0,
- 'db': 1,
- 'd': 2,
- 'd#': 3,
- 'dx': 4,
- 'ebb': 2,
- 'eb': 3,
- 'e': 4,
- 'e#': 5,
- 'ex': 6,
- 'fbb': 3,
- 'fb': 4,
- 'f': 5,
- 'f#': 6,
- 'fx': 7,
- 'gbb': 5,
- 'gb': 6,
- 'g': 7,
- 'g#': 8,
- 'gx': 9,
- 'abb': 7,
- 'ab': 8,
- 'a': 9,
- 'a#': 10,
- 'ax': 11,
- 'bbb': 9,
- 'bb': 10,
- 'b': 11,
- 'b#': 12,
- 'bx': 13
- };
- /**
- * scale index to note (sharps)
- * @type {Array}
- */
- var scaleIndexToNote = [
- 'C',
- 'C#',
- 'D',
- 'D#',
- 'E',
- 'F',
- 'F#',
- 'G',
- 'G#',
- 'A',
- 'A#',
- 'B'
- ];
- /**
- * The [concert pitch](https://en.wikipedia.org/wiki/Concert_pitch)
- * A4's values in Hertz.
- * @type {Frequency}
- * @static
- */
- Tone.Frequency.A4 = 440;
- /**
- * Convert a MIDI note to frequency value.
- * @param {MIDI} midi The midi number to convert.
- * @return {Frequency} the corresponding frequency value
- * @example
- * tone.midiToFrequency(69); // returns 440
- */
- Tone.Frequency.prototype.midiToFrequency = function (midi) {
- return Tone.Frequency.A4 * Math.pow(2, (midi - 69) / 12);
- };
- /**
- * Convert a frequency value to a MIDI note.
- * @param {Frequency} frequency The value to frequency value to convert.
- * @returns {MIDI}
- * @example
- * tone.midiToFrequency(440); // returns 69
- */
- Tone.Frequency.prototype.frequencyToMidi = function (frequency) {
- return 69 + Math.round(12 * Math.log(frequency / Tone.Frequency.A4) / Math.LN2);
- };
- return Tone.Frequency;
- });
- Module(function (Tone) {
- /**
- * @class Tone.TransportTime is a the time along the Transport's
- * timeline. It is similar to Tone.Time, but instead of evaluating
- * against the AudioContext's clock, it is evaluated against
- * the Transport's position. See [TransportTime wiki](https://github.com/Tonejs/Tone.js/wiki/TransportTime).
- * @constructor
- * @param {Time} val The time value as a number or string
- * @param {String=} units Unit values
- * @extends {Tone.Time}
- */
- Tone.TransportTime = function (val, units) {
- if (this instanceof Tone.TransportTime) {
- Tone.Time.call(this, val, units);
- } else {
- return new Tone.TransportTime(val, units);
- }
- };
- Tone.extend(Tone.TransportTime, Tone.Time);
- //clone the expressions so that
- //we can add more without modifying the original
- Tone.TransportTime.prototype._unaryExpressions = Object.create(Tone.Time.prototype._unaryExpressions);
- /**
- * Adds an additional unary expression
- * which quantizes values to the next subdivision
- * @type {Object}
- * @private
- */
- Tone.TransportTime.prototype._unaryExpressions.quantize = {
- regexp: /^@/,
- method: function (rh) {
- var subdivision = this._secondsToTicks(rh());
- var multiple = Math.ceil(Tone.Transport.ticks / subdivision);
- return this._ticksToUnits(multiple * subdivision);
- }
- };
- /**
- * Convert seconds into ticks
- * @param {Seconds} seconds
- * @return {Ticks}
- * @private
- */
- Tone.TransportTime.prototype._secondsToTicks = function (seconds) {
- var quarterTime = this._beatsToUnits(1);
- var quarters = seconds / quarterTime;
- return Math.round(quarters * Tone.Transport.PPQ);
- };
- /**
- * Evaluate the time expression. Returns values in ticks
- * @return {Ticks}
- */
- Tone.TransportTime.prototype.valueOf = function () {
- var val = this._secondsToTicks(this._expr());
- return val + (this._plusNow ? Tone.Transport.ticks : 0);
- };
- /**
- * Return the time in ticks.
- * @return {Ticks}
- */
- Tone.TransportTime.prototype.toTicks = function () {
- return this.valueOf();
- };
- /**
- * Return the time in seconds.
- * @return {Seconds}
- */
- Tone.TransportTime.prototype.toSeconds = function () {
- var val = this._expr();
- return val + (this._plusNow ? Tone.Transport.seconds : 0);
- };
- /**
- * Return the time as a frequency value
- * @return {Frequency}
- */
- Tone.TransportTime.prototype.toFrequency = function () {
- return 1 / this.toSeconds();
- };
- return Tone.TransportTime;
- });
- Module(function (Tone) {
- ///////////////////////////////////////////////////////////////////////////
- // TYPES
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Units which a value can take on.
- * @enum {String}
- */
- Tone.Type = {
- /**
- * Default units
- * @typedef {Default}
- */
- Default: 'number',
- /**
- * Time can be described in a number of ways. Read more [Time](https://github.com/Tonejs/Tone.js/wiki/Time).
- *
- * * Numbers, which will be taken literally as the time (in seconds).
- * * Notation, ("4n", "8t") describes time in BPM and time signature relative values.
- * * TransportTime, ("4:3:2") will also provide tempo and time signature relative times
- * in the form BARS:QUARTERS:SIXTEENTHS.
- * * Frequency, ("8hz") is converted to the length of the cycle in seconds.
- * * Now-Relative, ("+1") prefix any of the above with "+" and it will be interpreted as
- * "the current time plus whatever expression follows".
- * * Expressions, ("3:0 + 2 - (1m / 7)") any of the above can also be combined
- * into a mathematical expression which will be evaluated to compute the desired time.
- * * No Argument, for methods which accept time, no argument will be interpreted as
- * "now" (i.e. the currentTime).
- *
- * @typedef {Time}
- */
- Time: 'time',
- /**
- * Frequency can be described similar to time, except ultimately the
- * values are converted to frequency instead of seconds. A number
- * is taken literally as the value in hertz. Additionally any of the
- * Time encodings can be used. Note names in the form
- * of NOTE OCTAVE (i.e. C4) are also accepted and converted to their
- * frequency value.
- * @typedef {Frequency}
- */
- Frequency: 'frequency',
- /**
- * TransportTime describes a position along the Transport's timeline. It is
- * similar to Time in that it uses all the same encodings, but TransportTime specifically
- * pertains to the Transport's timeline, which is startable, stoppable, loopable, and seekable.
- * [Read more](https://github.com/Tonejs/Tone.js/wiki/TransportTime)
- * @typedef {TransportTime}
- */
- TransportTime: 'transportTime',
- /**
- * Ticks are the basic subunit of the Transport. They are
- * the smallest unit of time that the Transport supports.
- * @typedef {Ticks}
- */
- Ticks: 'ticks',
- /**
- * Normal values are within the range [0, 1].
- * @typedef {NormalRange}
- */
- NormalRange: 'normalRange',
- /**
- * AudioRange values are between [-1, 1].
- * @typedef {AudioRange}
- */
- AudioRange: 'audioRange',
- /**
- * Decibels are a logarithmic unit of measurement which is useful for volume
- * because of the logarithmic way that we perceive loudness. 0 decibels
- * means no change in volume. -10db is approximately half as loud and 10db
- * is twice is loud.
- * @typedef {Decibels}
- */
- Decibels: 'db',
- /**
- * Half-step note increments, i.e. 12 is an octave above the root. and 1 is a half-step up.
- * @typedef {Interval}
- */
- Interval: 'interval',
- /**
- * Beats per minute.
- * @typedef {BPM}
- */
- BPM: 'bpm',
- /**
- * The value must be greater than or equal to 0.
- * @typedef {Positive}
- */
- Positive: 'positive',
- /**
- * Gain is the ratio between input and output of a signal.
- * A gain of 0 is the same as silencing the signal. A gain of
- * 1, causes no change to the incoming signal.
- * @typedef {Gain}
- */
- Gain: 'gain',
- /**
- * A cent is a hundredth of a semitone.
- * @typedef {Cents}
- */
- Cents: 'cents',
- /**
- * Angle between 0 and 360.
- * @typedef {Degrees}
- */
- Degrees: 'degrees',
- /**
- * A number representing a midi note.
- * @typedef {MIDI}
- */
- MIDI: 'midi',
- /**
- * A colon-separated representation of time in the form of
- * Bars:Beats:Sixteenths.
- * @typedef {BarsBeatsSixteenths}
- */
- BarsBeatsSixteenths: 'barsBeatsSixteenths',
- /**
- * Sampling is the reduction of a continuous signal to a discrete signal.
- * Audio is typically sampled 44100 times per second.
- * @typedef {Samples}
- */
- Samples: 'samples',
- /**
- * Hertz are a frequency representation defined as one cycle per second.
- * @typedef {Hertz}
- */
- Hertz: 'hertz',
- /**
- * A frequency represented by a letter name,
- * accidental and octave. This system is known as
- * [Scientific Pitch Notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation).
- * @typedef {Note}
- */
- Note: 'note',
- /**
- * One millisecond is a thousandth of a second.
- * @typedef {Milliseconds}
- */
- Milliseconds: 'milliseconds',
- /**
- * Seconds are the time unit of the AudioContext. In the end,
- * all values need to be evaluated to seconds.
- * @typedef {Seconds}
- */
- Seconds: 'seconds',
- /**
- * A string representing a duration relative to a measure.
- * * "4n" = quarter note
- * * "2m" = two measures
- * * "8t" = eighth-note triplet
- * @typedef {Notation}
- */
- Notation: 'notation'
- };
- ///////////////////////////////////////////////////////////////////////////
- // AUGMENT TONE's PROTOTYPE
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Convert Time into seconds.
- *
- * Unlike the method which it overrides, this takes into account
- * transporttime and musical notation.
- *
- * Time : 1.40
- * Notation: 4n or 1m or 2t
- * Now Relative: +3n
- * Math: 3n+16n or even complicated expressions ((3n*2)/6 + 1)
- *
- * @param {Time} time
- * @return {Seconds}
- */
- Tone.prototype.toSeconds = function (time) {
- if (Tone.isNumber(time)) {
- return time;
- } else if (Tone.isUndef(time)) {
- return this.now();
- } else if (Tone.isString(time)) {
- return new Tone.Time(time).toSeconds();
- } else if (time instanceof Tone.TimeBase) {
- return time.toSeconds();
- }
- };
- /**
- * Convert a frequency representation into a number.
- * @param {Frequency} freq
- * @return {Hertz} the frequency in hertz
- */
- Tone.prototype.toFrequency = function (freq) {
- if (Tone.isNumber(freq)) {
- return freq;
- } else if (Tone.isString(freq) || Tone.isUndef(freq)) {
- return new Tone.Frequency(freq).valueOf();
- } else if (freq instanceof Tone.TimeBase) {
- return freq.toFrequency();
- }
- };
- /**
- * Convert a time representation into ticks.
- * @param {Time} time
- * @return {Ticks} the time in ticks
- */
- Tone.prototype.toTicks = function (time) {
- if (Tone.isNumber(time) || Tone.isString(time)) {
- return new Tone.TransportTime(time).toTicks();
- } else if (Tone.isUndef(time)) {
- return Tone.Transport.ticks;
- } else if (time instanceof Tone.TimeBase) {
- return time.toTicks();
- }
- };
- return Tone;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Param wraps the native Web Audio's AudioParam to provide
- * additional unit conversion functionality. It also
- * serves as a base-class for classes which have a single,
- * automatable parameter.
- * @extends {Tone.AudioNode}
- * @param {AudioParam} param The parameter to wrap.
- * @param {Tone.Type} units The units of the audio param.
- * @param {Boolean} convert If the param should be converted.
- */
- Tone.Param = function () {
- var options = Tone.defaults(arguments, [
- 'param',
- 'units',
- 'convert'
- ], Tone.Param);
- Tone.AudioNode.call(this);
- /**
- * The native parameter to control
- * @type {AudioParam}
- * @private
- */
- this._param = this.input = options.param;
- /**
- * The units of the parameter
- * @type {Tone.Type}
- */
- this.units = options.units;
- /**
- * If the value should be converted or not
- * @type {Boolean}
- */
- this.convert = options.convert;
- /**
- * True if the signal value is being overridden by
- * a connected signal.
- * @readOnly
- * @type {boolean}
- * @private
- */
- this.overridden = false;
- /**
- * If there is an LFO, this is where it is held.
- * @type {Tone.LFO}
- * @private
- */
- this._lfo = null;
- if (Tone.isObject(options.lfo)) {
- this.value = options.lfo;
- } else if (!Tone.isUndef(options.value)) {
- this.value = options.value;
- }
- };
- Tone.extend(Tone.Param, Tone.AudioNode);
- /**
- * Defaults
- * @type {Object}
- * @const
- */
- Tone.Param.defaults = {
- 'units': Tone.Type.Default,
- 'convert': true,
- 'param': undefined
- };
- /**
- * The current value of the parameter.
- * @memberOf Tone.Param#
- * @type {Number}
- * @name value
- */
- Object.defineProperty(Tone.Param.prototype, 'value', {
- get: function () {
- return this._toUnits(this._param.value);
- },
- set: function (value) {
- if (Tone.isObject(value)) {
- //throw an error if the LFO needs to be included
- if (Tone.isUndef(Tone.LFO)) {
- throw new Error('Include \'Tone.LFO\' to use an LFO as a Param value.');
- }
- //remove the old one
- if (this._lfo) {
- this._lfo.dispose();
- }
- this._lfo = new Tone.LFO(value).start();
- this._lfo.connect(this.input);
- } else {
- var convertedVal = this._fromUnits(value);
- this._param.cancelScheduledValues(0);
- this._param.value = convertedVal;
- }
- }
- });
- /**
- * Convert the given value from the type specified by Tone.Param.units
- * into the destination value (such as Gain or Frequency).
- * @private
- * @param {*} val the value to convert
- * @return {number} the number which the value should be set to
- */
- Tone.Param.prototype._fromUnits = function (val) {
- if (this.convert || Tone.isUndef(this.convert)) {
- switch (this.units) {
- case Tone.Type.Time:
- return this.toSeconds(val);
- case Tone.Type.Frequency:
- return this.toFrequency(val);
- case Tone.Type.Decibels:
- return Tone.dbToGain(val);
- case Tone.Type.NormalRange:
- return Math.min(Math.max(val, 0), 1);
- case Tone.Type.AudioRange:
- return Math.min(Math.max(val, -1), 1);
- case Tone.Type.Positive:
- return Math.max(val, 0);
- default:
- return val;
- }
- } else {
- return val;
- }
- };
- /**
- * Convert the parameters value into the units specified by Tone.Param.units.
- * @private
- * @param {number} val the value to convert
- * @return {number}
- */
- Tone.Param.prototype._toUnits = function (val) {
- if (this.convert || Tone.isUndef(this.convert)) {
- switch (this.units) {
- case Tone.Type.Decibels:
- return Tone.gainToDb(val);
- default:
- return val;
- }
- } else {
- return val;
- }
- };
- /**
- * the minimum output value
- * @type {Number}
- * @private
- */
- Tone.Param.prototype._minOutput = 0.00001;
- /**
- * Schedules a parameter value change at the given time.
- * @param {*} value The value to set the signal.
- * @param {Time} time The time when the change should occur.
- * @returns {Tone.Param} this
- * @example
- * //set the frequency to "G4" in exactly 1 second from now.
- * freq.setValueAtTime("G4", "+1");
- */
- Tone.Param.prototype.setValueAtTime = function (value, time) {
- this._param.setValueAtTime(this._fromUnits(value), this.toSeconds(time));
- return this;
- };
- /**
- * Creates a schedule point with the current value at the current time.
- * This is useful for creating an automation anchor point in order to
- * schedule changes from the current value.
- *
- * @param {number=} now (Optionally) pass the now value in.
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.setRampPoint = function (now) {
- now = Tone.defaultArg(now, this.now());
- var currentVal = this._param.value;
- // exponentialRampToValueAt cannot ever ramp from or to 0
- // More info: https://bugzilla.mozilla.org/show_bug.cgi?id=1125600#c2
- if (currentVal === 0) {
- currentVal = this._minOutput;
- }
- this._param.setValueAtTime(currentVal, now);
- return this;
- };
- /**
- * Schedules a linear continuous change in parameter value from the
- * previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) {
- value = this._fromUnits(value);
- this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
- return this;
- };
- /**
- * Schedules an exponential continuous change in parameter value from
- * the previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.exponentialRampToValueAtTime = function (value, endTime) {
- value = this._fromUnits(value);
- value = Math.max(this._minOutput, value);
- this._param.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
- return this;
- };
- /**
- * Schedules an exponential continuous change in parameter value from
- * the current time and current value to the given value over the
- * duration of the rampTime.
- *
- * @param {number} value The value to ramp to.
- * @param {Time} rampTime the time that it takes the
- * value to ramp from it's current value
- * @param {Time} [startTime=now] When the ramp should start.
- * @returns {Tone.Param} this
- * @example
- * //exponentially ramp to the value 2 over 4 seconds.
- * signal.exponentialRampToValue(2, 4);
- */
- Tone.Param.prototype.exponentialRampToValue = function (value, rampTime, startTime) {
- startTime = this.toSeconds(startTime);
- this.setRampPoint(startTime);
- this.exponentialRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
- return this;
- };
- /**
- * Schedules an linear continuous change in parameter value from
- * the current time and current value to the given value over the
- * duration of the rampTime.
- *
- * @param {number} value The value to ramp to.
- * @param {Time} rampTime the time that it takes the
- * value to ramp from it's current value
- * @param {Time} [startTime=now] When the ramp should start.
- * @returns {Tone.Param} this
- * @example
- * //linearly ramp to the value 4 over 3 seconds.
- * signal.linearRampToValue(4, 3);
- */
- Tone.Param.prototype.linearRampToValue = function (value, rampTime, startTime) {
- startTime = this.toSeconds(startTime);
- this.setRampPoint(startTime);
- this.linearRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
- return this;
- };
- /**
- * Start exponentially approaching the target value at the given time with
- * a rate having the given time constant.
- * @param {number} value
- * @param {Time} startTime
- * @param {number} timeConstant
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
- value = this._fromUnits(value);
- // The value will never be able to approach without timeConstant > 0.
- // http://www.w3.org/TR/webaudio/#dfn-setTargetAtTime, where the equation
- // is described. 0 results in a division by 0.
- value = Math.max(this._minOutput, value);
- timeConstant = Math.max(this._minOutput, timeConstant);
- this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
- return this;
- };
- /**
- * Sets an array of arbitrary parameter values starting at the given time
- * for the given duration.
- *
- * @param {Array} values
- * @param {Time} startTime
- * @param {Time} duration
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration) {
- duration = this.toSeconds(duration);
- startTime = this.toSeconds(startTime);
- this.setValueAtTime(values[0], startTime);
- var segTime = duration / (values.length - 1);
- for (var i = 1; i < values.length; i++) {
- this._param.linearRampToValueAtTime(this._fromUnits(values[i]), startTime + i * segTime);
- }
- return this;
- };
- /**
- * Cancels all scheduled parameter changes with times greater than or
- * equal to startTime.
- *
- * @param {Time} startTime
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.cancelScheduledValues = function (startTime) {
- this._param.cancelScheduledValues(this.toSeconds(startTime));
- return this;
- };
- /**
- * Ramps to the given value over the duration of the rampTime.
- * Automatically selects the best ramp type (exponential or linear)
- * depending on the `units` of the signal
- *
- * @param {number} value
- * @param {Time} rampTime The time that it takes the
- * value to ramp from it's current value
- * @param {Time} [startTime=now] When the ramp should start.
- * @returns {Tone.Param} this
- * @example
- * //ramp to the value either linearly or exponentially
- * //depending on the "units" value of the signal
- * signal.rampTo(0, 10);
- * @example
- * //schedule it to ramp starting at a specific time
- * signal.rampTo(0, 10, 5)
- */
- Tone.Param.prototype.rampTo = function (value, rampTime, startTime) {
- rampTime = Tone.defaultArg(rampTime, 0);
- if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM || this.units === Tone.Type.Decibels) {
- this.exponentialRampToValue(value, rampTime, startTime);
- } else {
- this.linearRampToValue(value, rampTime, startTime);
- }
- return this;
- };
- /**
- * The LFO created by the signal instance. If none
- * was created, this is null.
- * @type {Tone.LFO}
- * @readOnly
- * @memberOf Tone.Param#
- * @name lfo
- */
- Object.defineProperty(Tone.Param.prototype, 'lfo', {
- get: function () {
- return this._lfo;
- }
- });
- /**
- * Clean up
- * @returns {Tone.Param} this
- */
- Tone.Param.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._param = null;
- if (this._lfo) {
- this._lfo.dispose();
- this._lfo = null;
- }
- return this;
- };
- return Tone.Param;
- });
- Module(function (Tone) {
-
- /**
- * createGain shim
- * @private
- */
- if (window.GainNode && !AudioContext.prototype.createGain) {
- AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
- }
- /**
- * @class A thin wrapper around the Native Web Audio GainNode.
- * The GainNode is a basic building block of the Web Audio
- * API and is useful for routing audio and adjusting gains.
- * @extends {Tone}
- * @param {Number=} gain The initial gain of the GainNode
- * @param {Tone.Type=} units The units of the gain parameter.
- */
- Tone.Gain = function () {
- var options = Tone.defaults(arguments, [
- 'gain',
- 'units'
- ], Tone.Gain);
- Tone.AudioNode.call(this);
- /**
- * The GainNode
- * @type {GainNode}
- * @private
- */
- this.input = this.output = this._gainNode = this.context.createGain();
- /**
- * The gain parameter of the gain node.
- * @type {Gain}
- * @signal
- */
- this.gain = new Tone.Param({
- 'param': this._gainNode.gain,
- 'units': options.units,
- 'value': options.gain,
- 'convert': options.convert
- });
- this._readOnly('gain');
- };
- Tone.extend(Tone.Gain, Tone.AudioNode);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Gain.defaults = {
- 'gain': 1,
- 'convert': true
- };
- /**
- * Clean up.
- * @return {Tone.Gain} this
- */
- Tone.Gain.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._gainNode.disconnect();
- this._gainNode = null;
- this._writable('gain');
- this.gain.dispose();
- this.gain = null;
- };
- return Tone.Gain;
- });
- Module(function (Tone) {
-
- /**
- * @class A signal is an audio-rate value. Tone.Signal is a core component of the library.
- * Unlike a number, Signals can be scheduled with sample-level accuracy. Tone.Signal
- * has all of the methods available to native Web Audio
- * [AudioParam](http://webaudio.github.io/web-audio-api/#the-audioparam-interface)
- * as well as additional conveniences. Read more about working with signals
- * [here](https://github.com/Tonejs/Tone.js/wiki/Signals).
- *
- * @constructor
- * @extends {Tone.Param}
- * @param {Number|AudioParam} [value] Initial value of the signal. If an AudioParam
- * is passed in, that parameter will be wrapped
- * and controlled by the Signal.
- * @param {string} [units=Number] unit The units the signal is in.
- * @example
- * var signal = new Tone.Signal(10);
- */
- Tone.Signal = function () {
- var options = Tone.defaults(arguments, [
- 'value',
- 'units'
- ], Tone.Signal);
- var gainNode = Tone.context.createGain();
- options.param = gainNode.gain;
- Tone.Param.call(this, options);
- /**
- * The node where the constant signal value is scaled.
- * @type {GainNode}
- * @private
- */
- this.output = gainNode;
- /**
- * The node where the value is set.
- * @type {Tone.Param}
- * @private
- */
- this.input = this._param = this.output.gain;
- //connect the const output to the node output
- this.context.getConstant(1).connect(this.output);
- };
- Tone.extend(Tone.Signal, Tone.Param);
- /**
- * The default values
- * @type {Object}
- * @static
- * @const
- */
- Tone.Signal.defaults = {
- 'value': 0,
- 'units': Tone.Type.Default,
- 'convert': true
- };
- /**
- * When signals connect to other signals or AudioParams,
- * they take over the output value of that signal or AudioParam.
- * For all other nodes, the behavior is the same as a default connect
.
- *
- * @override
- * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
- * @param {number} [outputNumber=0] The output number to connect from.
- * @param {number} [inputNumber=0] The input number to connect to.
- * @returns {Tone.SignalBase} this
- * @method
- */
- Tone.Signal.prototype.connect = Tone.SignalBase.prototype.connect;
- /**
- * dispose and disconnect
- * @returns {Tone.Signal} this
- */
- Tone.Signal.prototype.dispose = function () {
- Tone.Param.prototype.dispose.call(this);
- return this;
- };
- return Tone.Signal;
- });
- Module(function (Tone) {
-
- /**
- * @class A signal which adds the method getValueAtTime.
- * Code and inspiration from https://github.com/jsantell/web-audio-automation-timeline
- * @extends {Tone.Signal}
- * @param {Number=} value The initial value of the signal
- * @param {String=} units The conversion units of the signal.
- */
- Tone.TimelineSignal = function () {
- var options = Tone.defaults(arguments, [
- 'value',
- 'units'
- ], Tone.Signal);
- Tone.Signal.call(this, options);
- /**
- * The scheduled events
- * @type {Tone.Timeline}
- * @private
- */
- this._events = new Tone.Timeline(100);
- /**
- * The initial scheduled value
- * @type {Number}
- * @private
- */
- this._initial = this._fromUnits(this._param.value);
- this.value = options.value;
- //delete the input node so that nothing can overwrite the signal value
- delete this.input;
- };
- Tone.extend(Tone.TimelineSignal, Tone.Signal);
- /**
- * The event types of a schedulable signal.
- * @enum {String}
- * @private
- */
- Tone.TimelineSignal.Type = {
- Linear: 'linear',
- Exponential: 'exponential',
- Target: 'target',
- Set: 'set'
- };
- /**
- * The current value of the signal.
- * @memberOf Tone.TimelineSignal#
- * @type {Number}
- * @name value
- */
- Object.defineProperty(Tone.TimelineSignal.prototype, 'value', {
- get: function () {
- var now = this.now();
- var val = this.getValueAtTime(now);
- return this._toUnits(val);
- },
- set: function (value) {
- if (this._events) {
- var convertedVal = this._fromUnits(value);
- this._initial = convertedVal;
- this.cancelScheduledValues();
- this._param.value = convertedVal;
- }
- }
- });
- ///////////////////////////////////////////////////////////////////////////
- // SCHEDULING
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Schedules a parameter value change at the given time.
- * @param {*} value The value to set the signal.
- * @param {Time} time The time when the change should occur.
- * @returns {Tone.TimelineSignal} this
- * @example
- * //set the frequency to "G4" in exactly 1 second from now.
- * freq.setValueAtTime("G4", "+1");
- */
- Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
- value = this._fromUnits(value);
- startTime = this.toSeconds(startTime);
- this._events.add({
- 'type': Tone.TimelineSignal.Type.Set,
- 'value': value,
- 'time': startTime
- });
- //invoke the original event
- this._param.setValueAtTime(value, startTime);
- return this;
- };
- /**
- * Schedules a linear continuous change in parameter value from the
- * previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
- value = this._fromUnits(value);
- endTime = this.toSeconds(endTime);
- this._events.add({
- 'type': Tone.TimelineSignal.Type.Linear,
- 'value': value,
- 'time': endTime
- });
- this._param.linearRampToValueAtTime(value, endTime);
- return this;
- };
- /**
- * Schedules an exponential continuous change in parameter value from
- * the previous scheduled parameter value to the given value.
- *
- * @param {number} value
- * @param {Time} endTime
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
- //get the previous event and make sure it's not starting from 0
- endTime = this.toSeconds(endTime);
- var beforeEvent = this._searchBefore(endTime);
- if (beforeEvent && beforeEvent.value === 0) {
- //reschedule that event
- this.setValueAtTime(this._minOutput, beforeEvent.time);
- }
- value = this._fromUnits(value);
- var setValue = Math.max(value, this._minOutput);
- this._events.add({
- 'type': Tone.TimelineSignal.Type.Exponential,
- 'value': setValue,
- 'time': endTime
- });
- //if the ramped to value is 0, make it go to the min output, and then set to 0.
- if (value < this._minOutput) {
- this._param.exponentialRampToValueAtTime(this._minOutput, endTime - this.sampleTime);
- this.setValueAtTime(0, endTime);
- } else {
- this._param.exponentialRampToValueAtTime(value, endTime);
- }
- return this;
- };
- /**
- * Start exponentially approaching the target value at the given time with
- * a rate having the given time constant.
- * @param {number} value
- * @param {Time} startTime
- * @param {number} timeConstant
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
- value = this._fromUnits(value);
- value = Math.max(this._minOutput, value);
- timeConstant = Math.max(this._minOutput, timeConstant);
- startTime = this.toSeconds(startTime);
- this._events.add({
- 'type': Tone.TimelineSignal.Type.Target,
- 'value': value,
- 'time': startTime,
- 'constant': timeConstant
- });
- this._param.setTargetAtTime(value, startTime, timeConstant);
- return this;
- };
- /**
- * Set an array of arbitrary values starting at the given time for the given duration.
- * @param {Float32Array} values
- * @param {Time} startTime
- * @param {Time} duration
- * @param {NormalRange} [scaling=1] If the values in the curve should be scaled by some value
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.setValueCurveAtTime = function (values, startTime, duration, scaling) {
- scaling = Tone.defaultArg(scaling, 1);
- duration = this.toSeconds(duration);
- startTime = this.toSeconds(startTime);
- var segTime = duration / (values.length - 1);
- this.setValueAtTime(values[0] * scaling, startTime);
- for (var i = 1; i < values.length; i++) {
- this.linearRampToValueAtTime(values[i] * scaling, startTime + i * segTime);
- }
- return this;
- };
- /**
- * Cancels all scheduled parameter changes with times greater than or
- * equal to startTime.
- *
- * @param {Time} startTime
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
- after = this.toSeconds(after);
- this._events.cancel(after);
- this._param.cancelScheduledValues(after);
- return this;
- };
- /**
- * Sets the computed value at the given time. This provides
- * a point from which a linear or exponential curve
- * can be scheduled after. Will cancel events after
- * the given time and shorten the currently scheduled
- * linear or exponential ramp so that it ends at `time` .
- * This is to avoid discontinuities and clicks in envelopes.
- * @param {Time} time When to set the ramp point
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.setRampPoint = function (time) {
- time = this.toSeconds(time);
- //get the value at the given time
- var val = this._toUnits(this.getValueAtTime(time));
- //if there is an event at the given time
- //and that even is not a "set"
- var before = this._searchBefore(time);
- if (before && before.time === time) {
- //remove everything after
- this.cancelScheduledValues(time + this.sampleTime);
- } else {
- //reschedule the next event to end at the given time
- var after = this._searchAfter(time);
- if (after) {
- //cancel the next event(s)
- this.cancelScheduledValues(time);
- if (after.type === Tone.TimelineSignal.Type.Linear) {
- this.linearRampToValueAtTime(val, time);
- } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
- this.exponentialRampToValueAtTime(val, time);
- }
- }
- this.setValueAtTime(val, time);
- }
- return this;
- };
- /**
- * Do a linear ramp to the given value between the start and finish times.
- * @param {Number} value The value to ramp to.
- * @param {Time} start The beginning anchor point to do the linear ramp
- * @param {Time} finish The ending anchor point by which the value of
- * the signal will equal the given value.
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
- this.setRampPoint(start);
- this.linearRampToValueAtTime(value, finish);
- return this;
- };
- /**
- * Do a exponential ramp to the given value between the start and finish times.
- * @param {Number} value The value to ramp to.
- * @param {Time} start The beginning anchor point to do the exponential ramp
- * @param {Time} finish The ending anchor point by which the value of
- * the signal will equal the given value.
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
- this.setRampPoint(start);
- this.exponentialRampToValueAtTime(value, finish);
- return this;
- };
- ///////////////////////////////////////////////////////////////////////////
- // GETTING SCHEDULED VALUES
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Returns the value before or equal to the given time
- * @param {Number} time The time to query
- * @return {Object} The event at or before the given time.
- * @private
- */
- Tone.TimelineSignal.prototype._searchBefore = function (time) {
- return this._events.get(time);
- };
- /**
- * The event after the given time
- * @param {Number} time The time to query.
- * @return {Object} The next event after the given time
- * @private
- */
- Tone.TimelineSignal.prototype._searchAfter = function (time) {
- return this._events.getAfter(time);
- };
- /**
- * Get the scheduled value at the given time. This will
- * return the unconverted (raw) value.
- * @param {Number} time The time in seconds.
- * @return {Number} The scheduled value at the given time.
- */
- Tone.TimelineSignal.prototype.getValueAtTime = function (time) {
- time = this.toSeconds(time);
- var after = this._searchAfter(time);
- var before = this._searchBefore(time);
- var value = this._initial;
- //if it was set by
- if (before === null) {
- value = this._initial;
- } else if (before.type === Tone.TimelineSignal.Type.Target) {
- var previous = this._events.getBefore(before.time);
- var previouVal;
- if (previous === null) {
- previouVal = this._initial;
- } else {
- previouVal = previous.value;
- }
- value = this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
- } else if (after === null) {
- value = before.value;
- } else if (after.type === Tone.TimelineSignal.Type.Linear) {
- value = this._linearInterpolate(before.time, before.value, after.time, after.value, time);
- } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
- value = this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
- } else {
- value = before.value;
- }
- return value;
- };
- /**
- * When signals connect to other signals or AudioParams,
- * they take over the output value of that signal or AudioParam.
- * For all other nodes, the behavior is the same as a default connect
.
- *
- * @override
- * @param {AudioParam|AudioNode|Tone.Signal|Tone} node
- * @param {number} [outputNumber=0] The output number to connect from.
- * @param {number} [inputNumber=0] The input number to connect to.
- * @returns {Tone.TimelineSignal} this
- * @method
- */
- Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
- ///////////////////////////////////////////////////////////////////////////
- // AUTOMATION CURVE CALCULATIONS
- // MIT License, copyright (c) 2014 Jordan Santell
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Calculates the the value along the curve produced by setTargetAtTime
- * @private
- */
- Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
- return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
- };
- /**
- * Calculates the the value along the curve produced by linearRampToValueAtTime
- * @private
- */
- Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
- return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
- };
- /**
- * Calculates the the value along the curve produced by exponentialRampToValueAtTime
- * @private
- */
- Tone.TimelineSignal.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
- v0 = Math.max(this._minOutput, v0);
- return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
- };
- /**
- * Clean up.
- * @return {Tone.TimelineSignal} this
- */
- Tone.TimelineSignal.prototype.dispose = function () {
- Tone.Signal.prototype.dispose.call(this);
- this._events.dispose();
- this._events = null;
- };
- return Tone.TimelineSignal;
- });
- Module(function (Tone) {
-
- /**
- * @class Pow applies an exponent to the incoming signal. The incoming signal
- * must be AudioRange.
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @param {Positive} exp The exponent to apply to the incoming signal, must be at least 2.
- * @example
- * var pow = new Tone.Pow(2);
- * var sig = new Tone.Signal(0.5).connect(pow);
- * //output of pow is 0.25.
- */
- Tone.Pow = function (exp) {
- Tone.SignalBase.call(this);
- /**
- * the exponent
- * @private
- * @type {number}
- */
- this._exp = Tone.defaultArg(exp, 1);
- /**
- * @type {WaveShaperNode}
- * @private
- */
- this._expScaler = this.input = this.output = new Tone.WaveShaper(this._expFunc(this._exp), 8192);
- };
- Tone.extend(Tone.Pow, Tone.SignalBase);
- /**
- * The value of the exponent.
- * @memberOf Tone.Pow#
- * @type {number}
- * @name value
- */
- Object.defineProperty(Tone.Pow.prototype, 'value', {
- get: function () {
- return this._exp;
- },
- set: function (exp) {
- this._exp = exp;
- this._expScaler.setMap(this._expFunc(this._exp));
- }
- });
- /**
- * the function which maps the waveshaper
- * @param {number} exp
- * @return {function}
- * @private
- */
- Tone.Pow.prototype._expFunc = function (exp) {
- return function (val) {
- return Math.pow(Math.abs(val), exp);
- };
- };
- /**
- * Clean up.
- * @returns {Tone.Pow} this
- */
- Tone.Pow.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._expScaler.dispose();
- this._expScaler = null;
- return this;
- };
- return Tone.Pow;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Envelope is an [ADSR](https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope)
- * envelope generator. Tone.Envelope outputs a signal which
- * can be connected to an AudioParam or Tone.Signal.
- *
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Time} [attack] The amount of time it takes for the envelope to go from
- * 0 to it's maximum value.
- * @param {Time} [decay] The period of time after the attack that it takes for the envelope
- * to fall to the sustain value.
- * @param {NormalRange} [sustain] The percent of the maximum value that the envelope rests at until
- * the release is triggered.
- * @param {Time} [release] The amount of time after the release is triggered it takes to reach 0.
- * @example
- * //an amplitude envelope
- * var gainNode = Tone.context.createGain();
- * var env = new Tone.Envelope({
- * "attack" : 0.1,
- * "decay" : 0.2,
- * "sustain" : 1,
- * "release" : 0.8,
- * });
- * env.connect(gainNode.gain);
- */
- Tone.Envelope = function () {
- //get all of the defaults
- var options = Tone.defaults(arguments, [
- 'attack',
- 'decay',
- 'sustain',
- 'release'
- ], Tone.Envelope);
- Tone.AudioNode.call(this);
- /**
- * When triggerAttack is called, the attack time is the amount of
- * time it takes for the envelope to reach it's maximum value.
- * @type {Time}
- */
- this.attack = options.attack;
- /**
- * After the attack portion of the envelope, the value will fall
- * over the duration of the decay time to it's sustain value.
- * @type {Time}
- */
- this.decay = options.decay;
- /**
- * The sustain value is the value
- * which the envelope rests at after triggerAttack is
- * called, but before triggerRelease is invoked.
- * @type {NormalRange}
- */
- this.sustain = options.sustain;
- /**
- * After triggerRelease is called, the envelope's
- * value will fall to it's miminum value over the
- * duration of the release time.
- * @type {Time}
- */
- this.release = options.release;
- /**
- * the next time the envelope is at standby
- * @type {number}
- * @private
- */
- this._attackCurve = 'linear';
- /**
- * the next time the envelope is at standby
- * @type {number}
- * @private
- */
- this._releaseCurve = 'exponential';
- /**
- * the signal
- * @type {Tone.TimelineSignal}
- * @private
- */
- this._sig = this.output = new Tone.TimelineSignal();
- this._sig.setValueAtTime(0, 0);
- //set the attackCurve initially
- this.attackCurve = options.attackCurve;
- this.releaseCurve = options.releaseCurve;
- };
- Tone.extend(Tone.Envelope, Tone.AudioNode);
- /**
- * the default parameters
- * @static
- * @const
- */
- Tone.Envelope.defaults = {
- 'attack': 0.01,
- 'decay': 0.1,
- 'sustain': 0.5,
- 'release': 1,
- 'attackCurve': 'linear',
- 'releaseCurve': 'exponential'
- };
- /**
- * Read the current value of the envelope. Useful for
- * syncronizing visual output to the envelope.
- * @memberOf Tone.Envelope#
- * @type {Number}
- * @name value
- * @readOnly
- */
- Object.defineProperty(Tone.Envelope.prototype, 'value', {
- get: function () {
- return this.getValueAtTime(this.now());
- }
- });
- /**
- * The shape of the attack.
- * Can be any of these strings:
- *
input[0]
- * and input[1]
. If a value is passed into the constructor,
- * the it will be added to the input.
- *
- * @constructor
- * @extends {Tone.Signal}
- * @param {number=} value If no value is provided, Tone.Add will sum the first
- * and second inputs.
- * @example
- * var signal = new Tone.Signal(2);
- * var add = new Tone.Add(2);
- * signal.connect(add);
- * //the output of add equals 4
- * @example
- * //if constructed with no arguments
- * //it will add the first and second inputs
- * var add = new Tone.Add();
- * var sig0 = new Tone.Signal(3).connect(add, 0, 0);
- * var sig1 = new Tone.Signal(4).connect(add, 0, 1);
- * //the output of add equals 7.
- */
- Tone.Add = function (value) {
- Tone.Signal.call(this);
- this.createInsOuts(2, 0);
- /**
- * the summing node
- * @type {GainNode}
- * @private
- */
- this._sum = this.input[0] = this.input[1] = this.output = new Tone.Gain();
- /**
- * @private
- * @type {Tone.Signal}
- */
- this._param = this.input[1] = new Tone.Signal(value);
- this._param.connect(this._sum);
- };
- Tone.extend(Tone.Add, Tone.Signal);
- /**
- * Clean up.
- * @returns {Tone.Add} this
- */
- Tone.Add.prototype.dispose = function () {
- Tone.Signal.prototype.dispose.call(this);
- this._sum.dispose();
- this._sum = null;
- return this;
- };
- return Tone.Add;
- });
- Module(function (Tone) {
-
- /**
- * @class Multiply two incoming signals. Or, if a number is given in the constructor,
- * multiplies the incoming signal by that value.
- *
- * @constructor
- * @extends {Tone.Signal}
- * @param {number=} value Constant value to multiple. If no value is provided,
- * it will return the product of the first and second inputs
- * @example
- * var mult = new Tone.Multiply();
- * var sigA = new Tone.Signal(3);
- * var sigB = new Tone.Signal(4);
- * sigA.connect(mult, 0, 0);
- * sigB.connect(mult, 0, 1);
- * //output of mult is 12.
- * @example
- * var mult = new Tone.Multiply(10);
- * var sig = new Tone.Signal(2).connect(mult);
- * //the output of mult is 20.
- */
- Tone.Multiply = function (value) {
- Tone.Signal.call(this);
- this.createInsOuts(2, 0);
- /**
- * the input node is the same as the output node
- * it is also the GainNode which handles the scaling of incoming signal
- *
- * @type {GainNode}
- * @private
- */
- this._mult = this.input[0] = this.output = new Tone.Gain();
- /**
- * the scaling parameter
- * @type {AudioParam}
- * @private
- */
- this._param = this.input[1] = this.output.gain;
- this._param.value = Tone.defaultArg(value, 0);
- };
- Tone.extend(Tone.Multiply, Tone.Signal);
- /**
- * clean up
- * @returns {Tone.Multiply} this
- */
- Tone.Multiply.prototype.dispose = function () {
- Tone.Signal.prototype.dispose.call(this);
- this._mult.dispose();
- this._mult = null;
- this._param = null;
- return this;
- };
- return Tone.Multiply;
- });
- Module(function (Tone) {
-
- /**
- * @class Negate the incoming signal. i.e. an input signal of 10 will output -10
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @example
- * var neg = new Tone.Negate();
- * var sig = new Tone.Signal(-2).connect(neg);
- * //output of neg is positive 2.
- */
- Tone.Negate = function () {
- Tone.SignalBase.call(this);
- /**
- * negation is done by multiplying by -1
- * @type {Tone.Multiply}
- * @private
- */
- this._multiply = this.input = this.output = new Tone.Multiply(-1);
- };
- Tone.extend(Tone.Negate, Tone.SignalBase);
- /**
- * clean up
- * @returns {Tone.Negate} this
- */
- Tone.Negate.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._multiply.dispose();
- this._multiply = null;
- return this;
- };
- return Tone.Negate;
- });
- Module(function (Tone) {
-
- /**
- * @class Subtract the signal connected to input[1]
from the signal connected
- * to input[0]
. If an argument is provided in the constructor, the
- * signals .value
will be subtracted from the incoming signal.
- *
- * @extends {Tone.Signal}
- * @constructor
- * @param {number=} value The value to subtract from the incoming signal. If the value
- * is omitted, it will subtract the second signal from the first.
- * @example
- * var sub = new Tone.Subtract(1);
- * var sig = new Tone.Signal(4).connect(sub);
- * //the output of sub is 3.
- * @example
- * var sub = new Tone.Subtract();
- * var sigA = new Tone.Signal(10);
- * var sigB = new Tone.Signal(2.5);
- * sigA.connect(sub, 0, 0);
- * sigB.connect(sub, 0, 1);
- * //output of sub is 7.5
- */
- Tone.Subtract = function (value) {
- Tone.Signal.call(this);
- this.createInsOuts(2, 0);
- /**
- * the summing node
- * @type {GainNode}
- * @private
- */
- this._sum = this.input[0] = this.output = new Tone.Gain();
- /**
- * negate the input of the second input before connecting it
- * to the summing node.
- * @type {Tone.Negate}
- * @private
- */
- this._neg = new Tone.Negate();
- /**
- * the node where the value is set
- * @private
- * @type {Tone.Signal}
- */
- this._param = this.input[1] = new Tone.Signal(value);
- this._param.chain(this._neg, this._sum);
- };
- Tone.extend(Tone.Subtract, Tone.Signal);
- /**
- * Clean up.
- * @returns {Tone.SignalBase} this
- */
- Tone.Subtract.prototype.dispose = function () {
- Tone.Signal.prototype.dispose.call(this);
- this._neg.dispose();
- this._neg = null;
- this._sum.disconnect();
- this._sum = null;
- return this;
- };
- return Tone.Subtract;
- });
- Module(function (Tone) {
-
- /**
- * @class GreaterThanZero outputs 1 when the input is strictly greater than zero
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @example
- * var gt0 = new Tone.GreaterThanZero();
- * var sig = new Tone.Signal(0.01).connect(gt0);
- * //the output of gt0 is 1.
- * sig.value = 0;
- * //the output of gt0 is 0.
- */
- Tone.GreaterThanZero = function () {
- Tone.SignalBase.call(this);
- /**
- * @type {Tone.WaveShaper}
- * @private
- */
- this._thresh = this.output = new Tone.WaveShaper(function (val) {
- if (val <= 0) {
- return 0;
- } else {
- return 1;
- }
- }, 127);
- /**
- * scale the first thresholded signal by a large value.
- * this will help with values which are very close to 0
- * @type {Tone.Multiply}
- * @private
- */
- this._scale = this.input = new Tone.Multiply(10000);
- //connections
- this._scale.connect(this._thresh);
- };
- Tone.extend(Tone.GreaterThanZero, Tone.SignalBase);
- /**
- * dispose method
- * @returns {Tone.GreaterThanZero} this
- */
- Tone.GreaterThanZero.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._scale.dispose();
- this._scale = null;
- this._thresh.dispose();
- this._thresh = null;
- return this;
- };
- return Tone.GreaterThanZero;
- });
- Module(function (Tone) {
-
- /**
- * @class Output 1 if the signal is greater than the value, otherwise outputs 0.
- * can compare two signals or a signal and a number.
- *
- * @constructor
- * @extends {Tone.Signal}
- * @param {number} [value=0] the value to compare to the incoming signal
- * @example
- * var gt = new Tone.GreaterThan(2);
- * var sig = new Tone.Signal(4).connect(gt);
- * //output of gt is equal 1.
- */
- Tone.GreaterThan = function (value) {
- Tone.Signal.call(this);
- this.createInsOuts(2, 0);
- /**
- * subtract the amount from the incoming signal
- * @type {Tone.Subtract}
- * @private
- */
- this._param = this.input[0] = new Tone.Subtract(value);
- this.input[1] = this._param.input[1];
- /**
- * compare that amount to zero
- * @type {Tone.GreaterThanZero}
- * @private
- */
- this._gtz = this.output = new Tone.GreaterThanZero();
- //connect
- this._param.connect(this._gtz);
- };
- Tone.extend(Tone.GreaterThan, Tone.Signal);
- /**
- * dispose method
- * @returns {Tone.GreaterThan} this
- */
- Tone.GreaterThan.prototype.dispose = function () {
- Tone.Signal.prototype.dispose.call(this);
- this._gtz.dispose();
- this._gtz = null;
- return this;
- };
- return Tone.GreaterThan;
- });
- Module(function (Tone) {
-
- /**
- * @class Return the absolute value of an incoming signal.
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @example
- * var signal = new Tone.Signal(-1);
- * var abs = new Tone.Abs();
- * signal.connect(abs);
- * //the output of abs is 1.
- */
- Tone.Abs = function () {
- Tone.SignalBase.call(this);
- /**
- * @type {Tone.LessThan}
- * @private
- */
- this._abs = this.input = this.output = new Tone.WaveShaper(function (val) {
- if (val === 0) {
- return 0;
- } else {
- return Math.abs(val);
- }
- }, 127);
- };
- Tone.extend(Tone.Abs, Tone.SignalBase);
- /**
- * dispose method
- * @returns {Tone.Abs} this
- */
- Tone.Abs.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._abs.dispose();
- this._abs = null;
- return this;
- };
- return Tone.Abs;
- });
- Module(function (Tone) {
-
- /**
- * @class Signal-rate modulo operator. Only works in AudioRange [-1, 1] and for modulus
- * values in the NormalRange.
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @param {NormalRange} modulus The modulus to apply.
- * @example
- * var mod = new Tone.Modulo(0.2)
- * var sig = new Tone.Signal(0.5).connect(mod);
- * //mod outputs 0.1
- */
- Tone.Modulo = function (modulus) {
- Tone.SignalBase.call(this);
- this.createInsOuts(1, 0);
- /**
- * A waveshaper gets the integer multiple of
- * the input signal and the modulus.
- * @private
- * @type {Tone.WaveShaper}
- */
- this._shaper = new Tone.WaveShaper(Math.pow(2, 16));
- /**
- * the integer multiple is multiplied by the modulus
- * @type {Tone.Multiply}
- * @private
- */
- this._multiply = new Tone.Multiply();
- /**
- * and subtracted from the input signal
- * @type {Tone.Subtract}
- * @private
- */
- this._subtract = this.output = new Tone.Subtract();
- /**
- * the modulus signal
- * @type {Tone.Signal}
- * @private
- */
- this._modSignal = new Tone.Signal(modulus);
- //connections
- this.input.fan(this._shaper, this._subtract);
- this._modSignal.connect(this._multiply, 0, 0);
- this._shaper.connect(this._multiply, 0, 1);
- this._multiply.connect(this._subtract, 0, 1);
- this._setWaveShaper(modulus);
- };
- Tone.extend(Tone.Modulo, Tone.SignalBase);
- /**
- * @param {number} mod the modulus to apply
- * @private
- */
- Tone.Modulo.prototype._setWaveShaper = function (mod) {
- this._shaper.setMap(function (val) {
- var multiple = Math.floor((val + 0.0001) / mod);
- return multiple;
- });
- };
- /**
- * The modulus value.
- * @memberOf Tone.Modulo#
- * @type {NormalRange}
- * @name value
- */
- Object.defineProperty(Tone.Modulo.prototype, 'value', {
- get: function () {
- return this._modSignal.value;
- },
- set: function (mod) {
- this._modSignal.value = mod;
- this._setWaveShaper(mod);
- }
- });
- /**
- * clean up
- * @returns {Tone.Modulo} this
- */
- Tone.Modulo.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._shaper.dispose();
- this._shaper = null;
- this._multiply.dispose();
- this._multiply = null;
- this._subtract.dispose();
- this._subtract = null;
- this._modSignal.dispose();
- this._modSignal = null;
- return this;
- };
- return Tone.Modulo;
- });
- Module(function (Tone) {
-
- /**
- * @class AudioToGain converts an input in AudioRange [-1,1] to NormalRange [0,1].
- * See Tone.GainToAudio.
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @example
- * var a2g = new Tone.AudioToGain();
- */
- Tone.AudioToGain = function () {
- Tone.SignalBase.call(this);
- /**
- * @type {WaveShaperNode}
- * @private
- */
- this._norm = this.input = this.output = new Tone.WaveShaper(function (x) {
- return (x + 1) / 2;
- });
- };
- Tone.extend(Tone.AudioToGain, Tone.SignalBase);
- /**
- * clean up
- * @returns {Tone.AudioToGain} this
- */
- Tone.AudioToGain.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._norm.dispose();
- this._norm = null;
- return this;
- };
- return Tone.AudioToGain;
- });
- Module(function (Tone) {
-
- /**
- * @class Evaluate an expression at audio rate. input[0]
.
- * @type {Tone.Gain}
- */
- this.a = this.input[0] = new Tone.Gain();
- /**
- * Alias for input[1]
.
- * @type {Tone.Gain}
- */
- this.b = this.input[1] = new Tone.Gain();
- /**
- * The mix between the two inputs. A fade value of 0
- * will output 100% input[0]
and
- * a value of 1 will output 100% input[1]
.
- * @type {NormalRange}
- * @signal
- */
- this.fade = new Tone.Signal(Tone.defaultArg(initialFade, 0.5), Tone.Type.NormalRange);
- /**
- * equal power gain cross fade
- * @private
- * @type {Tone.EqualPowerGain}
- */
- this._equalPowerA = new Tone.EqualPowerGain();
- /**
- * equal power gain cross fade
- * @private
- * @type {Tone.EqualPowerGain}
- */
- this._equalPowerB = new Tone.EqualPowerGain();
- /**
- * invert the incoming signal
- * @private
- * @type {Tone}
- */
- this._invert = new Tone.Expr('1 - $0');
- //connections
- this.a.connect(this.output);
- this.b.connect(this.output);
- this.fade.chain(this._equalPowerB, this.b.gain);
- this.fade.chain(this._invert, this._equalPowerA, this.a.gain);
- this._readOnly('fade');
- };
- Tone.extend(Tone.CrossFade, Tone.AudioNode);
- /**
- * clean up
- * @returns {Tone.CrossFade} this
- */
- Tone.CrossFade.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable('fade');
- this._equalPowerA.dispose();
- this._equalPowerA = null;
- this._equalPowerB.dispose();
- this._equalPowerB = null;
- this.fade.dispose();
- this.fade = null;
- this._invert.dispose();
- this._invert = null;
- this.a.dispose();
- this.a = null;
- this.b.dispose();
- this.b = null;
- return this;
- };
- return Tone.CrossFade;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Filter is a filter which allows for all of the same native methods
- * as the [BiquadFilterNode](http://webaudio.github.io/web-audio-api/#the-biquadfilternode-interface).
- * Tone.Filter has the added ability to set the filter rolloff at -12
- * (default), -24 and -48.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Frequency|Object} [frequency] The cutoff frequency of the filter.
- * @param {string=} type The type of filter.
- * @param {number=} rolloff The drop in decibels per octave after the cutoff frequency.
- * 3 choices: -12, -24, and -48
- * @example
- * var filter = new Tone.Filter(200, "highpass");
- */
- Tone.Filter = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'type',
- 'rolloff'
- ], Tone.Filter);
- Tone.AudioNode.call(this);
- this.createInsOuts(1, 1);
- /**
- * the filter(s)
- * @type {Array}
- * @private
- */
- this._filters = [];
- /**
- * The cutoff frequency of the filter.
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);
- /**
- * The detune parameter
- * @type {Cents}
- * @signal
- */
- this.detune = new Tone.Signal(0, Tone.Type.Cents);
- /**
- * The gain of the filter, only used in certain filter types
- * @type {Number}
- * @signal
- */
- this.gain = new Tone.Signal({
- 'value': options.gain,
- 'convert': false
- });
- /**
- * The Q or Quality of the filter
- * @type {Positive}
- * @signal
- */
- this.Q = new Tone.Signal(options.Q);
- /**
- * the type of the filter
- * @type {string}
- * @private
- */
- this._type = options.type;
- /**
- * the rolloff value of the filter
- * @type {number}
- * @private
- */
- this._rolloff = options.rolloff;
- //set the rolloff;
- this.rolloff = options.rolloff;
- this._readOnly([
- 'detune',
- 'frequency',
- 'gain',
- 'Q'
- ]);
- };
- Tone.extend(Tone.Filter, Tone.AudioNode);
- /**
- * the default parameters
- *
- * @static
- * @type {Object}
- */
- Tone.Filter.defaults = {
- 'type': 'lowpass',
- 'frequency': 350,
- 'rolloff': -12,
- 'Q': 1,
- 'gain': 0
- };
- /**
- * The type of the filter. Types: "lowpass", "highpass",
- * "bandpass", "lowshelf", "highshelf", "notch", "allpass", or "peaking".
- * @memberOf Tone.Filter#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.Filter.prototype, 'type', {
- get: function () {
- return this._type;
- },
- set: function (type) {
- var types = [
- 'lowpass',
- 'highpass',
- 'bandpass',
- 'lowshelf',
- 'highshelf',
- 'notch',
- 'allpass',
- 'peaking'
- ];
- if (types.indexOf(type) === -1) {
- throw new TypeError('Tone.Filter: invalid type ' + type);
- }
- this._type = type;
- for (var i = 0; i < this._filters.length; i++) {
- this._filters[i].type = type;
- }
- }
- });
- /**
- * The rolloff of the filter which is the drop in db
- * per octave. Implemented internally by cascading filters.
- * Only accepts the values -12, -24, -48 and -96.
- * @memberOf Tone.Filter#
- * @type {number}
- * @name rolloff
- */
- Object.defineProperty(Tone.Filter.prototype, 'rolloff', {
- get: function () {
- return this._rolloff;
- },
- set: function (rolloff) {
- rolloff = parseInt(rolloff, 10);
- var possibilities = [
- -12,
- -24,
- -48,
- -96
- ];
- var cascadingCount = possibilities.indexOf(rolloff);
- //check the rolloff is valid
- if (cascadingCount === -1) {
- throw new RangeError('Tone.Filter: rolloff can only be -12, -24, -48 or -96');
- }
- cascadingCount += 1;
- this._rolloff = rolloff;
- //first disconnect the filters and throw them away
- this.input.disconnect();
- for (var i = 0; i < this._filters.length; i++) {
- this._filters[i].disconnect();
- this._filters[i] = null;
- }
- this._filters = new Array(cascadingCount);
- for (var count = 0; count < cascadingCount; count++) {
- var filter = this.context.createBiquadFilter();
- filter.type = this._type;
- this.frequency.connect(filter.frequency);
- this.detune.connect(filter.detune);
- this.Q.connect(filter.Q);
- this.gain.connect(filter.gain);
- this._filters[count] = filter;
- }
- //connect them up
- var connectionChain = [this.input].concat(this._filters).concat([this.output]);
- Tone.connectSeries.apply(Tone, connectionChain);
- }
- });
- /**
- * Clean up.
- * @return {Tone.Filter} this
- */
- Tone.Filter.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- for (var i = 0; i < this._filters.length; i++) {
- this._filters[i].disconnect();
- this._filters[i] = null;
- }
- this._filters = null;
- this._writable([
- 'detune',
- 'frequency',
- 'gain',
- 'Q'
- ]);
- this.frequency.dispose();
- this.Q.dispose();
- this.frequency = null;
- this.Q = null;
- this.detune.dispose();
- this.detune = null;
- this.gain.dispose();
- this.gain = null;
- return this;
- };
- return Tone.Filter;
- });
- Module(function (Tone) {
-
- /**
- * @class Split the incoming signal into three bands (low, mid, high)
- * with two crossover frequency controls.
- *
- * @extends {Tone.AudioNode}
- * @constructor
- * @param {Frequency|Object} [lowFrequency] the low/mid crossover frequency
- * @param {Frequency} [highFrequency] the mid/high crossover frequency
- */
- Tone.MultibandSplit = function () {
- var options = Tone.defaults(arguments, [
- 'lowFrequency',
- 'highFrequency'
- ], Tone.MultibandSplit);
- Tone.AudioNode.call(this);
- /**
- * the input
- * @type {Tone.Gain}
- * @private
- */
- this.input = new Tone.Gain();
- /**
- * the outputs
- * @type {Array}
- * @private
- */
- this.output = new Array(3);
- /**
- * The low band. Alias for output[0]
- * @type {Tone.Filter}
- */
- this.low = this.output[0] = new Tone.Filter(0, 'lowpass');
- /**
- * the lower filter of the mid band
- * @type {Tone.Filter}
- * @private
- */
- this._lowMidFilter = new Tone.Filter(0, 'highpass');
- /**
- * The mid band output. Alias for output[1]
- * @type {Tone.Filter}
- */
- this.mid = this.output[1] = new Tone.Filter(0, 'lowpass');
- /**
- * The high band output. Alias for output[2]
- * @type {Tone.Filter}
- */
- this.high = this.output[2] = new Tone.Filter(0, 'highpass');
- /**
- * The low/mid crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.lowFrequency = new Tone.Signal(options.lowFrequency, Tone.Type.Frequency);
- /**
- * The mid/high crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.highFrequency = new Tone.Signal(options.highFrequency, Tone.Type.Frequency);
- /**
- * The quality of all the filters
- * @type {Number}
- * @signal
- */
- this.Q = new Tone.Signal(options.Q);
- this.input.fan(this.low, this.high);
- this.input.chain(this._lowMidFilter, this.mid);
- //the frequency control signal
- this.lowFrequency.connect(this.low.frequency);
- this.lowFrequency.connect(this._lowMidFilter.frequency);
- this.highFrequency.connect(this.mid.frequency);
- this.highFrequency.connect(this.high.frequency);
- //the Q value
- this.Q.connect(this.low.Q);
- this.Q.connect(this._lowMidFilter.Q);
- this.Q.connect(this.mid.Q);
- this.Q.connect(this.high.Q);
- this._readOnly([
- 'high',
- 'mid',
- 'low',
- 'highFrequency',
- 'lowFrequency'
- ]);
- };
- Tone.extend(Tone.MultibandSplit, Tone.AudioNode);
- /**
- * @private
- * @static
- * @type {Object}
- */
- Tone.MultibandSplit.defaults = {
- 'lowFrequency': 400,
- 'highFrequency': 2500,
- 'Q': 1
- };
- /**
- * Clean up.
- * @returns {Tone.MultibandSplit} this
- */
- Tone.MultibandSplit.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable([
- 'high',
- 'mid',
- 'low',
- 'highFrequency',
- 'lowFrequency'
- ]);
- this.low.dispose();
- this.low = null;
- this._lowMidFilter.dispose();
- this._lowMidFilter = null;
- this.mid.dispose();
- this.mid = null;
- this.high.dispose();
- this.high = null;
- this.lowFrequency.dispose();
- this.lowFrequency = null;
- this.highFrequency.dispose();
- this.highFrequency = null;
- this.Q.dispose();
- this.Q = null;
- return this;
- };
- return Tone.MultibandSplit;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.EQ3 is a three band EQ with control over low, mid, and high gain as
- * well as the low and high crossover frequencies.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- *
- * @param {Decibels|Object} [lowLevel] The gain applied to the lows.
- * @param {Decibels} [midLevel] The gain applied to the mid.
- * @param {Decibels} [highLevel] The gain applied to the high.
- * @example
- * var eq = new Tone.EQ3(-10, 3, -20);
- */
- Tone.EQ3 = function () {
- var options = Tone.defaults(arguments, [
- 'low',
- 'mid',
- 'high'
- ], Tone.EQ3);
- Tone.AudioNode.call(this);
- /**
- * the output node
- * @type {GainNode}
- * @private
- */
- this.output = new Tone.Gain();
- /**
- * the multiband split
- * @type {Tone.MultibandSplit}
- * @private
- */
- this._multibandSplit = this.input = new Tone.MultibandSplit({
- 'lowFrequency': options.lowFrequency,
- 'highFrequency': options.highFrequency
- });
- /**
- * The gain for the lower signals
- * @type {Tone.Gain}
- * @private
- */
- this._lowGain = new Tone.Gain(options.low, Tone.Type.Decibels);
- /**
- * The gain for the mid signals
- * @type {Tone.Gain}
- * @private
- */
- this._midGain = new Tone.Gain(options.mid, Tone.Type.Decibels);
- /**
- * The gain in decibels of the high part
- * @type {Tone.Gain}
- * @private
- */
- this._highGain = new Tone.Gain(options.high, Tone.Type.Decibels);
- /**
- * The gain in decibels of the low part
- * @type {Decibels}
- * @signal
- */
- this.low = this._lowGain.gain;
- /**
- * The gain in decibels of the mid part
- * @type {Decibels}
- * @signal
- */
- this.mid = this._midGain.gain;
- /**
- * The gain in decibels of the high part
- * @type {Decibels}
- * @signal
- */
- this.high = this._highGain.gain;
- /**
- * The Q value for all of the filters.
- * @type {Positive}
- * @signal
- */
- this.Q = this._multibandSplit.Q;
- /**
- * The low/mid crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.lowFrequency = this._multibandSplit.lowFrequency;
- /**
- * The mid/high crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.highFrequency = this._multibandSplit.highFrequency;
- //the frequency bands
- this._multibandSplit.low.chain(this._lowGain, this.output);
- this._multibandSplit.mid.chain(this._midGain, this.output);
- this._multibandSplit.high.chain(this._highGain, this.output);
- this._readOnly([
- 'low',
- 'mid',
- 'high',
- 'lowFrequency',
- 'highFrequency'
- ]);
- };
- Tone.extend(Tone.EQ3, Tone.AudioNode);
- /**
- * the default values
- */
- Tone.EQ3.defaults = {
- 'low': 0,
- 'mid': 0,
- 'high': 0,
- 'lowFrequency': 400,
- 'highFrequency': 2500
- };
- /**
- * clean up
- * @returns {Tone.EQ3} this
- */
- Tone.EQ3.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable([
- 'low',
- 'mid',
- 'high',
- 'lowFrequency',
- 'highFrequency'
- ]);
- this._multibandSplit.dispose();
- this._multibandSplit = null;
- this.lowFrequency = null;
- this.highFrequency = null;
- this._lowGain.dispose();
- this._lowGain = null;
- this._midGain.dispose();
- this._midGain = null;
- this._highGain.dispose();
- this._highGain = null;
- this.low = null;
- this.mid = null;
- this.high = null;
- this.Q = null;
- return this;
- };
- return Tone.EQ3;
- });
- Module(function (Tone) {
-
- /**
- * @class Performs a linear scaling on an input signal.
- * Scales a NormalRange input to between
- * outputMin and outputMax.
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @param {number} [outputMin=0] The output value when the input is 0.
- * @param {number} [outputMax=1] The output value when the input is 1.
- * @example
- * var scale = new Tone.Scale(50, 100);
- * var signal = new Tone.Signal(0.5).connect(scale);
- * //the output of scale equals 75
- */
- Tone.Scale = function (outputMin, outputMax) {
- Tone.SignalBase.call(this);
- /**
- * @private
- * @type {number}
- */
- this._outputMin = Tone.defaultArg(outputMin, 0);
- /**
- * @private
- * @type {number}
- */
- this._outputMax = Tone.defaultArg(outputMax, 1);
- /**
- * @private
- * @type {Tone.Multiply}
- * @private
- */
- this._scale = this.input = new Tone.Multiply(1);
- /**
- * @private
- * @type {Tone.Add}
- * @private
- */
- this._add = this.output = new Tone.Add(0);
- this._scale.connect(this._add);
- this._setRange();
- };
- Tone.extend(Tone.Scale, Tone.SignalBase);
- /**
- * The minimum output value. This number is output when
- * the value input value is 0.
- * @memberOf Tone.Scale#
- * @type {number}
- * @name min
- */
- Object.defineProperty(Tone.Scale.prototype, 'min', {
- get: function () {
- return this._outputMin;
- },
- set: function (min) {
- this._outputMin = min;
- this._setRange();
- }
- });
- /**
- * The maximum output value. This number is output when
- * the value input value is 1.
- * @memberOf Tone.Scale#
- * @type {number}
- * @name max
- */
- Object.defineProperty(Tone.Scale.prototype, 'max', {
- get: function () {
- return this._outputMax;
- },
- set: function (max) {
- this._outputMax = max;
- this._setRange();
- }
- });
- /**
- * set the values
- * @private
- */
- Tone.Scale.prototype._setRange = function () {
- this._add.value = this._outputMin;
- this._scale.value = this._outputMax - this._outputMin;
- };
- /**
- * Clean up.
- * @returns {Tone.Scale} this
- */
- Tone.Scale.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._add.dispose();
- this._add = null;
- this._scale.dispose();
- this._scale = null;
- return this;
- };
- return Tone.Scale;
- });
- Module(function (Tone) {
- /**
- * @class Performs an exponential scaling on an input signal.
- * Scales a NormalRange value [0,1] exponentially
- * to the output range of outputMin to outputMax.
- *
- * @constructor
- * @extends {Tone.SignalBase}
- * @param {number} [outputMin=0] The output value when the input is 0.
- * @param {number} [outputMax=1] The output value when the input is 1.
- * @param {number} [exponent=2] The exponent which scales the incoming signal.
- * @example
- * var scaleExp = new Tone.ScaleExp(0, 100, 2);
- * var signal = new Tone.Signal(0.5).connect(scaleExp);
- */
- Tone.ScaleExp = function (outputMin, outputMax, exponent) {
- Tone.SignalBase.call(this);
- /**
- * scale the input to the output range
- * @type {Tone.Scale}
- * @private
- */
- this._scale = this.output = new Tone.Scale(outputMin, outputMax);
- /**
- * @private
- * @type {Tone.Pow}
- * @private
- */
- this._exp = this.input = new Tone.Pow(Tone.defaultArg(exponent, 2));
- this._exp.connect(this._scale);
- };
- Tone.extend(Tone.ScaleExp, Tone.SignalBase);
- /**
- * Instead of interpolating linearly between the min
and
- * max
values, setting the exponent will interpolate between
- * the two values with an exponential curve.
- * @memberOf Tone.ScaleExp#
- * @type {number}
- * @name exponent
- */
- Object.defineProperty(Tone.ScaleExp.prototype, 'exponent', {
- get: function () {
- return this._exp.value;
- },
- set: function (exp) {
- this._exp.value = exp;
- }
- });
- /**
- * The minimum output value. This number is output when
- * the value input value is 0.
- * @memberOf Tone.ScaleExp#
- * @type {number}
- * @name min
- */
- Object.defineProperty(Tone.ScaleExp.prototype, 'min', {
- get: function () {
- return this._scale.min;
- },
- set: function (min) {
- this._scale.min = min;
- }
- });
- /**
- * The maximum output value. This number is output when
- * the value input value is 1.
- * @memberOf Tone.ScaleExp#
- * @type {number}
- * @name max
- */
- Object.defineProperty(Tone.ScaleExp.prototype, 'max', {
- get: function () {
- return this._scale.max;
- },
- set: function (max) {
- this._scale.max = max;
- }
- });
- /**
- * Clean up.
- * @returns {Tone.ScaleExp} this
- */
- Tone.ScaleExp.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._scale.dispose();
- this._scale = null;
- this._exp.dispose();
- this._exp = null;
- return this;
- };
- return Tone.ScaleExp;
- });
- Module(function (Tone) {
-
- /**
- * createDelay shim
- * @private
- */
- if (window.DelayNode && !AudioContext.prototype.createDelay) {
- AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
- }
- /**
- * @class Wrapper around Web Audio's native [DelayNode](http://webaudio.github.io/web-audio-api/#the-delaynode-interface).
- * @extends {Tone}
- * @param {Time=} delayTime The delay applied to the incoming signal.
- * @param {Time=} maxDelay The maximum delay time.
- */
- Tone.Delay = function () {
- var options = Tone.defaults(arguments, [
- 'delayTime',
- 'maxDelay'
- ], Tone.Delay);
- Tone.AudioNode.call(this);
- /**
- * The native delay node
- * @type {DelayNode}
- * @private
- */
- this._delayNode = this.input = this.output = this.context.createDelay(this.toSeconds(options.maxDelay));
- /**
- * The amount of time the incoming signal is
- * delayed.
- * @type {Time}
- * @signal
- */
- this.delayTime = new Tone.Param({
- 'param': this._delayNode.delayTime,
- 'units': Tone.Type.Time,
- 'value': options.delayTime
- });
- this._readOnly('delayTime');
- };
- Tone.extend(Tone.Delay, Tone.AudioNode);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Delay.defaults = {
- 'maxDelay': 1,
- 'delayTime': 0
- };
- /**
- * Clean up.
- * @return {Tone.Delay} this
- */
- Tone.Delay.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._delayNode.disconnect();
- this._delayNode = null;
- this._writable('delayTime');
- this.delayTime = null;
- return this;
- };
- return Tone.Delay;
- });
- Module(function (Tone) {
-
- /**
- * @class Comb filters are basic building blocks for physical modeling. Read more
- * about comb filters on [CCRMA's website](https://ccrma.stanford.edu/~jos/pasp/Feedback_Comb_Filters.html).
- *
- * @extends {Tone.AudioNode}
- * @constructor
- * @param {Time|Object} [delayTime] The delay time of the filter.
- * @param {NormalRange=} resonance The amount of feedback the filter has.
- */
- Tone.FeedbackCombFilter = function () {
- var options = Tone.defaults(arguments, [
- 'delayTime',
- 'resonance'
- ], Tone.FeedbackCombFilter);
- Tone.AudioNode.call(this);
- /**
- * the delay node
- * @type {DelayNode}
- * @private
- */
- this._delay = this.input = this.output = new Tone.Delay(options.delayTime);
- /**
- * The amount of delay of the comb filter.
- * @type {Time}
- * @signal
- */
- this.delayTime = this._delay.delayTime;
- /**
- * the feedback node
- * @type {GainNode}
- * @private
- */
- this._feedback = new Tone.Gain(options.resonance, Tone.Type.NormalRange);
- /**
- * The amount of feedback of the delayed signal.
- * @type {NormalRange}
- * @signal
- */
- this.resonance = this._feedback.gain;
- this._delay.chain(this._feedback, this._delay);
- this._readOnly([
- 'resonance',
- 'delayTime'
- ]);
- };
- Tone.extend(Tone.FeedbackCombFilter, Tone.AudioNode);
- /**
- * the default parameters
- * @static
- * @const
- * @type {Object}
- */
- Tone.FeedbackCombFilter.defaults = {
- 'delayTime': 0.1,
- 'resonance': 0.5
- };
- /**
- * clean up
- * @returns {Tone.FeedbackCombFilter} this
- */
- Tone.FeedbackCombFilter.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable([
- 'resonance',
- 'delayTime'
- ]);
- this._delay.dispose();
- this._delay = null;
- this.delayTime = null;
- this._feedback.dispose();
- this._feedback = null;
- this.resonance = null;
- return this;
- };
- return Tone.FeedbackCombFilter;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Follower is a crude envelope follower which will follow
- * the amplitude of an incoming signal.
- * Take care with small (< 0.02) attack or decay values
- * as follower has some ripple which is exaggerated
- * at these values. Read more about envelope followers (also known
- * as envelope detectors) on [Wikipedia](https://en.wikipedia.org/wiki/Envelope_detector).
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Time|Object} [attack] The rate at which the follower rises.
- * @param {Time=} release The rate at which the folower falls.
- * @example
- * var follower = new Tone.Follower(0.2, 0.4);
- */
- Tone.Follower = function () {
- var options = Tone.defaults(arguments, [
- 'attack',
- 'release'
- ], Tone.Follower);
- Tone.AudioNode.call(this);
- this.createInsOuts(1, 1);
- /**
- * @type {Tone.Abs}
- * @private
- */
- this._abs = new Tone.Abs();
- /**
- * the lowpass filter which smooths the input
- * @type {BiquadFilterNode}
- * @private
- */
- this._filter = this.context.createBiquadFilter();
- this._filter.type = 'lowpass';
- this._filter.frequency.value = 0;
- this._filter.Q.value = -100;
- /**
- * @type {WaveShaperNode}
- * @private
- */
- this._frequencyValues = new Tone.WaveShaper();
- /**
- * @type {Tone.Subtract}
- * @private
- */
- this._sub = new Tone.Subtract();
- /**
- * @type {Tone.Delay}
- * @private
- */
- this._delay = new Tone.Delay(this.blockTime);
- /**
- * this keeps it far from 0, even for very small differences
- * @type {Tone.Multiply}
- * @private
- */
- this._mult = new Tone.Multiply(10000);
- /**
- * @private
- * @type {number}
- */
- this._attack = options.attack;
- /**
- * @private
- * @type {number}
- */
- this._release = options.release;
- //the smoothed signal to get the values
- this.input.chain(this._abs, this._filter, this.output);
- //the difference path
- this._abs.connect(this._sub, 0, 1);
- this._filter.chain(this._delay, this._sub);
- //threshold the difference and use the thresh to set the frequency
- this._sub.chain(this._mult, this._frequencyValues, this._filter.frequency);
- //set the attack and release values in the table
- this._setAttackRelease(this._attack, this._release);
- };
- Tone.extend(Tone.Follower, Tone.AudioNode);
- /**
- * @static
- * @type {Object}
- */
- Tone.Follower.defaults = {
- 'attack': 0.05,
- 'release': 0.5
- };
- /**
- * sets the attack and release times in the wave shaper
- * @param {Time} attack
- * @param {Time} release
- * @private
- */
- Tone.Follower.prototype._setAttackRelease = function (attack, release) {
- var minTime = this.blockTime;
- attack = Tone.Time(attack).toFrequency();
- release = Tone.Time(release).toFrequency();
- attack = Math.max(attack, minTime);
- release = Math.max(release, minTime);
- this._frequencyValues.setMap(function (val) {
- if (val <= 0) {
- return attack;
- } else {
- return release;
- }
- });
- };
- /**
- * The attack time.
- * @memberOf Tone.Follower#
- * @type {Time}
- * @name attack
- */
- Object.defineProperty(Tone.Follower.prototype, 'attack', {
- get: function () {
- return this._attack;
- },
- set: function (attack) {
- this._attack = attack;
- this._setAttackRelease(this._attack, this._release);
- }
- });
- /**
- * The release time.
- * @memberOf Tone.Follower#
- * @type {Time}
- * @name release
- */
- Object.defineProperty(Tone.Follower.prototype, 'release', {
- get: function () {
- return this._release;
- },
- set: function (release) {
- this._release = release;
- this._setAttackRelease(this._attack, this._release);
- }
- });
- /**
- * Borrows the connect method from Signal so that the output can be used
- * as a Tone.Signal control signal.
- * @function
- */
- Tone.Follower.prototype.connect = Tone.Signal.prototype.connect;
- /**
- * dispose
- * @returns {Tone.Follower} this
- */
- Tone.Follower.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._filter.disconnect();
- this._filter = null;
- this._frequencyValues.disconnect();
- this._frequencyValues = null;
- this._delay.dispose();
- this._delay = null;
- this._sub.disconnect();
- this._sub = null;
- this._abs.dispose();
- this._abs = null;
- this._mult.dispose();
- this._mult = null;
- this._curve = null;
- return this;
- };
- return Tone.Follower;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.ScaledEnvelop is an envelope which can be scaled
- * to any range. It's useful for applying an envelope
- * to a frequency or any other non-NormalRange signal
- * parameter.
- *
- * @extends {Tone.Envelope}
- * @constructor
- * @param {Time|Object} [attack] the attack time in seconds
- * @param {Time} [decay] the decay time in seconds
- * @param {number} [sustain] a percentage (0-1) of the full amplitude
- * @param {Time} [release] the release time in seconds
- * @example
- * var scaledEnv = new Tone.ScaledEnvelope({
- * "attack" : 0.2,
- * "min" : 200,
- * "max" : 2000
- * });
- * scaledEnv.connect(oscillator.frequency);
- */
- Tone.ScaledEnvelope = function () {
- //get all of the defaults
- var options = Tone.defaults(arguments, [
- 'attack',
- 'decay',
- 'sustain',
- 'release'
- ], Tone.Envelope);
- Tone.Envelope.call(this, options);
- options = Tone.defaultArg(options, Tone.ScaledEnvelope.defaults);
- /**
- * scale the incoming signal by an exponent
- * @type {Tone.Pow}
- * @private
- */
- this._exp = this.output = new Tone.Pow(options.exponent);
- /**
- * scale the signal to the desired range
- * @type {Tone.Multiply}
- * @private
- */
- this._scale = this.output = new Tone.Scale(options.min, options.max);
- this._sig.chain(this._exp, this._scale);
- };
- Tone.extend(Tone.ScaledEnvelope, Tone.Envelope);
- /**
- * the default parameters
- * @static
- */
- Tone.ScaledEnvelope.defaults = {
- 'min': 0,
- 'max': 1,
- 'exponent': 1
- };
- /**
- * The envelope's min output value. This is the value which it
- * starts at.
- * @memberOf Tone.ScaledEnvelope#
- * @type {number}
- * @name min
- */
- Object.defineProperty(Tone.ScaledEnvelope.prototype, 'min', {
- get: function () {
- return this._scale.min;
- },
- set: function (min) {
- this._scale.min = min;
- }
- });
- /**
- * The envelope's max output value. In other words, the value
- * at the peak of the attack portion of the envelope.
- * @memberOf Tone.ScaledEnvelope#
- * @type {number}
- * @name max
- */
- Object.defineProperty(Tone.ScaledEnvelope.prototype, 'max', {
- get: function () {
- return this._scale.max;
- },
- set: function (max) {
- this._scale.max = max;
- }
- });
- /**
- * The envelope's exponent value.
- * @memberOf Tone.ScaledEnvelope#
- * @type {number}
- * @name exponent
- */
- Object.defineProperty(Tone.ScaledEnvelope.prototype, 'exponent', {
- get: function () {
- return this._exp.value;
- },
- set: function (exp) {
- this._exp.value = exp;
- }
- });
- /**
- * clean up
- * @returns {Tone.ScaledEnvelope} this
- */
- Tone.ScaledEnvelope.prototype.dispose = function () {
- Tone.Envelope.prototype.dispose.call(this);
- this._scale.dispose();
- this._scale = null;
- this._exp.dispose();
- this._exp = null;
- return this;
- };
- return Tone.ScaledEnvelope;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.FrequencyEnvelope is a Tone.ScaledEnvelope, but instead of `min` and `max`
- * it's got a `baseFrequency` and `octaves` parameter.
- *
- * @extends {Tone.Envelope}
- * @constructor
- * @param {Time|Object} [attack] the attack time in seconds
- * @param {Time} [decay] the decay time in seconds
- * @param {number} [sustain] a percentage (0-1) of the full amplitude
- * @param {Time} [release] the release time in seconds
- * @example
- * var env = new Tone.FrequencyEnvelope({
- * "attack" : 0.2,
- * "baseFrequency" : "C2",
- * "octaves" : 4
- * });
- * scaledEnv.connect(oscillator.frequency);
- */
- Tone.FrequencyEnvelope = function () {
- var options = Tone.defaults(arguments, [
- 'attack',
- 'decay',
- 'sustain',
- 'release'
- ], Tone.Envelope);
- Tone.ScaledEnvelope.call(this, options);
- //merge it with the frequency envelope defaults
- options = Tone.defaultArg(options, Tone.FrequencyEnvelope.defaults);
- /**
- * Stores the octave value
- * @type {Positive}
- * @private
- */
- this._octaves = options.octaves;
- //setup
- this.baseFrequency = options.baseFrequency;
- this.octaves = options.octaves;
- };
- Tone.extend(Tone.FrequencyEnvelope, Tone.Envelope);
- /**
- * the default parameters
- * @static
- */
- Tone.FrequencyEnvelope.defaults = {
- 'baseFrequency': 200,
- 'octaves': 4,
- 'exponent': 2
- };
- /**
- * The envelope's mininum output value. This is the value which it
- * starts at.
- * @memberOf Tone.FrequencyEnvelope#
- * @type {Frequency}
- * @name baseFrequency
- */
- Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'baseFrequency', {
- get: function () {
- return this._scale.min;
- },
- set: function (min) {
- this._scale.min = this.toFrequency(min);
- //also update the octaves
- this.octaves = this._octaves;
- }
- });
- /**
- * The number of octaves above the baseFrequency that the
- * envelope will scale to.
- * @memberOf Tone.FrequencyEnvelope#
- * @type {Positive}
- * @name octaves
- */
- Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'octaves', {
- get: function () {
- return this._octaves;
- },
- set: function (octaves) {
- this._octaves = octaves;
- this._scale.max = this.baseFrequency * Math.pow(2, octaves);
- }
- });
- /**
- * The envelope's exponent value.
- * @memberOf Tone.FrequencyEnvelope#
- * @type {number}
- * @name exponent
- */
- Object.defineProperty(Tone.FrequencyEnvelope.prototype, 'exponent', {
- get: function () {
- return this._exp.value;
- },
- set: function (exp) {
- this._exp.value = exp;
- }
- });
- /**
- * clean up
- * @returns {Tone.FrequencyEnvelope} this
- */
- Tone.FrequencyEnvelope.prototype.dispose = function () {
- Tone.ScaledEnvelope.prototype.dispose.call(this);
- return this;
- };
- return Tone.FrequencyEnvelope;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Gate only passes a signal through when the incoming
- * signal exceeds a specified threshold. To do this, Gate uses
- * a Tone.Follower to follow the amplitude of the incoming signal.
- * A common implementation of this class is a [Noise Gate](https://en.wikipedia.org/wiki/Noise_gate).
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Decibels|Object} [threshold] The threshold above which the gate will open.
- * @param {Time=} attack The follower's attack time
- * @param {Time=} release The follower's release time
- * @example
- * var gate = new Tone.Gate(-30, 0.2, 0.3).toMaster();
- * var mic = new Tone.UserMedia().connect(gate);
- * //the gate will only pass through the incoming
- * //signal when it's louder than -30db
- */
- Tone.Gate = function () {
- var options = Tone.defaults(arguments, [
- 'threshold',
- 'attack',
- 'release'
- ], Tone.Gate);
- Tone.AudioNode.call(this);
- this.createInsOuts(1, 1);
- /**
- * @type {Tone.Follower}
- * @private
- */
- this._follower = new Tone.Follower(options.attack, options.release);
- /**
- * @type {Tone.GreaterThan}
- * @private
- */
- this._gt = new Tone.GreaterThan(Tone.dbToGain(options.threshold));
- //the connections
- this.input.connect(this.output);
- //the control signal
- this.input.chain(this._gt, this._follower, this.output.gain);
- };
- Tone.extend(Tone.Gate, Tone.AudioNode);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.Gate.defaults = {
- 'attack': 0.1,
- 'release': 0.1,
- 'threshold': -40
- };
- /**
- * The threshold of the gate in decibels
- * @memberOf Tone.Gate#
- * @type {Decibels}
- * @name threshold
- */
- Object.defineProperty(Tone.Gate.prototype, 'threshold', {
- get: function () {
- return Tone.gainToDb(this._gt.value);
- },
- set: function (thresh) {
- this._gt.value = Tone.dbToGain(thresh);
- }
- });
- /**
- * The attack speed of the gate
- * @memberOf Tone.Gate#
- * @type {Time}
- * @name attack
- */
- Object.defineProperty(Tone.Gate.prototype, 'attack', {
- get: function () {
- return this._follower.attack;
- },
- set: function (attackTime) {
- this._follower.attack = attackTime;
- }
- });
- /**
- * The release speed of the gate
- * @memberOf Tone.Gate#
- * @type {Time}
- * @name release
- */
- Object.defineProperty(Tone.Gate.prototype, 'release', {
- get: function () {
- return this._follower.release;
- },
- set: function (releaseTime) {
- this._follower.release = releaseTime;
- }
- });
- /**
- * Clean up.
- * @returns {Tone.Gate} this
- */
- Tone.Gate.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._follower.dispose();
- this._gt.dispose();
- this._follower = null;
- this._gt = null;
- return this;
- };
- return Tone.Gate;
- });
- Module(function (Tone) {
- /**
- * @class Tone.TickSignal extends Tone.TimelineSignal, but adds the capability
- * to calculate the number of elapsed ticks. exponential and target curves
- * are approximated with multiple linear ramps.
- *
- * Thank you Bruno Dias, H. Sofia Pinto, and David M. Matos, for your [WAC paper](https://smartech.gatech.edu/bitstream/handle/1853/54588/WAC2016-49.pdf)
- * describing integrating timing functions for tempo calculations.
- *
- * @param {Number} value The initial value of the signal
- * @extends {Tone.TimelineSignal}
- */
- Tone.TickSignal = function (value) {
- value = Tone.defaultArg(value, 1);
- Tone.TimelineSignal.call(this, {
- 'units': Tone.Type.Ticks,
- 'value': value
- });
- //extend the memory
- this._events.memory = Infinity;
- };
- Tone.extend(Tone.TickSignal, Tone.TimelineSignal);
- /**
- * Wraps Tone.TimelineSignal methods so that they also
- * record the ticks.
- * @param {Function} method
- * @return {Function}
- * @private
- */
- function _wrapScheduleMethods(method) {
- return function (value, time) {
- time = this.toSeconds(time);
- method.apply(this, arguments);
- var event = this._events.get(time);
- var previousEvent = this._events.previousEvent(event);
- var ticksUntilTime = this._getTickUntilEvent(previousEvent, time - this.sampleTime);
- event.ticks = Math.max(ticksUntilTime, 0);
- return this;
- };
- }
- Tone.TickSignal.prototype.setValueAtTime = _wrapScheduleMethods(Tone.TimelineSignal.prototype.setValueAtTime);
- Tone.TickSignal.prototype.linearRampToValueAtTime = _wrapScheduleMethods(Tone.TimelineSignal.prototype.linearRampToValueAtTime);
- /**
- * Start exponentially approaching the target value at the given time with
- * a rate having the given time constant.
- * @param {number} value
- * @param {Time} startTime
- * @param {number} timeConstant
- * @returns {Tone.TickSignal} this
- */
- Tone.TickSignal.prototype.setTargetAtTime = function (value, time, constant) {
- //aproximate it with multiple linear ramps
- time = this.toSeconds(time);
- this.setRampPoint(time);
- value = this._fromUnits(value);
- //start from previously scheduled value
- var prevEvent = this._events.get(time);
- var segments = 5;
- for (var i = 0; i <= segments; i++) {
- var segTime = constant * i + time;
- var rampVal = this._exponentialApproach(prevEvent.time, prevEvent.value, value, constant, segTime);
- this.linearRampToValueAtTime(this._toUnits(rampVal), segTime);
- }
- return this;
- };
- /**
- * Schedules an exponential continuous change in parameter value from
- * the previous scheduled parameter value to the given value.
- * @param {number} value
- * @param {Time} endTime
- * @returns {Tone.TickSignal} this
- */
- Tone.TickSignal.prototype.exponentialRampToValueAtTime = function (value, time) {
- //aproximate it with multiple linear ramps
- time = this.toSeconds(time);
- value = this._fromUnits(value);
- //start from previously scheduled value
- var prevEvent = this._events.get(time);
- if (prevEvent === null) {
- prevEvent = {
- 'value': this._initial,
- 'time': 0
- };
- }
- var segments = 5;
- var segmentDur = (time - prevEvent.time) / segments;
- for (var i = 0; i <= segments; i++) {
- var segTime = segmentDur * i + prevEvent.time;
- var rampVal = this._exponentialInterpolate(prevEvent.time, prevEvent.value, time, value, segTime);
- this.linearRampToValueAtTime(this._toUnits(rampVal), segTime);
- }
- return this;
- };
- /**
- * Returns the tick value at the time. Takes into account
- * any automation curves scheduled on the signal.
- * @private
- * @param {Time} time The time to get the tick count at
- * @return {Ticks} The number of ticks which have elapsed at the time
- * given any automations.
- */
- Tone.TickSignal.prototype._getTickUntilEvent = function (event, time) {
- if (event === null) {
- event = {
- 'ticks': 0,
- 'time': 0
- };
- }
- var val0 = this.getValueAtTime(event.time);
- var val1 = this.getValueAtTime(time);
- return 0.5 * (time - event.time) * (val0 + val1) + event.ticks;
- };
- /**
- * Returns the tick value at the time. Takes into account
- * any automation curves scheduled on the signal.
- * @param {Time} time The time to get the tick count at
- * @return {Ticks} The number of ticks which have elapsed at the time
- * given any automations.
- */
- Tone.TickSignal.prototype.getTickAtTime = function (time) {
- time = this.toSeconds(time);
- var event = this._events.get(time);
- return this._getTickUntilEvent(event, time);
- };
- /**
- * Return the elapsed time of the number of ticks from the given time
- * @param {Ticks} ticks The number of ticks to calculate
- * @param {Time} time The time to get the next tick from
- * @return {Seconds} The duration of the number of ticks from the given time in seconds
- */
- Tone.TickSignal.prototype.getDurationOfTicks = function (ticks, time) {
- time = this.toSeconds(time);
- var currentTick = this.getTickAtTime(time);
- return this.getTimeOfTick(currentTick + ticks) - time;
- };
- /**
- * Given a tick, returns the time that tick occurs at.
- * @param {Ticks} tick
- * @return {Time} The time that the tick occurs.
- */
- Tone.TickSignal.prototype.getTimeOfTick = function (tick) {
- var before = this._events.get(tick, 'ticks');
- var after = this._events.getAfter(tick, 'ticks');
- if (before && before.ticks === tick) {
- return before.time;
- } else if (before && after && after.type === Tone.TimelineSignal.Type.Linear && before.value !== after.value) {
- var val0 = this.getValueAtTime(before.time);
- var val1 = this.getValueAtTime(after.time);
- var delta = (val1 - val0) / (after.time - before.time);
- var k = Math.sqrt(Math.pow(val0, 2) - 2 * delta * (before.ticks - tick));
- var sol1 = (-val0 + k) / delta;
- var sol2 = (-val0 - k) / delta;
- return (sol1 > 0 ? sol1 : sol2) + before.time;
- } else if (before) {
- if (before.value === 0) {
- return Infinity;
- } else {
- return before.time + (tick - before.ticks) / before.value;
- }
- } else {
- return tick / this._initial;
- }
- };
- return Tone.TickSignal;
- });
- Module(function (Tone) {
-
- /**
- * @class A Timeline State. Provides the methods: setStateAtTime("state", time)
- * and getValueAtTime(time)
.
- *
- * @extends {Tone.Timeline}
- * @param {String} initial The initial state of the TimelineState.
- * Defaults to undefined
- */
- Tone.TimelineState = function (initial) {
- Tone.Timeline.call(this);
- /**
- * The initial state
- * @private
- * @type {String}
- */
- this._initial = initial;
- };
- Tone.extend(Tone.TimelineState, Tone.Timeline);
- /**
- * Returns the scheduled state scheduled before or at
- * the given time.
- * @param {Number} time The time to query.
- * @return {String} The name of the state input in setStateAtTime.
- */
- Tone.TimelineState.prototype.getValueAtTime = function (time) {
- var event = this.get(time);
- if (event !== null) {
- return event.state;
- } else {
- return this._initial;
- }
- };
- /**
- * Add a state to the timeline.
- * @param {String} state The name of the state to set.
- * @param {Number} time The time to query.
- * @returns {Tone.TimelineState} this
- */
- Tone.TimelineState.prototype.setStateAtTime = function (state, time) {
- this.add({
- 'state': state,
- 'time': time
- });
- return this;
- };
- return Tone.TimelineState;
- });
- Module(function (Tone) {
-
- /**
- * @class A sample accurate clock which provides a callback at the given rate.
- * While the callback is not sample-accurate (it is still susceptible to
- * loose JS timing), the time passed in as the argument to the callback
- * is precise. For most applications, it is better to use Tone.Transport
- * instead of the Clock by itself since you can synchronize multiple callbacks.
- *
- * @constructor
- * @extends {Tone.Emitter}
- * @param {function} callback The callback to be invoked with the time of the audio event
- * @param {Frequency} frequency The rate of the callback
- * @example
- * //the callback will be invoked approximately once a second
- * //and will print the time exactly once a second apart.
- * var clock = new Tone.Clock(function(time){
- * console.log(time);
- * }, 1);
- */
- Tone.Clock = function () {
- var options = Tone.defaults(arguments, [
- 'callback',
- 'frequency'
- ], Tone.Clock);
- Tone.Emitter.call(this);
- /**
- * The callback function to invoke at the scheduled tick.
- * @type {Function}
- */
- this.callback = options.callback;
- /**
- * The next time the callback is scheduled.
- * @type {Number}
- * @private
- */
- this._nextTick = 0;
- /**
- * The last state of the clock.
- * @type {State}
- * @private
- */
- this._lastState = Tone.State.Stopped;
- /**
- * The rate the callback function should be invoked.
- * @type {BPM}
- * @signal
- */
- this.frequency = new Tone.TickSignal(options.frequency, Tone.Type.Frequency);
- this._readOnly('frequency');
- /**
- * The number of times the callback was invoked. Starts counting at 0
- * and increments after the callback was invoked.
- * @type {Ticks}
- * @readOnly
- */
- this.ticks = 0;
- /**
- * The state timeline
- * @type {Tone.TimelineState}
- * @private
- */
- this._state = new Tone.TimelineState(Tone.State.Stopped);
- /**
- * The loop function bound to its context.
- * This is necessary to remove the event in the end.
- * @type {Function}
- * @private
- */
- this._boundLoop = this._loop.bind(this);
- //bind a callback to the worker thread
- this.context.on('tick', this._boundLoop);
- };
- Tone.extend(Tone.Clock, Tone.Emitter);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Clock.defaults = {
- 'callback': Tone.noOp,
- 'frequency': 1
- };
- /**
- * Returns the playback state of the source, either "started", "stopped" or "paused".
- * @type {Tone.State}
- * @readOnly
- * @memberOf Tone.Clock#
- * @name state
- */
- Object.defineProperty(Tone.Clock.prototype, 'state', {
- get: function () {
- return this._state.getValueAtTime(this.now());
- }
- });
- /**
- * Start the clock at the given time. Optionally pass in an offset
- * of where to start the tick counter from.
- * @param {Time=} time The time the clock should start
- * @param {Ticks=} offset Where the tick counter starts counting from.
- * @return {Tone.Clock} this
- */
- Tone.Clock.prototype.start = function (time, offset) {
- time = this.toSeconds(time);
- if (this._state.getValueAtTime(time) !== Tone.State.Started) {
- this._state.setStateAtTime(Tone.State.Started, time);
- this._state.get(time).offset = offset;
- }
- return this;
- };
- /**
- * Stop the clock. Stopping the clock resets the tick counter to 0.
- * @param {Time} [time=now] The time when the clock should stop.
- * @returns {Tone.Clock} this
- * @example
- * clock.stop();
- */
- Tone.Clock.prototype.stop = function (time) {
- time = this.toSeconds(time);
- this._state.cancel(time);
- this._state.setStateAtTime(Tone.State.Stopped, time);
- return this;
- };
- /**
- * Pause the clock. Pausing does not reset the tick counter.
- * @param {Time} [time=now] The time when the clock should stop.
- * @returns {Tone.Clock} this
- */
- Tone.Clock.prototype.pause = function (time) {
- time = this.toSeconds(time);
- if (this._state.getValueAtTime(time) === Tone.State.Started) {
- this._state.setStateAtTime(Tone.State.Paused, time);
- }
- return this;
- };
- /**
- * The scheduling loop.
- * @private
- */
- Tone.Clock.prototype._loop = function () {
- //the end of the update interval
- var endTime = this.now() + this.context.updateInterval;
- //the current event at the time of the loop
- var event = this._state.get(endTime);
- if (event) {
- //state change events
- if (event.state !== this._lastState) {
- this._lastState = event.state;
- switch (event.state) {
- case Tone.State.Started:
- if (!Tone.isUndef(event.offset)) {
- this.ticks = event.offset;
- }
- this._nextTick = event.time;
- this.emit('start', event.time, this.ticks);
- break;
- case Tone.State.Stopped:
- this.ticks = 0;
- this.emit('stop', event.time);
- break;
- case Tone.State.Paused:
- this.emit('pause', event.time);
- break;
- }
- }
- //all the tick events
- while (endTime > this._nextTick && this._state) {
- var tickTime = this._nextTick;
- if (this.frequency) {
- this._nextTick += this.frequency.getDurationOfTicks(1, this._nextTick);
- if (event.state === Tone.State.Started) {
- try {
- this.callback(tickTime);
- this.ticks++;
- } catch (e) {
- this.ticks++;
- throw e;
- }
- }
- }
- }
- }
- };
- /**
- * Returns the scheduled state at the given time.
- * @param {Time} time The time to query.
- * @return {String} The name of the state input in setStateAtTime.
- * @example
- * clock.start("+0.1");
- * clock.getStateAtTime("+0.1"); //returns "started"
- */
- Tone.Clock.prototype.getStateAtTime = function (time) {
- time = this.toSeconds(time);
- return this._state.getValueAtTime(time);
- };
- /**
- * Clean up
- * @returns {Tone.Clock} this
- */
- Tone.Clock.prototype.dispose = function () {
- Tone.Emitter.prototype.dispose.call(this);
- this.context.off('tick', this._boundLoop);
- this._writable('frequency');
- this.frequency.dispose();
- this.frequency = null;
- this._boundLoop = null;
- this._nextTick = Infinity;
- this.callback = null;
- this._state.dispose();
- this._state = null;
- };
- return Tone.Clock;
- });
- Module(function (Tone) {
-
- /**
- * @class Similar to Tone.Timeline, but all events represent
- * intervals with both "time" and "duration" times. The
- * events are placed in a tree structure optimized
- * for querying an intersection point with the timeline
- * events. Internally uses an [Interval Tree](https://en.wikipedia.org/wiki/Interval_tree)
- * to represent the data.
- * @extends {Tone}
- */
- Tone.IntervalTimeline = function () {
- Tone.call(this);
- /**
- * The root node of the inteval tree
- * @type {IntervalNode}
- * @private
- */
- this._root = null;
- /**
- * Keep track of the length of the timeline.
- * @type {Number}
- * @private
- */
- this._length = 0;
- };
- Tone.extend(Tone.IntervalTimeline);
- /**
- * The event to add to the timeline. All events must
- * have a time and duration value
- * @param {Object} event The event to add to the timeline
- * @return {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.add = function (event) {
- if (Tone.isUndef(event.time) || Tone.isUndef(event.duration)) {
- throw new Error('Tone.IntervalTimeline: events must have time and duration parameters');
- }
- var node = new IntervalNode(event.time, event.time + event.duration, event);
- if (this._root === null) {
- this._root = node;
- } else {
- this._root.insert(node);
- }
- this._length++;
- // Restructure tree to be balanced
- while (node !== null) {
- node.updateHeight();
- node.updateMax();
- this._rebalance(node);
- node = node.parent;
- }
- return this;
- };
- /**
- * Remove an event from the timeline.
- * @param {Object} event The event to remove from the timeline
- * @return {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.remove = function (event) {
- if (this._root !== null) {
- var results = [];
- this._root.search(event.time, results);
- for (var i = 0; i < results.length; i++) {
- var node = results[i];
- if (node.event === event) {
- this._removeNode(node);
- this._length--;
- break;
- }
- }
- }
- return this;
- };
- /**
- * The number of items in the timeline.
- * @type {Number}
- * @memberOf Tone.IntervalTimeline#
- * @name length
- * @readOnly
- */
- Object.defineProperty(Tone.IntervalTimeline.prototype, 'length', {
- get: function () {
- return this._length;
- }
- });
- /**
- * Remove events whose time time is after the given time
- * @param {Number} time The time to query.
- * @returns {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.cancel = function (after) {
- this.forEachAfter(after, function (event) {
- this.remove(event);
- }.bind(this));
- return this;
- };
- /**
- * Set the root node as the given node
- * @param {IntervalNode} node
- * @private
- */
- Tone.IntervalTimeline.prototype._setRoot = function (node) {
- this._root = node;
- if (this._root !== null) {
- this._root.parent = null;
- }
- };
- /**
- * Replace the references to the node in the node's parent
- * with the replacement node.
- * @param {IntervalNode} node
- * @param {IntervalNode} replacement
- * @private
- */
- Tone.IntervalTimeline.prototype._replaceNodeInParent = function (node, replacement) {
- if (node.parent !== null) {
- if (node.isLeftChild()) {
- node.parent.left = replacement;
- } else {
- node.parent.right = replacement;
- }
- this._rebalance(node.parent);
- } else {
- this._setRoot(replacement);
- }
- };
- /**
- * Remove the node from the tree and replace it with
- * a successor which follows the schema.
- * @param {IntervalNode} node
- * @private
- */
- Tone.IntervalTimeline.prototype._removeNode = function (node) {
- if (node.left === null && node.right === null) {
- this._replaceNodeInParent(node, null);
- } else if (node.right === null) {
- this._replaceNodeInParent(node, node.left);
- } else if (node.left === null) {
- this._replaceNodeInParent(node, node.right);
- } else {
- var balance = node.getBalance();
- var replacement, temp;
- if (balance > 0) {
- if (node.left.right === null) {
- replacement = node.left;
- replacement.right = node.right;
- temp = replacement;
- } else {
- replacement = node.left.right;
- while (replacement.right !== null) {
- replacement = replacement.right;
- }
- replacement.parent.right = replacement.left;
- temp = replacement.parent;
- replacement.left = node.left;
- replacement.right = node.right;
- }
- } else {
- if (node.right.left === null) {
- replacement = node.right;
- replacement.left = node.left;
- temp = replacement;
- } else {
- replacement = node.right.left;
- while (replacement.left !== null) {
- replacement = replacement.left;
- }
- replacement.parent = replacement.parent;
- replacement.parent.left = replacement.right;
- temp = replacement.parent;
- replacement.left = node.left;
- replacement.right = node.right;
- }
- }
- if (node.parent !== null) {
- if (node.isLeftChild()) {
- node.parent.left = replacement;
- } else {
- node.parent.right = replacement;
- }
- } else {
- this._setRoot(replacement);
- }
- // this._replaceNodeInParent(node, replacement);
- this._rebalance(temp);
- }
- node.dispose();
- };
- /**
- * Rotate the tree to the left
- * @param {IntervalNode} node
- * @private
- */
- Tone.IntervalTimeline.prototype._rotateLeft = function (node) {
- var parent = node.parent;
- var isLeftChild = node.isLeftChild();
- // Make node.right the new root of this sub tree (instead of node)
- var pivotNode = node.right;
- node.right = pivotNode.left;
- pivotNode.left = node;
- if (parent !== null) {
- if (isLeftChild) {
- parent.left = pivotNode;
- } else {
- parent.right = pivotNode;
- }
- } else {
- this._setRoot(pivotNode);
- }
- };
- /**
- * Rotate the tree to the right
- * @param {IntervalNode} node
- * @private
- */
- Tone.IntervalTimeline.prototype._rotateRight = function (node) {
- var parent = node.parent;
- var isLeftChild = node.isLeftChild();
- // Make node.left the new root of this sub tree (instead of node)
- var pivotNode = node.left;
- node.left = pivotNode.right;
- pivotNode.right = node;
- if (parent !== null) {
- if (isLeftChild) {
- parent.left = pivotNode;
- } else {
- parent.right = pivotNode;
- }
- } else {
- this._setRoot(pivotNode);
- }
- };
- /**
- * Balance the BST
- * @param {IntervalNode} node
- * @private
- */
- Tone.IntervalTimeline.prototype._rebalance = function (node) {
- var balance = node.getBalance();
- if (balance > 1) {
- if (node.left.getBalance() < 0) {
- this._rotateLeft(node.left);
- } else {
- this._rotateRight(node);
- }
- } else if (balance < -1) {
- if (node.right.getBalance() > 0) {
- this._rotateRight(node.right);
- } else {
- this._rotateLeft(node);
- }
- }
- };
- /**
- * Get an event whose time and duration span the give time. Will
- * return the match whose "time" value is closest to the given time.
- * @param {Object} event The event to add to the timeline
- * @return {Object} The event which spans the desired time
- */
- Tone.IntervalTimeline.prototype.get = function (time) {
- if (this._root !== null) {
- var results = [];
- this._root.search(time, results);
- if (results.length > 0) {
- var max = results[0];
- for (var i = 1; i < results.length; i++) {
- if (results[i].low > max.low) {
- max = results[i];
- }
- }
- return max.event;
- }
- }
- return null;
- };
- /**
- * Iterate over everything in the timeline.
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.forEach = function (callback) {
- if (this._root !== null) {
- var allNodes = [];
- this._root.traverse(function (node) {
- allNodes.push(node);
- });
- for (var i = 0; i < allNodes.length; i++) {
- var ev = allNodes[i].event;
- if (ev) {
- callback(ev);
- }
- }
- }
- return this;
- };
- /**
- * Iterate over everything in the array in which the given time
- * overlaps with the time and duration time of the event.
- * @param {Number} time The time to check if items are overlapping
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.forEachAtTime = function (time, callback) {
- if (this._root !== null) {
- var results = [];
- this._root.search(time, results);
- for (var i = results.length - 1; i >= 0; i--) {
- var ev = results[i].event;
- if (ev) {
- callback(ev);
- }
- }
- }
- return this;
- };
- /**
- * Iterate over everything in the array in which the time is greater
- * than the given time.
- * @param {Number} time The time to check if items are before
- * @param {Function} callback The callback to invoke with every item
- * @returns {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.forEachAfter = function (time, callback) {
- if (this._root !== null) {
- var results = [];
- this._root.searchAfter(time, results);
- for (var i = results.length - 1; i >= 0; i--) {
- var ev = results[i].event;
- callback(ev);
- }
- }
- return this;
- };
- /**
- * Clean up
- * @return {Tone.IntervalTimeline} this
- */
- Tone.IntervalTimeline.prototype.dispose = function () {
- var allNodes = [];
- if (this._root !== null) {
- this._root.traverse(function (node) {
- allNodes.push(node);
- });
- }
- for (var i = 0; i < allNodes.length; i++) {
- allNodes[i].dispose();
- }
- allNodes = null;
- this._root = null;
- return this;
- };
- ///////////////////////////////////////////////////////////////////////////
- // INTERVAL NODE HELPER
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Represents a node in the binary search tree, with the addition
- * of a "high" value which keeps track of the highest value of
- * its children.
- * References:
- * https://brooknovak.wordpress.com/2013/12/07/augmented-interval-tree-in-c/
- * http://www.mif.vu.lt/~valdas/ALGORITMAI/LITERATURA/Cormen/Cormen.pdf
- * @param {Number} low
- * @param {Number} high
- * @private
- */
- var IntervalNode = function (low, high, event) {
- //the event container
- this.event = event;
- //the low value
- this.low = low;
- //the high value
- this.high = high;
- //the high value for this and all child nodes
- this.max = this.high;
- //the nodes to the left
- this._left = null;
- //the nodes to the right
- this._right = null;
- //the parent node
- this.parent = null;
- //the number of child nodes
- this.height = 0;
- };
- /**
- * Insert a node into the correct spot in the tree
- * @param {IntervalNode} node
- */
- IntervalNode.prototype.insert = function (node) {
- if (node.low <= this.low) {
- if (this.left === null) {
- this.left = node;
- } else {
- this.left.insert(node);
- }
- } else {
- if (this.right === null) {
- this.right = node;
- } else {
- this.right.insert(node);
- }
- }
- };
- /**
- * Search the tree for nodes which overlap
- * with the given point
- * @param {Number} point The point to query
- * @param {Array} results The array to put the results
- */
- IntervalNode.prototype.search = function (point, results) {
- // If p is to the right of the rightmost point of any interval
- // in this node and all children, there won't be any matches.
- if (point > this.max) {
- return;
- }
- // Search left children
- if (this.left !== null) {
- this.left.search(point, results);
- }
- // Check this node
- if (this.low <= point && this.high > point) {
- results.push(this);
- }
- // If p is to the left of the time of this interval,
- // then it can't be in any child to the right.
- if (this.low > point) {
- return;
- }
- // Search right children
- if (this.right !== null) {
- this.right.search(point, results);
- }
- };
- /**
- * Search the tree for nodes which are less
- * than the given point
- * @param {Number} point The point to query
- * @param {Array} results The array to put the results
- */
- IntervalNode.prototype.searchAfter = function (point, results) {
- // Check this node
- if (this.low >= point) {
- results.push(this);
- if (this.left !== null) {
- this.left.searchAfter(point, results);
- }
- }
- // search the right side
- if (this.right !== null) {
- this.right.searchAfter(point, results);
- }
- };
- /**
- * Invoke the callback on this element and both it's branches
- * @param {Function} callback
- */
- IntervalNode.prototype.traverse = function (callback) {
- callback(this);
- if (this.left !== null) {
- this.left.traverse(callback);
- }
- if (this.right !== null) {
- this.right.traverse(callback);
- }
- };
- /**
- * Update the height of the node
- */
- IntervalNode.prototype.updateHeight = function () {
- if (this.left !== null && this.right !== null) {
- this.height = Math.max(this.left.height, this.right.height) + 1;
- } else if (this.right !== null) {
- this.height = this.right.height + 1;
- } else if (this.left !== null) {
- this.height = this.left.height + 1;
- } else {
- this.height = 0;
- }
- };
- /**
- * Update the height of the node
- */
- IntervalNode.prototype.updateMax = function () {
- this.max = this.high;
- if (this.left !== null) {
- this.max = Math.max(this.max, this.left.max);
- }
- if (this.right !== null) {
- this.max = Math.max(this.max, this.right.max);
- }
- };
- /**
- * The balance is how the leafs are distributed on the node
- * @return {Number} Negative numbers are balanced to the right
- */
- IntervalNode.prototype.getBalance = function () {
- var balance = 0;
- if (this.left !== null && this.right !== null) {
- balance = this.left.height - this.right.height;
- } else if (this.left !== null) {
- balance = this.left.height + 1;
- } else if (this.right !== null) {
- balance = -(this.right.height + 1);
- }
- return balance;
- };
- /**
- * @returns {Boolean} true if this node is the left child
- * of its parent
- */
- IntervalNode.prototype.isLeftChild = function () {
- return this.parent !== null && this.parent.left === this;
- };
- /**
- * get/set the left node
- * @type {IntervalNode}
- */
- Object.defineProperty(IntervalNode.prototype, 'left', {
- get: function () {
- return this._left;
- },
- set: function (node) {
- this._left = node;
- if (node !== null) {
- node.parent = this;
- }
- this.updateHeight();
- this.updateMax();
- }
- });
- /**
- * get/set the right node
- * @type {IntervalNode}
- */
- Object.defineProperty(IntervalNode.prototype, 'right', {
- get: function () {
- return this._right;
- },
- set: function (node) {
- this._right = node;
- if (node !== null) {
- node.parent = this;
- }
- this.updateHeight();
- this.updateMax();
- }
- });
- /**
- * null out references.
- */
- IntervalNode.prototype.dispose = function () {
- this.parent = null;
- this._left = null;
- this._right = null;
- this.event = null;
- };
- ///////////////////////////////////////////////////////////////////////////
- // END INTERVAL NODE HELPER
- ///////////////////////////////////////////////////////////////////////////
- return Tone.IntervalTimeline;
- });
- Module(function (Tone) {
-
- /**
- * @class Transport for timing musical events.
- * Supports tempo curves and time changes. Unlike browser-based timing (setInterval, requestAnimationFrame)
- * Tone.Transport timing events pass in the exact time of the scheduled event
- * in the argument of the callback function. Pass that time value to the object
- * you're scheduling. input[0]
- * @type {GainNode}
- */
- this.left = this.input[0] = new Tone.Gain();
- /**
- * The right input channel.
- * Alias for input[1]
.
- * @type {GainNode}
- */
- this.right = this.input[1] = new Tone.Gain();
- /**
- * the merger node for the two channels
- * @type {ChannelMergerNode}
- * @private
- */
- this._merger = this.output = this.context.createChannelMerger(2);
- //connections
- this.left.connect(this._merger, 0, 0);
- this.right.connect(this._merger, 0, 1);
- this.left.channelCount = 1;
- this.right.channelCount = 1;
- this.left.channelCountMode = 'explicit';
- this.right.channelCountMode = 'explicit';
- };
- Tone.extend(Tone.Merge, Tone.AudioNode);
- /**
- * Clean up.
- * @returns {Tone.Merge} this
- */
- Tone.Merge.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this.left.dispose();
- this.left = null;
- this.right.dispose();
- this.right = null;
- this._merger.disconnect();
- this._merger = null;
- return this;
- };
- return Tone.Merge;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Meter gets the [RMS](https://en.wikipedia.org/wiki/Root_mean_square)
- * of an input signal with some averaging applied. It can also get the raw
- * value of the input signal.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {String} type Either "level" or "signal".
- * @param {Number} smoothing The amount of smoothing applied between frames.
- * @example
- * var meter = new Tone.Meter();
- * var mic = new Tone.UserMedia().open();
- * //connect mic to the meter
- * mic.connect(meter);
- * //the current level of the mic input
- * var level = meter.value;
- */
- Tone.Meter = function () {
- var options = Tone.defaults(arguments, [
- 'type',
- 'smoothing'
- ], Tone.Meter);
- Tone.AudioNode.call(this);
- /**
- * The type of the meter, either "level" or "signal".
- * A "level" meter will return the volume level (rms) of the
- * input signal and a "signal" meter will return
- * the signal value of the input.
- * @type {String}
- */
- this.type = options.type;
- /**
- * The analyser node which computes the levels.
- * @private
- * @type {Tone.Analyser}
- */
- this.input = this.output = this._analyser = new Tone.Analyser('waveform', 512);
- /**
- * The amount of carryover between the current and last frame.
- * Only applied meter for "level" type.
- * @type {Number}
- */
- this.smoothing = options.smoothing;
- /**
- * The last computed value
- * @type {Number}
- * @private
- */
- this._lastValue = 0;
- };
- Tone.extend(Tone.Meter, Tone.AudioNode);
- /**
- * @private
- * @enum {String}
- */
- Tone.Meter.Type = {
- Level: 'level',
- Signal: 'signal'
- };
- /**
- * The defaults
- * @type {Object}
- * @static
- * @const
- */
- Tone.Meter.defaults = {
- 'smoothing': 0.8,
- 'type': Tone.Meter.Type.Level
- };
- /**
- * The current value of the meter. A value of 1 is
- * "unity".
- * @memberOf Tone.Meter#
- * @type {Number}
- * @name value
- * @readOnly
- */
- Object.defineProperty(Tone.Meter.prototype, 'value', {
- get: function () {
- var signal = this._analyser.analyse();
- if (this.type === Tone.Meter.Type.Level) {
- //rms
- var sum = 0;
- for (var i = 0; i < signal.length; i++) {
- sum += Math.pow(signal[i], 2);
- }
- var rms = Math.sqrt(sum / signal.length);
- //smooth it
- rms = Math.max(rms, this._lastValue * this.smoothing);
- this._lastValue = rms;
- //scale it
- var unity = 0.35;
- var val = rms / unity;
- //scale the output curve
- return Math.sqrt(val);
- } else {
- return signal[0];
- }
- }
- });
- /**
- * Clean up.
- * @returns {Tone.Meter} this
- */
- Tone.Meter.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._analyser.dispose();
- this._analyser = null;
- return this;
- };
- return Tone.Meter;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Split splits an incoming signal into left and right channels.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @example
- * var split = new Tone.Split();
- * stereoSignal.connect(split);
- */
- Tone.Split = function () {
- Tone.AudioNode.call(this);
- this.createInsOuts(0, 2);
- /**
- * @type {ChannelSplitterNode}
- * @private
- */
- this._splitter = this.input = this.context.createChannelSplitter(2);
- this._splitter.channelCount = 2;
- this._splitter.channelCountMode = 'explicit';
- /**
- * Left channel output.
- * Alias for output[0]
- * @type {Tone.Gain}
- */
- this.left = this.output[0] = new Tone.Gain();
- /**
- * Right channel output.
- * Alias for output[1]
- * @type {Tone.Gain}
- */
- this.right = this.output[1] = new Tone.Gain();
- //connections
- this._splitter.connect(this.left, 0, 0);
- this._splitter.connect(this.right, 1, 0);
- };
- Tone.extend(Tone.Split, Tone.AudioNode);
- /**
- * Clean up.
- * @returns {Tone.Split} this
- */
- Tone.Split.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._splitter.disconnect();
- this.left.dispose();
- this.left = null;
- this.right.dispose();
- this.right = null;
- this._splitter = null;
- return this;
- };
- return Tone.Split;
- });
- Module(function (Tone) {
-
- /**
- * @class Mid/Side processing separates the the 'mid' signal
- * (which comes out of both the left and the right channel)
- * and the 'side' (which only comes out of the the side channels).
- * Mid = (Left+Right)/sqrt(2); // obtain mid-signal from left and right
- * Side = (Left-Right)/sqrt(2); // obtain side-signal from left and righ
- *
- *
- * @extends {Tone.AudioNode}
- * @constructor
- */
- Tone.MidSideSplit = function () {
- Tone.AudioNode.call(this);
- this.createInsOuts(0, 2);
- /**
- * split the incoming signal into left and right channels
- * @type {Tone.Split}
- * @private
- */
- this._split = this.input = new Tone.Split();
- /**
- * The mid send. Connect to mid processing. Alias for
- * output[0]
- * @type {Tone.Expr}
- */
- this.mid = this.output[0] = new Tone.Expr('($0 + $1) * $2');
- /**
- * The side output. Connect to side processing. Alias for
- * output[1]
- * @type {Tone.Expr}
- */
- this.side = this.output[1] = new Tone.Expr('($0 - $1) * $2');
- this._split.connect(this.mid, 0, 0);
- this._split.connect(this.mid, 1, 1);
- this._split.connect(this.side, 0, 0);
- this._split.connect(this.side, 1, 1);
- this.context.getConstant(Math.SQRT1_2).connect(this.mid, 0, 2);
- this.context.getConstant(Math.SQRT1_2).connect(this.side, 0, 2);
- };
- Tone.extend(Tone.MidSideSplit, Tone.AudioNode);
- /**
- * clean up
- * @returns {Tone.MidSideSplit} this
- */
- Tone.MidSideSplit.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this.mid.dispose();
- this.mid = null;
- this.side.dispose();
- this.side = null;
- this._split.dispose();
- this._split = null;
- return this;
- };
- return Tone.MidSideSplit;
- });
- Module(function (Tone) {
-
- /**
- * @class Mid/Side processing separates the the 'mid' signal
- * (which comes out of both the left and the right channel)
- * and the 'side' (which only comes out of the the side channels).
- * MidSideMerge merges the mid and side signal after they've been seperated
- * by Tone.MidSideSplit.
- * Left = (Mid+Side)/sqrt(2); // obtain left signal from mid and side
- * Right = (Mid-Side)/sqrt(2); // obtain right signal from mid and side
- *
- *
- * @extends {Tone.AudioNode}
- * @constructor
- */
- Tone.MidSideMerge = function () {
- Tone.AudioNode.call(this);
- this.createInsOuts(2, 0);
- /**
- * The mid signal input. Alias for
- * input[0]
- * @type {Tone.Gain}
- */
- this.mid = this.input[0] = new Tone.Gain();
- /**
- * recombine the mid/side into Left
- * @type {Tone.Expr}
- * @private
- */
- this._left = new Tone.Expr('($0 + $1) * $2');
- /**
- * The side signal input. Alias for
- * input[1]
- * @type {Tone.Gain}
- */
- this.side = this.input[1] = new Tone.Gain();
- /**
- * recombine the mid/side into Right
- * @type {Tone.Expr}
- * @private
- */
- this._right = new Tone.Expr('($0 - $1) * $2');
- /**
- * Merge the left/right signal back into a stereo signal.
- * @type {Tone.Merge}
- * @private
- */
- this._merge = this.output = new Tone.Merge();
- this.mid.connect(this._left, 0, 0);
- this.side.connect(this._left, 0, 1);
- this.mid.connect(this._right, 0, 0);
- this.side.connect(this._right, 0, 1);
- this._left.connect(this._merge, 0, 0);
- this._right.connect(this._merge, 0, 1);
- this.context.getConstant(Math.SQRT1_2).connect(this._left, 0, 2);
- this.context.getConstant(Math.SQRT1_2).connect(this._right, 0, 2);
- };
- Tone.extend(Tone.MidSideMerge, Tone.AudioNode);
- /**
- * clean up
- * @returns {Tone.MidSideMerge} this
- */
- Tone.MidSideMerge.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this.mid.dispose();
- this.mid = null;
- this.side.dispose();
- this.side = null;
- this._left.dispose();
- this._left = null;
- this._right.dispose();
- this._right = null;
- this._merge.dispose();
- this._merge = null;
- return this;
- };
- return Tone.MidSideMerge;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.MidSideCompressor applies two different compressors to the mid
- * and side signal components. See Tone.MidSideSplit.
- *
- * @extends {Tone.AudioNode}
- * @param {Object} options The options that are passed to the mid and side
- * compressors.
- * @constructor
- */
- Tone.MidSideCompressor = function (options) {
- Tone.AudioNode.call(this);
- options = Tone.defaultArg(options, Tone.MidSideCompressor.defaults);
- /**
- * the mid/side split
- * @type {Tone.MidSideSplit}
- * @private
- */
- this._midSideSplit = this.input = new Tone.MidSideSplit();
- /**
- * the mid/side recombination
- * @type {Tone.MidSideMerge}
- * @private
- */
- this._midSideMerge = this.output = new Tone.MidSideMerge();
- /**
- * The compressor applied to the mid signal
- * @type {Tone.Compressor}
- */
- this.mid = new Tone.Compressor(options.mid);
- /**
- * The compressor applied to the side signal
- * @type {Tone.Compressor}
- */
- this.side = new Tone.Compressor(options.side);
- this._midSideSplit.mid.chain(this.mid, this._midSideMerge.mid);
- this._midSideSplit.side.chain(this.side, this._midSideMerge.side);
- this._readOnly([
- 'mid',
- 'side'
- ]);
- };
- Tone.extend(Tone.MidSideCompressor, Tone.AudioNode);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.MidSideCompressor.defaults = {
- 'mid': {
- 'ratio': 3,
- 'threshold': -24,
- 'release': 0.03,
- 'attack': 0.02,
- 'knee': 16
- },
- 'side': {
- 'ratio': 6,
- 'threshold': -30,
- 'release': 0.25,
- 'attack': 0.03,
- 'knee': 10
- }
- };
- /**
- * Clean up.
- * @returns {Tone.MidSideCompressor} this
- */
- Tone.MidSideCompressor.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable([
- 'mid',
- 'side'
- ]);
- this.mid.dispose();
- this.mid = null;
- this.side.dispose();
- this.side = null;
- this._midSideSplit.dispose();
- this._midSideSplit = null;
- this._midSideMerge.dispose();
- this._midSideMerge = null;
- return this;
- };
- return Tone.MidSideCompressor;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Mono coerces the incoming mono or stereo signal into a mono signal
- * where both left and right channels have the same value. This can be useful
- * for [stereo imaging](https://en.wikipedia.org/wiki/Stereo_imaging).
- *
- * @extends {Tone.AudioNode}
- * @constructor
- */
- Tone.Mono = function () {
- Tone.AudioNode.call(this);
- this.createInsOuts(1, 0);
- /**
- * merge the signal
- * @type {Tone.Merge}
- * @private
- */
- this._merge = this.output = new Tone.Merge();
- this.input.connect(this._merge, 0, 0);
- this.input.connect(this._merge, 0, 1);
- this.input.gain.value = Tone.dbToGain(-10);
- };
- Tone.extend(Tone.Mono);
- /**
- * clean up
- * @returns {Tone.Mono} this
- */
- Tone.Mono.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._merge.dispose();
- this._merge = null;
- return this;
- };
- return Tone.Mono;
- });
- Module(function (Tone) {
-
- /**
- * @class A compressor with seperate controls over low/mid/high dynamics
- *
- * @extends {Tone.AudioNode}
- * @constructor
- * @param {Object} options The low/mid/high compressor settings.
- * @example
- * var multiband = new Tone.MultibandCompressor({
- * "lowFrequency" : 200,
- * "highFrequency" : 1300
- * "low" : {
- * "threshold" : -12
- * }
- * })
- */
- Tone.MultibandCompressor = function (options) {
- Tone.AudioNode.call(this);
- options = Tone.defaultArg(arguments, Tone.MultibandCompressor.defaults);
- /**
- * split the incoming signal into high/mid/low
- * @type {Tone.MultibandSplit}
- * @private
- */
- this._splitter = this.input = new Tone.MultibandSplit({
- 'lowFrequency': options.lowFrequency,
- 'highFrequency': options.highFrequency
- });
- /**
- * low/mid crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.lowFrequency = this._splitter.lowFrequency;
- /**
- * mid/high crossover frequency.
- * @type {Frequency}
- * @signal
- */
- this.highFrequency = this._splitter.highFrequency;
- /**
- * the output
- * @type {Tone.Gain}
- * @private
- */
- this.output = new Tone.Gain();
- /**
- * The compressor applied to the low frequencies.
- * @type {Tone.Compressor}
- */
- this.low = new Tone.Compressor(options.low);
- /**
- * The compressor applied to the mid frequencies.
- * @type {Tone.Compressor}
- */
- this.mid = new Tone.Compressor(options.mid);
- /**
- * The compressor applied to the high frequencies.
- * @type {Tone.Compressor}
- */
- this.high = new Tone.Compressor(options.high);
- //connect the compressor
- this._splitter.low.chain(this.low, this.output);
- this._splitter.mid.chain(this.mid, this.output);
- this._splitter.high.chain(this.high, this.output);
- this._readOnly([
- 'high',
- 'mid',
- 'low',
- 'highFrequency',
- 'lowFrequency'
- ]);
- };
- Tone.extend(Tone.MultibandCompressor, Tone.AudioNode);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.MultibandCompressor.defaults = {
- 'low': Tone.Compressor.defaults,
- 'mid': Tone.Compressor.defaults,
- 'high': Tone.Compressor.defaults,
- 'lowFrequency': 250,
- 'highFrequency': 2000
- };
- /**
- * clean up
- * @returns {Tone.MultibandCompressor} this
- */
- Tone.MultibandCompressor.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._splitter.dispose();
- this._writable([
- 'high',
- 'mid',
- 'low',
- 'highFrequency',
- 'lowFrequency'
- ]);
- this.low.dispose();
- this.mid.dispose();
- this.high.dispose();
- this._splitter = null;
- this.low = null;
- this.mid = null;
- this.high = null;
- this.lowFrequency = null;
- this.highFrequency = null;
- return this;
- };
- return Tone.MultibandCompressor;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Panner is an equal power Left/Right Panner and does not
- * support 3D. Panner uses the StereoPannerNode when available.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {NormalRange} [initialPan=0] The initail panner value (center).
- * @example
- * //pan the input signal hard right.
- * var panner = new Tone.Panner(1);
- */
- Tone.Panner = function (initialPan) {
- Tone.AudioNode.call(this);
- if (Tone.Panner.hasStereoPanner) {
- /**
- * the panner node
- * @type {StereoPannerNode}
- * @private
- */
- this._panner = this.input = this.output = this.context.createStereoPanner();
- /**
- * The pan control. -1 = hard left, 1 = hard right.
- * @type {NormalRange}
- * @signal
- */
- this.pan = this._panner.pan;
- } else {
- /**
- * the dry/wet knob
- * @type {Tone.CrossFade}
- * @private
- */
- this._crossFade = new Tone.CrossFade();
- /**
- * @type {Tone.Merge}
- * @private
- */
- this._merger = this.output = new Tone.Merge();
- /**
- * @type {Tone.Split}
- * @private
- */
- this._splitter = this.input = new Tone.Split();
- /**
- * The pan control. -1 = hard left, 1 = hard right.
- * @type {AudioRange}
- * @signal
- */
- this.pan = new Tone.Signal(0, Tone.Type.AudioRange);
- /**
- * always sends 0
- * @type {Tone.Zero}
- * @private
- */
- this._zero = new Tone.Zero();
- /**
- * The analog to gain conversion
- * @type {Tone.AudioToGain}
- * @private
- */
- this._a2g = new Tone.AudioToGain();
- //CONNECTIONS:
- this._zero.connect(this._a2g);
- this.pan.chain(this._a2g, this._crossFade.fade);
- //left channel is a, right channel is b
- this._splitter.connect(this._crossFade, 0, 0);
- this._splitter.connect(this._crossFade, 1, 1);
- //merge it back together
- this._crossFade.a.connect(this._merger, 0, 0);
- this._crossFade.b.connect(this._merger, 0, 1);
- }
- //initial value
- this.pan.value = Tone.defaultArg(initialPan, 0);
- this._readOnly('pan');
- };
- Tone.extend(Tone.Panner, Tone.AudioNode);
- /**
- * Indicates if the panner is using the new StereoPannerNode internally
- * @type {Boolean}
- * @static
- * @private
- * @readOnly
- */
- Tone.Panner.hasStereoPanner = Tone.context && Tone.isFunction(Tone.context.createStereoPanner);
- /**
- * Clean up.
- * @returns {Tone.Panner} this
- */
- Tone.Panner.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable('pan');
- if (Tone.Panner.hasStereoPanner) {
- this._panner.disconnect();
- this._panner = null;
- this.pan = null;
- } else {
- this._zero.dispose();
- this._zero = null;
- this._crossFade.dispose();
- this._crossFade = null;
- this._splitter.dispose();
- this._splitter = null;
- this._merger.dispose();
- this._merger = null;
- this.pan.dispose();
- this.pan = null;
- this._a2g.dispose();
- this._a2g = null;
- }
- return this;
- };
- return Tone.Panner;
- });
- Module(function (Tone) {
-
- /**
- * @class A spatialized panner node which supports equalpower or HRTF panning.
- * Tries to normalize the API across various browsers. See Tone.Listener
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Number} positionX The initial x position.
- * @param {Number} positionY The initial y position.
- * @param {Number} positionZ The initial z position.
- */
- Tone.Panner3D = function () {
- var options = Tone.defaults(arguments, [
- 'positionX',
- 'positionY',
- 'positionZ'
- ], Tone.Panner3D);
- Tone.AudioNode.call(this);
- /**
- * The panner node
- * @type {PannerNode}
- * @private
- */
- this._panner = this.input = this.output = this.context.createPanner();
- //set some values
- this._panner.panningModel = options.panningModel;
- this._panner.maxDistance = options.maxDistance;
- this._panner.distanceModel = options.distanceModel;
- this._panner.coneOuterGain = options.coneOuterGain;
- this._panner.coneOuterAngle = options.coneOuterAngle;
- this._panner.coneInnerAngle = options.coneInnerAngle;
- this._panner.refDistance = options.refDistance;
- this._panner.rolloffFactor = options.rolloffFactor;
- /**
- * Holds the current orientation
- * @type {Array}
- * @private
- */
- this._orientation = [
- options.orientationX,
- options.orientationY,
- options.orientationZ
- ];
- /**
- * Holds the current position
- * @type {Array}
- * @private
- */
- this._position = [
- options.positionX,
- options.positionY,
- options.positionZ
- ];
- // set the default position/orientation
- this.orientationX = options.orientationX;
- this.orientationY = options.orientationY;
- this.orientationZ = options.orientationZ;
- this.positionX = options.positionX;
- this.positionY = options.positionY;
- this.positionZ = options.positionZ;
- };
- Tone.extend(Tone.Panner3D, Tone.AudioNode);
- /**
- * Defaults according to the specification
- * @static
- * @const
- * @type {Object}
- */
- Tone.Panner3D.defaults = {
- 'positionX': 0,
- 'positionY': 0,
- 'positionZ': 0,
- 'orientationX': 0,
- 'orientationY': 0,
- 'orientationZ': 0,
- 'panningModel': 'equalpower',
- 'maxDistance': 10000,
- 'distanceModel': 'inverse',
- 'coneOuterGain': 0,
- 'coneOuterAngle': 360,
- 'coneInnerAngle': 360,
- 'refDistance': 1,
- 'rolloffFactor': 1
- };
- /**
- * The ramp time which is applied to the setTargetAtTime
- * @type {Number}
- * @private
- */
- Tone.Panner3D.prototype._rampTimeConstant = 0.01;
- /**
- * Sets the position of the source in 3d space.
- * @param {Number} x
- * @param {Number} y
- * @param {Number} z
- * @return {Tone.Panner3D} this
- */
- Tone.Panner3D.prototype.setPosition = function (x, y, z) {
- if (this._panner.positionX) {
- var now = this.now();
- this._panner.positionX.setTargetAtTime(x, now, this._rampTimeConstant);
- this._panner.positionY.setTargetAtTime(y, now, this._rampTimeConstant);
- this._panner.positionZ.setTargetAtTime(z, now, this._rampTimeConstant);
- } else {
- this._panner.setPosition(x, y, z);
- }
- this._position = Array.prototype.slice.call(arguments);
- return this;
- };
- /**
- * Sets the orientation of the source in 3d space.
- * @param {Number} x
- * @param {Number} y
- * @param {Number} z
- * @return {Tone.Panner3D} this
- */
- Tone.Panner3D.prototype.setOrientation = function (x, y, z) {
- if (this._panner.orientationX) {
- var now = this.now();
- this._panner.orientationX.setTargetAtTime(x, now, this._rampTimeConstant);
- this._panner.orientationY.setTargetAtTime(y, now, this._rampTimeConstant);
- this._panner.orientationZ.setTargetAtTime(z, now, this._rampTimeConstant);
- } else {
- this._panner.setOrientation(x, y, z);
- }
- this._orientation = Array.prototype.slice.call(arguments);
- return this;
- };
- /**
- * The x position of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name positionX
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'positionX', {
- set: function (pos) {
- this._position[0] = pos;
- this.setPosition.apply(this, this._position);
- },
- get: function () {
- return this._position[0];
- }
- });
- /**
- * The y position of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name positionY
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'positionY', {
- set: function (pos) {
- this._position[1] = pos;
- this.setPosition.apply(this, this._position);
- },
- get: function () {
- return this._position[1];
- }
- });
- /**
- * The z position of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name positionZ
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'positionZ', {
- set: function (pos) {
- this._position[2] = pos;
- this.setPosition.apply(this, this._position);
- },
- get: function () {
- return this._position[2];
- }
- });
- /**
- * The x orientation of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name orientationX
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'orientationX', {
- set: function (pos) {
- this._orientation[0] = pos;
- this.setOrientation.apply(this, this._orientation);
- },
- get: function () {
- return this._orientation[0];
- }
- });
- /**
- * The y orientation of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name orientationY
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'orientationY', {
- set: function (pos) {
- this._orientation[1] = pos;
- this.setOrientation.apply(this, this._orientation);
- },
- get: function () {
- return this._orientation[1];
- }
- });
- /**
- * The z orientation of the panner object.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name orientationZ
- */
- Object.defineProperty(Tone.Panner3D.prototype, 'orientationZ', {
- set: function (pos) {
- this._orientation[2] = pos;
- this.setOrientation.apply(this, this._orientation);
- },
- get: function () {
- return this._orientation[2];
- }
- });
- /**
- * Proxy a property on the panner to an exposed public propery
- * @param {String} prop
- * @private
- */
- Tone.Panner3D._aliasProperty = function (prop) {
- Object.defineProperty(Tone.Panner3D.prototype, prop, {
- set: function (val) {
- this._panner[prop] = val;
- },
- get: function () {
- return this._panner[prop];
- }
- });
- };
- /**
- * The panning model. Either "equalpower" or "HRTF".
- * @type {String}
- * @memberOf Tone.Panner3D#
- * @name panningModel
- */
- Tone.Panner3D._aliasProperty('panningModel');
- /**
- * A reference distance for reducing volume as source move further from the listener
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name refDistance
- */
- Tone.Panner3D._aliasProperty('refDistance');
- /**
- * Describes how quickly the volume is reduced as source moves away from listener.
- * @type {Number}
- * @memberOf Tone.Panner3D#
- * @name rolloffFactor
- */
- Tone.Panner3D._aliasProperty('rolloffFactor');
- /**
- * The distance model used by, "linear", "inverse", or "exponential".
- * @type {String}
- * @memberOf Tone.Panner3D#
- * @name distanceModel
- */
- Tone.Panner3D._aliasProperty('distanceModel');
- /**
- * The angle, in degrees, inside of which there will be no volume reduction
- * @type {Degrees}
- * @memberOf Tone.Panner3D#
- * @name coneInnerAngle
- */
- Tone.Panner3D._aliasProperty('coneInnerAngle');
- /**
- * The angle, in degrees, outside of which the volume will be reduced
- * to a constant value of coneOuterGain
- * @type {Degrees}
- * @memberOf Tone.Panner3D#
- * @name coneOuterAngle
- */
- Tone.Panner3D._aliasProperty('coneOuterAngle');
- /**
- * The gain outside of the coneOuterAngle
- * @type {Gain}
- * @memberOf Tone.Panner3D#
- * @name coneOuterGain
- */
- Tone.Panner3D._aliasProperty('coneOuterGain');
- /**
- * The maximum distance between source and listener,
- * after which the volume will not be reduced any further.
- * @type {Positive}
- * @memberOf Tone.Panner3D#
- * @name maxDistance
- */
- Tone.Panner3D._aliasProperty('maxDistance');
- /**
- * Clean up.
- * @returns {Tone.Panner3D} this
- */
- Tone.Panner3D.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._panner.disconnect();
- this._panner = null;
- this._orientation = null;
- this._position = null;
- return this;
- };
- return Tone.Panner3D;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.PanVol is a Tone.Panner and Tone.Volume in one.
- *
- * @extends {Tone.AudioNode}
- * @constructor
- * @param {AudioRange} pan the initial pan
- * @param {number} volume The output volume.
- * @example
- * //pan the incoming signal left and drop the volume
- * var panVol = new Tone.PanVol(-0.25, -12);
- */
- Tone.PanVol = function () {
- var options = Tone.defaults(arguments, [
- 'pan',
- 'volume'
- ], Tone.PanVol);
- Tone.AudioNode.call(this);
- /**
- * The panning node
- * @type {Tone.Panner}
- * @private
- */
- this._panner = this.input = new Tone.Panner(options.pan);
- /**
- * The L/R panning control.
- * @type {AudioRange}
- * @signal
- */
- this.pan = this._panner.pan;
- /**
- * The volume node
- * @type {Tone.Volume}
- * @private
- */
- this._volume = this.output = new Tone.Volume(options.volume);
- /**
- * The volume control in decibels.
- * @type {Decibels}
- * @signal
- */
- this.volume = this._volume.volume;
- //connections
- this._panner.connect(this._volume);
- this.mute = options.mute;
- this._readOnly([
- 'pan',
- 'volume'
- ]);
- };
- Tone.extend(Tone.PanVol, Tone.AudioNode);
- /**
- * The defaults
- * @type {Object}
- * @const
- * @static
- */
- Tone.PanVol.defaults = {
- 'pan': 0,
- 'volume': 0,
- 'mute': false
- };
- /**
- * Mute/unmute the volume
- * @memberOf Tone.PanVol#
- * @name mute
- * @type {Boolean}
- */
- Object.defineProperty(Tone.PanVol.prototype, 'mute', {
- get: function () {
- return this._volume.mute;
- },
- set: function (mute) {
- this._volume.mute = mute;
- }
- });
- /**
- * clean up
- * @returns {Tone.PanVol} this
- */
- Tone.PanVol.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._writable([
- 'pan',
- 'volume'
- ]);
- this._panner.dispose();
- this._panner = null;
- this.pan = null;
- this._volume.dispose();
- this._volume = null;
- this.volume = null;
- return this;
- };
- return Tone.PanVol;
- });
- Module(function (Tone) {
- /**
- * @class Tone.Solo lets you isolate a specific audio stream. When
- * an instance is set to `solo=true`, it will mute all other instances.
- * @extends {Tone.AudioNode}
- * @example
- * var soloA = new Tone.Solo()
- * var soloB = new Tone.Solo()
- * soloA.solo = true
- * //no audio will pass through soloB
- */
- Tone.Solo = function () {
- var options = Tone.defaults(arguments, ['solo'], Tone.Solo);
- Tone.AudioNode.call(this);
- /**
- * The input and output node
- * @type {Tone.Gain}
- */
- this.input = this.output = new Tone.Gain();
- /**
- * A bound _soloed method
- * @type {Function}
- * @private
- */
- this._soloBind = this._soloed.bind(this);
- //listen for solo events class-wide.
- this.context.on('solo', this._soloBind);
- //set initially
- this.solo = options.solo;
- };
- Tone.extend(Tone.Solo, Tone.AudioNode);
- /**
- * The defaults
- * @type {Object}
- * @static
- */
- Tone.Solo.defaults = { solo: false };
- /**
- * Isolates this instance and mutes all other instances of Tone.Solo.
- * Only one instance can be soloed at a time. A soloed
- * instance will report `solo=false` when another instance is soloed.
- * @memberOf Tone.Solo#
- * @type {Boolean}
- * @name solo
- */
- Object.defineProperty(Tone.Solo.prototype, 'solo', {
- get: function () {
- return this._isSoloed();
- },
- set: function (solo) {
- if (solo) {
- this._addSolo();
- } else {
- this._removeSolo();
- }
- this.context.emit('solo', this);
- }
- });
- /**
- * If the current instance is muted, i.e. another instance is soloed
- * @memberOf Tone.Solo#
- * @type {Boolean}
- * @name muted
- * @readOnly
- */
- Object.defineProperty(Tone.Solo.prototype, 'muted', {
- get: function () {
- return this.input.gain.value === 0;
- }
- });
- /**
- * Add this to the soloed array
- * @private
- */
- Tone.Solo.prototype._addSolo = function () {
- if (!Tone.isArray(this.context._currentSolo)) {
- this.context._currentSolo = [];
- }
- if (!this._isSoloed()) {
- this.context._currentSolo.push(this);
- }
- };
- /**
- * Remove this from the soloed array
- * @private
- */
- Tone.Solo.prototype._removeSolo = function () {
- if (this._isSoloed()) {
- var index = this.context._currentSolo.indexOf(this);
- this.context._currentSolo.splice(index, 1);
- }
- };
- /**
- * @return {Boolean} Is this on the soloed array
- * @private
- */
- Tone.Solo.prototype._isSoloed = function () {
- if (Tone.isArray(this.context._currentSolo)) {
- return this.context._currentSolo.length !== 0 && this.context._currentSolo.indexOf(this) !== -1;
- } else {
- return false;
- }
- };
- /**
- * @return {Boolean} Returns true if no one is soloed
- * @private
- */
- Tone.Solo.prototype._noSolos = function () {
- return !Tone.isArray(this.context._currentSolo) || this.context._currentSolo.length === 0;
- };
- /**
- * Solo the current instance and unsolo all other instances.
- * @param {Tone.Solo} instance The instance which is being soloed/unsoloed.
- * @private
- */
- Tone.Solo.prototype._soloed = function () {
- if (this._isSoloed()) {
- this.input.gain.value = 1;
- } else if (this._noSolos()) {
- //no one is soloed
- this.input.gain.value = 1;
- } else {
- this.input.gain.value = 0;
- }
- };
- /**
- * Clean up
- * @return {Tone.Solo} this
- */
- Tone.Solo.prototype.dispose = function () {
- this.context.off('solo', this._soloBind);
- this._removeSolo();
- this._soloBind = null;
- Tone.AudioNode.prototype.dispose.call(this);
- return this;
- };
- return Tone.Solo;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.CtrlInterpolate will interpolate between given values based
- * on the "index" property. Passing in an array or object literal
- * will interpolate each of the parameters. Note (i.e. "C3")
- * and Time (i.e. "4n + 2") can be interpolated. All other values are
- * assumed to be numbers.
- * @example
- * var interp = new Tone.CtrlInterpolate([0, 2, 9, 4]);
- * interp.index = 0.75;
- * interp.value; //returns 1.5
- *
- * @example
- * var interp = new Tone.CtrlInterpolate([
- * [2, 4, 5],
- * [9, 3, 2],
- * ]);
- * @param {Array} values The array of values to interpolate over
- * @param {Positive} index The initial interpolation index.
- * @extends {Tone}
- */
- Tone.CtrlInterpolate = function () {
- var options = Tone.defaults(arguments, [
- 'values',
- 'index'
- ], Tone.CtrlInterpolate);
- Tone.call(this);
- /**
- * The values to interpolate between
- * @type {Array}
- */
- this.values = options.values;
- /**
- * The interpolated index between values. For example: a value of 1.5
- * would interpolate equally between the value at index 1
- * and the value at index 2.
- * @example
- * interp.index = 0;
- * interp.value; //returns the value at 0
- * interp.index = 0.5;
- * interp.value; //returns the value between indices 0 and 1.
- * @type {Positive}
- */
- this.index = options.index;
- };
- Tone.extend(Tone.CtrlInterpolate);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.CtrlInterpolate.defaults = {
- 'index': 0,
- 'values': []
- };
- /**
- * The current interpolated value based on the index
- * @readOnly
- * @memberOf Tone.CtrlInterpolate#
- * @type {*}
- * @name value
- */
- Object.defineProperty(Tone.CtrlInterpolate.prototype, 'value', {
- get: function () {
- var index = this.index;
- index = Math.min(index, this.values.length - 1);
- var lowerPosition = Math.floor(index);
- var lower = this.values[lowerPosition];
- var upper = this.values[Math.ceil(index)];
- return this._interpolate(index - lowerPosition, lower, upper);
- }
- });
- /**
- * Internal interpolation routine
- * @param {NormalRange} index The index between the lower and upper
- * @param {*} lower
- * @param {*} upper
- * @return {*} The interpolated value
- * @private
- */
- Tone.CtrlInterpolate.prototype._interpolate = function (index, lower, upper) {
- if (Tone.isArray(lower)) {
- var retArray = [];
- for (var i = 0; i < lower.length; i++) {
- retArray[i] = this._interpolate(index, lower[i], upper[i]);
- }
- return retArray;
- } else if (Tone.isObject(lower)) {
- var retObj = {};
- for (var attr in lower) {
- retObj[attr] = this._interpolate(index, lower[attr], upper[attr]);
- }
- return retObj;
- } else {
- lower = this._toNumber(lower);
- upper = this._toNumber(upper);
- return (1 - index) * lower + index * upper;
- }
- };
- /**
- * Convert from the given type into a number
- * @param {Number|String} value
- * @return {Number}
- * @private
- */
- Tone.CtrlInterpolate.prototype._toNumber = function (val) {
- if (Tone.isNumber(val)) {
- return val;
- } else {
- //otherwise assume that it's Time...
- return this.toSeconds(val);
- }
- };
- /**
- * Clean up
- * @return {Tone.CtrlInterpolate} this
- */
- Tone.CtrlInterpolate.prototype.dispose = function () {
- this.values = null;
- };
- return Tone.CtrlInterpolate;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.CtrlMarkov represents a Markov Chain where each call
- * to Tone.CtrlMarkov.next will move to the next state. If the next
- * state choice is an array, the next state is chosen randomly with
- * even probability for all of the choices. For a weighted probability
- * of the next choices, pass in an object with "state" and "probability" attributes.
- * The probabilities will be normalized and then chosen. If no next options
- * are given for the current state, the state will stay there.
- * @extends {Tone}
- * @example
- * var chain = new Tone.CtrlMarkov({
- * "beginning" : ["end", "middle"],
- * "middle" : "end"
- * });
- * chain.value = "beginning";
- * chain.next(); //returns "end" or "middle" with 50% probability
- *
- * @example
- * var chain = new Tone.CtrlMarkov({
- * "beginning" : [{"value" : "end", "probability" : 0.8},
- * {"value" : "middle", "probability" : 0.2}],
- * "middle" : "end"
- * });
- * chain.value = "beginning";
- * chain.next(); //returns "end" with 80% probability or "middle" with 20%.
- * @param {Object} values An object with the state names as the keys
- * and the next state(s) as the values.
- */
- Tone.CtrlMarkov = function (values, initial) {
- Tone.call(this);
- /**
- * The Markov values with states as the keys
- * and next state(s) as the values.
- * @type {Object}
- */
- this.values = Tone.defaultArg(values, {});
- /**
- * The current state of the Markov values. The next
- * state will be evaluated and returned when Tone.CtrlMarkov.next
- * is invoked.
- * @type {String}
- */
- this.value = Tone.defaultArg(initial, Object.keys(this.values)[0]);
- };
- Tone.extend(Tone.CtrlMarkov);
- /**
- * Returns the next state of the Markov values.
- * @return {String}
- */
- Tone.CtrlMarkov.prototype.next = function () {
- if (this.values.hasOwnProperty(this.value)) {
- var next = this.values[this.value];
- if (Tone.isArray(next)) {
- var distribution = this._getProbDistribution(next);
- var rand = Math.random();
- var total = 0;
- for (var i = 0; i < distribution.length; i++) {
- var dist = distribution[i];
- if (rand > total && rand < total + dist) {
- var chosen = next[i];
- if (Tone.isObject(chosen)) {
- this.value = chosen.value;
- } else {
- this.value = chosen;
- }
- }
- total += dist;
- }
- } else {
- this.value = next;
- }
- }
- return this.value;
- };
- /**
- * Choose randomly from an array weighted options in the form
- * {"state" : string, "probability" : number} or an array of values
- * @param {Array} options
- * @return {Array} The randomly selected choice
- * @private
- */
- Tone.CtrlMarkov.prototype._getProbDistribution = function (options) {
- var distribution = [];
- var total = 0;
- var needsNormalizing = false;
- for (var i = 0; i < options.length; i++) {
- var option = options[i];
- if (Tone.isObject(option)) {
- needsNormalizing = true;
- distribution[i] = option.probability;
- } else {
- distribution[i] = 1 / options.length;
- }
- total += distribution[i];
- }
- if (needsNormalizing) {
- //normalize the values
- for (var j = 0; j < distribution.length; j++) {
- distribution[j] = distribution[j] / total;
- }
- }
- return distribution;
- };
- /**
- * Clean up
- * @return {Tone.CtrlMarkov} this
- */
- Tone.CtrlMarkov.prototype.dispose = function () {
- this.values = null;
- };
- return Tone.CtrlMarkov;
- });
- Module(function (Tone) {
-
- /**
- * @class Generate patterns from an array of values.
- * Has a number of arpeggiation and randomized
- * selection patterns.
- *
- * Mid *= 2*(1-width)
- * Side *= 2*width
- *
- *
- * @extends {Tone.MidSideEffect}
- * @constructor
- * @param {NormalRange|Object} [width] The stereo width. A width of 0 is mono and 1 is stereo. 0.5 is no change.
- */
- Tone.StereoWidener = function () {
- var options = Tone.defaults(arguments, ['width'], Tone.StereoWidener);
- Tone.MidSideEffect.call(this, options);
- /**
- * The width control. 0 = 100% mid. 1 = 100% side. 0.5 = no change.
- * @type {NormalRange}
- * @signal
- */
- this.width = new Tone.Signal(options.width, Tone.Type.NormalRange);
- /**
- * Mid multiplier
- * @type {Tone.Expr}
- * @private
- */
- this._midMult = new Tone.Expr('$0 * ($1 * (1 - $2))');
- /**
- * Side multiplier
- * @type {Tone.Expr}
- * @private
- */
- this._sideMult = new Tone.Expr('$0 * ($1 * $2)');
- /**
- * constant output of 2
- * @type {Tone}
- * @private
- */
- this._two = new Tone.Signal(2);
- //the mid chain
- this._two.connect(this._midMult, 0, 1);
- this.width.connect(this._midMult, 0, 2);
- //the side chain
- this._two.connect(this._sideMult, 0, 1);
- this.width.connect(this._sideMult, 0, 2);
- //connect it to the effect send/return
- this.midSend.chain(this._midMult, this.midReturn);
- this.sideSend.chain(this._sideMult, this.sideReturn);
- this._readOnly(['width']);
- };
- Tone.extend(Tone.StereoWidener, Tone.MidSideEffect);
- /**
- * the default values
- * @static
- * @type {Object}
- */
- Tone.StereoWidener.defaults = { 'width': 0.5 };
- /**
- * Clean up.
- * @returns {Tone.StereoWidener} this
- */
- Tone.StereoWidener.prototype.dispose = function () {
- Tone.MidSideEffect.prototype.dispose.call(this);
- this._writable(['width']);
- this.width.dispose();
- this.width = null;
- this._midMult.dispose();
- this._midMult = null;
- this._sideMult.dispose();
- this._sideMult = null;
- this._two.dispose();
- this._two = null;
- return this;
- };
- return Tone.StereoWidener;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Tremolo modulates the amplitude of an incoming signal using a Tone.LFO.
- * The type, frequency, and depth of the LFO is controllable.
- *
- * @extends {Tone.StereoEffect}
- * @constructor
- * @param {Frequency} [frequency] The rate of the effect.
- * @param {NormalRange} [depth] The depth of the effect.
- * @example
- * //create a tremolo and start it's LFO
- * var tremolo = new Tone.Tremolo(9, 0.75).toMaster().start();
- * //route an oscillator through the tremolo and start it
- * var oscillator = new Tone.Oscillator().connect(tremolo).start();
- */
- Tone.Tremolo = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'depth'
- ], Tone.Tremolo);
- Tone.StereoEffect.call(this, options);
- /**
- * The tremelo LFO in the left channel
- * @type {Tone.LFO}
- * @private
- */
- this._lfoL = new Tone.LFO({
- 'phase': options.spread,
- 'min': 1,
- 'max': 0
- });
- /**
- * The tremelo LFO in the left channel
- * @type {Tone.LFO}
- * @private
- */
- this._lfoR = new Tone.LFO({
- 'phase': options.spread,
- 'min': 1,
- 'max': 0
- });
- /**
- * Where the gain is multiplied
- * @type {Tone.Gain}
- * @private
- */
- this._amplitudeL = new Tone.Gain();
- /**
- * Where the gain is multiplied
- * @type {Tone.Gain}
- * @private
- */
- this._amplitudeR = new Tone.Gain();
- /**
- * The frequency of the tremolo.
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);
- /**
- * The depth of the effect. A depth of 0, has no effect
- * on the amplitude, and a depth of 1 makes the amplitude
- * modulate fully between 0 and 1.
- * @type {NormalRange}
- * @signal
- */
- this.depth = new Tone.Signal(options.depth, Tone.Type.NormalRange);
- this._readOnly([
- 'frequency',
- 'depth'
- ]);
- this.effectSendL.chain(this._amplitudeL, this.effectReturnL);
- this.effectSendR.chain(this._amplitudeR, this.effectReturnR);
- this._lfoL.connect(this._amplitudeL.gain);
- this._lfoR.connect(this._amplitudeR.gain);
- this.frequency.fan(this._lfoL.frequency, this._lfoR.frequency);
- this.depth.fan(this._lfoR.amplitude, this._lfoL.amplitude);
- this.type = options.type;
- this.spread = options.spread;
- };
- Tone.extend(Tone.Tremolo, Tone.StereoEffect);
- /**
- * @static
- * @const
- * @type {Object}
- */
- Tone.Tremolo.defaults = {
- 'frequency': 10,
- 'type': 'sine',
- 'depth': 0.5,
- 'spread': 180
- };
- /**
- * Start the tremolo.
- * @param {Time} [time=now] When the tremolo begins.
- * @returns {Tone.Tremolo} this
- */
- Tone.Tremolo.prototype.start = function (time) {
- this._lfoL.start(time);
- this._lfoR.start(time);
- return this;
- };
- /**
- * Stop the tremolo.
- * @param {Time} [time=now] When the tremolo stops.
- * @returns {Tone.Tremolo} this
- */
- Tone.Tremolo.prototype.stop = function (time) {
- this._lfoL.stop(time);
- this._lfoR.stop(time);
- return this;
- };
- /**
- * Sync the effect to the transport.
- * @param {Time} [delay=0] Delay time before starting the effect after the
- * Transport has started.
- * @returns {Tone.AutoFilter} this
- */
- Tone.Tremolo.prototype.sync = function (delay) {
- this._lfoL.sync(delay);
- this._lfoR.sync(delay);
- return this;
- };
- /**
- * Unsync the filter from the transport
- * @returns {Tone.Tremolo} this
- */
- Tone.Tremolo.prototype.unsync = function () {
- this._lfoL.unsync();
- this._lfoR.unsync();
- return this;
- };
- /**
- * The Tremolo's oscillator type.
- * @memberOf Tone.Tremolo#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.Tremolo.prototype, 'type', {
- get: function () {
- return this._lfoL.type;
- },
- set: function (type) {
- this._lfoL.type = type;
- this._lfoR.type = type;
- }
- });
- /**
- * Amount of stereo spread. When set to 0, both LFO's will be panned centrally.
- * When set to 180, LFO's will be panned hard left and right respectively.
- * @memberOf Tone.Tremolo#
- * @type {Degrees}
- * @name spread
- */
- Object.defineProperty(Tone.Tremolo.prototype, 'spread', {
- get: function () {
- return this._lfoR.phase - this._lfoL.phase; //180
- },
- set: function (spread) {
- this._lfoL.phase = 90 - spread / 2;
- this._lfoR.phase = spread / 2 + 90;
- }
- });
- /**
- * clean up
- * @returns {Tone.Tremolo} this
- */
- Tone.Tremolo.prototype.dispose = function () {
- Tone.StereoEffect.prototype.dispose.call(this);
- this._writable([
- 'frequency',
- 'depth'
- ]);
- this._lfoL.dispose();
- this._lfoL = null;
- this._lfoR.dispose();
- this._lfoR = null;
- this._amplitudeL.dispose();
- this._amplitudeL = null;
- this._amplitudeR.dispose();
- this._amplitudeR = null;
- this.frequency = null;
- this.depth = null;
- return this;
- };
- return Tone.Tremolo;
- });
- Module(function (Tone) {
-
- /**
- * @class A Vibrato effect composed of a Tone.Delay and a Tone.LFO. The LFO
- * modulates the delayTime of the delay, causing the pitch to rise
- * and fall.
- * @extends {Tone.Effect}
- * @param {Frequency} frequency The frequency of the vibrato.
- * @param {NormalRange} depth The amount the pitch is modulated.
- */
- Tone.Vibrato = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'depth'
- ], Tone.Vibrato);
- Tone.Effect.call(this, options);
- /**
- * The delay node used for the vibrato effect
- * @type {Tone.Delay}
- * @private
- */
- this._delayNode = new Tone.Delay(0, options.maxDelay);
- /**
- * The LFO used to control the vibrato
- * @type {Tone.LFO}
- * @private
- */
- this._lfo = new Tone.LFO({
- 'type': options.type,
- 'min': 0,
- 'max': options.maxDelay,
- 'frequency': options.frequency,
- 'phase': -90 //offse the phase so the resting position is in the center
- }).start().connect(this._delayNode.delayTime);
- /**
- * The frequency of the vibrato
- * @type {Frequency}
- * @signal
- */
- this.frequency = this._lfo.frequency;
- /**
- * The depth of the vibrato.
- * @type {NormalRange}
- * @signal
- */
- this.depth = this._lfo.amplitude;
- this.depth.value = options.depth;
- this._readOnly([
- 'frequency',
- 'depth'
- ]);
- this.effectSend.chain(this._delayNode, this.effectReturn);
- };
- Tone.extend(Tone.Vibrato, Tone.Effect);
- /**
- * The defaults
- * @type {Object}
- * @const
- */
- Tone.Vibrato.defaults = {
- 'maxDelay': 0.005,
- 'frequency': 5,
- 'depth': 0.1,
- 'type': 'sine'
- };
- /**
- * Type of oscillator attached to the Vibrato.
- * @memberOf Tone.Vibrato#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.Vibrato.prototype, 'type', {
- get: function () {
- return this._lfo.type;
- },
- set: function (type) {
- this._lfo.type = type;
- }
- });
- /**
- * Clean up.
- * @returns {Tone.Vibrato} this
- */
- Tone.Vibrato.prototype.dispose = function () {
- Tone.Effect.prototype.dispose.call(this);
- this._delayNode.dispose();
- this._delayNode = null;
- this._lfo.dispose();
- this._lfo = null;
- this._writable([
- 'frequency',
- 'depth'
- ]);
- this.frequency = null;
- this.depth = null;
- };
- return Tone.Vibrato;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Event abstracts away Tone.Transport.schedule and provides a schedulable
- * callback for a single or repeatable events along the timeline.
- *
- * @extends {Tone}
- * @param {function} callback The callback to invoke at the time.
- * @param {*} value The value or values which should be passed to
- * the callback function on invocation.
- * @example
- * var chord = new Tone.Event(function(time, chord){
- * //the chord as well as the exact time of the event
- * //are passed in as arguments to the callback function
- * }, ["D4", "E4", "F4"]);
- * //start the chord at the beginning of the transport timeline
- * chord.start();
- * //loop it every measure for 8 measures
- * chord.loop = 8;
- * chord.loopEnd = "1m";
- */
- Tone.Event = function () {
- var options = Tone.defaults(arguments, [
- 'callback',
- 'value'
- ], Tone.Event);
- Tone.call(this);
- /**
- * Loop value
- * @type {Boolean|Positive}
- * @private
- */
- this._loop = options.loop;
- /**
- * The callback to invoke.
- * @type {Function}
- */
- this.callback = options.callback;
- /**
- * The value which is passed to the
- * callback function.
- * @type {*}
- * @private
- */
- this.value = options.value;
- /**
- * When the note is scheduled to start.
- * @type {Number}
- * @private
- */
- this._loopStart = this.toTicks(options.loopStart);
- /**
- * When the note is scheduled to start.
- * @type {Number}
- * @private
- */
- this._loopEnd = this.toTicks(options.loopEnd);
- /**
- * Tracks the scheduled events
- * @type {Tone.TimelineState}
- * @private
- */
- this._state = new Tone.TimelineState(Tone.State.Stopped);
- /**
- * The playback speed of the note. A speed of 1
- * is no change.
- * @private
- * @type {Positive}
- */
- this._playbackRate = 1;
- /**
- * A delay time from when the event is scheduled to start
- * @type {Ticks}
- * @private
- */
- this._startOffset = 0;
- /**
- * private holder of probability value
- * @type {NormalRange}
- * @private
- */
- this._probability = options.probability;
- /**
- * the amount of variation from the
- * given time.
- * @type {Boolean|Time}
- * @private
- */
- this._humanize = options.humanize;
- /**
- * If mute is true, the callback won't be
- * invoked.
- * @type {Boolean}
- */
- this.mute = options.mute;
- //set the initial values
- this.playbackRate = options.playbackRate;
- };
- Tone.extend(Tone.Event);
- /**
- * The default values
- * @type {Object}
- * @const
- */
- Tone.Event.defaults = {
- 'callback': Tone.noOp,
- 'loop': false,
- 'loopEnd': '1m',
- 'loopStart': 0,
- 'playbackRate': 1,
- 'value': null,
- 'probability': 1,
- 'mute': false,
- 'humanize': false
- };
- /**
- * Reschedule all of the events along the timeline
- * with the updated values.
- * @param {Time} after Only reschedules events after the given time.
- * @return {Tone.Event} this
- * @private
- */
- Tone.Event.prototype._rescheduleEvents = function (after) {
- //if no argument is given, schedules all of the events
- after = Tone.defaultArg(after, -1);
- this._state.forEachFrom(after, function (event) {
- var duration;
- if (event.state === Tone.State.Started) {
- if (!Tone.isUndef(event.id)) {
- Tone.Transport.clear(event.id);
- }
- var startTick = event.time + Math.round(this.startOffset / this._playbackRate);
- if (this._loop) {
- duration = Infinity;
- if (Tone.isNumber(this._loop)) {
- duration = this._loop * this._getLoopDuration();
- }
- var nextEvent = this._state.getAfter(startTick);
- if (nextEvent !== null) {
- duration = Math.min(duration, nextEvent.time - startTick);
- }
- if (duration !== Infinity) {
- //schedule a stop since it's finite duration
- this._state.setStateAtTime(Tone.State.Stopped, startTick + duration + 1);
- duration = Tone.Time(duration, 'i');
- }
- var interval = Tone.Time(this._getLoopDuration(), 'i');
- event.id = Tone.Transport.scheduleRepeat(this._tick.bind(this), interval, Tone.TransportTime(startTick, 'i'), duration);
- } else {
- event.id = Tone.Transport.schedule(this._tick.bind(this), startTick + 'i');
- }
- }
- }.bind(this));
- return this;
- };
- /**
- * Returns the playback state of the note, either "started" or "stopped".
- * @type {String}
- * @readOnly
- * @memberOf Tone.Event#
- * @name state
- */
- Object.defineProperty(Tone.Event.prototype, 'state', {
- get: function () {
- return this._state.getValueAtTime(Tone.Transport.ticks);
- }
- });
- /**
- * The start from the scheduled start time
- * @type {Ticks}
- * @memberOf Tone.Event#
- * @name startOffset
- * @private
- */
- Object.defineProperty(Tone.Event.prototype, 'startOffset', {
- get: function () {
- return this._startOffset;
- },
- set: function (offset) {
- this._startOffset = offset;
- }
- });
- /**
- * The probability of the notes being triggered.
- * @memberOf Tone.Event#
- * @type {NormalRange}
- * @name probability
- */
- Object.defineProperty(Tone.Event.prototype, 'probability', {
- get: function () {
- return this._probability;
- },
- set: function (prob) {
- this._probability = prob;
- }
- });
- /**
- * If set to true, will apply small random variation
- * to the callback time. If the value is given as a time, it will randomize
- * by that amount.
- * @example
- * event.humanize = true;
- * @type {Boolean|Time}
- * @name humanize
- */
- Object.defineProperty(Tone.Event.prototype, 'humanize', {
- get: function () {
- return this._humanize;
- },
- set: function (variation) {
- this._humanize = variation;
- }
- });
- /**
- * Start the note at the given time.
- * @param {TimelinePosition} time When the note should start.
- * @return {Tone.Event} this
- */
- Tone.Event.prototype.start = function (time) {
- time = this.toTicks(time);
- if (this._state.getValueAtTime(time) === Tone.State.Stopped) {
- this._state.add({
- 'state': Tone.State.Started,
- 'time': time,
- 'id': undefined
- });
- this._rescheduleEvents(time);
- }
- return this;
- };
- /**
- * Stop the Event at the given time.
- * @param {TimelinePosition} time When the note should stop.
- * @return {Tone.Event} this
- */
- Tone.Event.prototype.stop = function (time) {
- this.cancel(time);
- time = this.toTicks(time);
- if (this._state.getValueAtTime(time) === Tone.State.Started) {
- this._state.setStateAtTime(Tone.State.Stopped, time);
- var previousEvent = this._state.getBefore(time);
- var reschedulTime = time;
- if (previousEvent !== null) {
- reschedulTime = previousEvent.time;
- }
- this._rescheduleEvents(reschedulTime);
- }
- return this;
- };
- /**
- * Cancel all scheduled events greater than or equal to the given time
- * @param {TimelinePosition} [time=0] The time after which events will be cancel.
- * @return {Tone.Event} this
- */
- Tone.Event.prototype.cancel = function (time) {
- time = Tone.defaultArg(time, -Infinity);
- time = this.toTicks(time);
- this._state.forEachFrom(time, function (event) {
- Tone.Transport.clear(event.id);
- });
- this._state.cancel(time);
- return this;
- };
- /**
- * The callback function invoker. Also
- * checks if the Event is done playing
- * @param {Number} time The time of the event in seconds
- * @private
- */
- Tone.Event.prototype._tick = function (time) {
- if (!this.mute && this._state.getValueAtTime(Tone.Transport.ticks) === Tone.State.Started) {
- if (this.probability < 1 && Math.random() > this.probability) {
- return;
- }
- if (this.humanize) {
- var variation = 0.02;
- if (!Tone.isBoolean(this.humanize)) {
- variation = this.toSeconds(this.humanize);
- }
- time += (Math.random() * 2 - 1) * variation;
- }
- this.callback(time, this.value);
- }
- };
- /**
- * Get the duration of the loop.
- * @return {Ticks}
- * @private
- */
- Tone.Event.prototype._getLoopDuration = function () {
- return Math.round((this._loopEnd - this._loopStart) / this._playbackRate);
- };
- /**
- * If the note should loop or not
- * between Tone.Event.loopStart and
- * Tone.Event.loopEnd. An integer
- * value corresponds to the number of
- * loops the Event does after it starts.
- * @memberOf Tone.Event#
- * @type {Boolean|Positive}
- * @name loop
- */
- Object.defineProperty(Tone.Event.prototype, 'loop', {
- get: function () {
- return this._loop;
- },
- set: function (loop) {
- this._loop = loop;
- this._rescheduleEvents();
- }
- });
- /**
- * The playback rate of the note. Defaults to 1.
- * @memberOf Tone.Event#
- * @type {Positive}
- * @name playbackRate
- * @example
- * note.loop = true;
- * //repeat the note twice as fast
- * note.playbackRate = 2;
- */
- Object.defineProperty(Tone.Event.prototype, 'playbackRate', {
- get: function () {
- return this._playbackRate;
- },
- set: function (rate) {
- this._playbackRate = rate;
- this._rescheduleEvents();
- }
- });
- /**
- * The loopEnd point is the time the event will loop
- * if Tone.Event.loop is true.
- * @memberOf Tone.Event#
- * @type {TransportTime}
- * @name loopEnd
- */
- Object.defineProperty(Tone.Event.prototype, 'loopEnd', {
- get: function () {
- return Tone.TransportTime(this._loopEnd, 'i').toNotation();
- },
- set: function (loopEnd) {
- this._loopEnd = this.toTicks(loopEnd);
- if (this._loop) {
- this._rescheduleEvents();
- }
- }
- });
- /**
- * The time when the loop should start.
- * @memberOf Tone.Event#
- * @type {TransportTime}
- * @name loopStart
- */
- Object.defineProperty(Tone.Event.prototype, 'loopStart', {
- get: function () {
- return Tone.TransportTime(this._loopStart, 'i').toNotation();
- },
- set: function (loopStart) {
- this._loopStart = this.toTicks(loopStart);
- if (this._loop) {
- this._rescheduleEvents();
- }
- }
- });
- /**
- * The current progress of the loop interval.
- * Returns 0 if the event is not started yet or
- * it is not set to loop.
- * @memberOf Tone.Event#
- * @type {NormalRange}
- * @name progress
- * @readOnly
- */
- Object.defineProperty(Tone.Event.prototype, 'progress', {
- get: function () {
- if (this._loop) {
- var ticks = Tone.Transport.ticks;
- var lastEvent = this._state.get(ticks);
- if (lastEvent !== null && lastEvent.state === Tone.State.Started) {
- var loopDuration = this._getLoopDuration();
- var progress = (ticks - lastEvent.time) % loopDuration;
- return progress / loopDuration;
- } else {
- return 0;
- }
- } else {
- return 0;
- }
- }
- });
- /**
- * Clean up
- * @return {Tone.Event} this
- */
- Tone.Event.prototype.dispose = function () {
- this.cancel();
- this._state.dispose();
- this._state = null;
- this.callback = null;
- this.value = null;
- };
- return Tone.Event;
- });
- Module(function (Tone) {
- /**
- * @class Tone.Loop creates a looped callback at the
- * specified interval. The callback can be
- * started, stopped and scheduled along
- * the Transport's timeline.
- * @example
- * var loop = new Tone.Loop(function(time){
- * //triggered every eighth note.
- * console.log(time);
- * }, "8n").start(0);
- * Tone.Transport.start();
- * @extends {Tone}
- * @param {Function} callback The callback to invoke with the event.
- * @param {Time} interval The time between successive callback calls.
- */
- Tone.Loop = function () {
- var options = Tone.defaults(arguments, [
- 'callback',
- 'interval'
- ], Tone.Loop);
- Tone.call(this);
- /**
- * The event which produces the callbacks
- */
- this._event = new Tone.Event({
- 'callback': this._tick.bind(this),
- 'loop': true,
- 'loopEnd': options.interval,
- 'playbackRate': options.playbackRate,
- 'probability': options.probability
- });
- /**
- * The callback to invoke with the next event in the pattern
- * @type {Function}
- */
- this.callback = options.callback;
- //set the iterations
- this.iterations = options.iterations;
- };
- Tone.extend(Tone.Loop);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Loop.defaults = {
- 'interval': '4n',
- 'callback': Tone.noOp,
- 'playbackRate': 1,
- 'iterations': Infinity,
- 'probability': true,
- 'mute': false
- };
- /**
- * Start the loop at the specified time along the Transport's
- * timeline.
- * @param {TimelinePosition=} time When to start the Loop.
- * @return {Tone.Loop} this
- */
- Tone.Loop.prototype.start = function (time) {
- this._event.start(time);
- return this;
- };
- /**
- * Stop the loop at the given time.
- * @param {TimelinePosition=} time When to stop the Arpeggio
- * @return {Tone.Loop} this
- */
- Tone.Loop.prototype.stop = function (time) {
- this._event.stop(time);
- return this;
- };
- /**
- * Cancel all scheduled events greater than or equal to the given time
- * @param {TimelinePosition} [time=0] The time after which events will be cancel.
- * @return {Tone.Loop} this
- */
- Tone.Loop.prototype.cancel = function (time) {
- this._event.cancel(time);
- return this;
- };
- /**
- * Internal function called when the notes should be called
- * @param {Number} time The time the event occurs
- * @private
- */
- Tone.Loop.prototype._tick = function (time) {
- this.callback(time);
- };
- /**
- * The state of the Loop, either started or stopped.
- * @memberOf Tone.Loop#
- * @type {String}
- * @name state
- * @readOnly
- */
- Object.defineProperty(Tone.Loop.prototype, 'state', {
- get: function () {
- return this._event.state;
- }
- });
- /**
- * The progress of the loop as a value between 0-1. 0, when
- * the loop is stopped or done iterating.
- * @memberOf Tone.Loop#
- * @type {NormalRange}
- * @name progress
- * @readOnly
- */
- Object.defineProperty(Tone.Loop.prototype, 'progress', {
- get: function () {
- return this._event.progress;
- }
- });
- /**
- * The time between successive callbacks.
- * @example
- * loop.interval = "8n"; //loop every 8n
- * @memberOf Tone.Loop#
- * @type {Time}
- * @name interval
- */
- Object.defineProperty(Tone.Loop.prototype, 'interval', {
- get: function () {
- return this._event.loopEnd;
- },
- set: function (interval) {
- this._event.loopEnd = interval;
- }
- });
- /**
- * The playback rate of the loop. The normal playback rate is 1 (no change).
- * A `playbackRate` of 2 would be twice as fast.
- * @memberOf Tone.Loop#
- * @type {Time}
- * @name playbackRate
- */
- Object.defineProperty(Tone.Loop.prototype, 'playbackRate', {
- get: function () {
- return this._event.playbackRate;
- },
- set: function (rate) {
- this._event.playbackRate = rate;
- }
- });
- /**
- * Random variation +/-0.01s to the scheduled time.
- * Or give it a time value which it will randomize by.
- * @type {Boolean|Time}
- * @memberOf Tone.Loop#
- * @name humanize
- */
- Object.defineProperty(Tone.Loop.prototype, 'humanize', {
- get: function () {
- return this._event.humanize;
- },
- set: function (variation) {
- this._event.humanize = variation;
- }
- });
- /**
- * The probably of the callback being invoked.
- * @memberOf Tone.Loop#
- * @type {NormalRange}
- * @name probability
- */
- Object.defineProperty(Tone.Loop.prototype, 'probability', {
- get: function () {
- return this._event.probability;
- },
- set: function (prob) {
- this._event.probability = prob;
- }
- });
- /**
- * Muting the Loop means that no callbacks are invoked.
- * @memberOf Tone.Loop#
- * @type {Boolean}
- * @name mute
- */
- Object.defineProperty(Tone.Loop.prototype, 'mute', {
- get: function () {
- return this._event.mute;
- },
- set: function (mute) {
- this._event.mute = mute;
- }
- });
- /**
- * The number of iterations of the loop. The default
- * value is Infinity (loop forever).
- * @memberOf Tone.Loop#
- * @type {Positive}
- * @name iterations
- */
- Object.defineProperty(Tone.Loop.prototype, 'iterations', {
- get: function () {
- if (this._event.loop === true) {
- return Infinity;
- } else {
- return this._event.loop;
- }
- },
- set: function (iters) {
- if (iters === Infinity) {
- this._event.loop = true;
- } else {
- this._event.loop = iters;
- }
- }
- });
- /**
- * Clean up
- * @return {Tone.Loop} this
- */
- Tone.Loop.prototype.dispose = function () {
- this._event.dispose();
- this._event = null;
- this.callback = null;
- };
- return Tone.Loop;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Part is a collection Tone.Events which can be
- * started/stopped and looped as a single unit.
- *
- * @extends {Tone.Event}
- * @param {Function} callback The callback to invoke on each event
- * @param {Array} events the array of events
- * @example
- * var part = new Tone.Part(function(time, note){
- * //the notes given as the second element in the array
- * //will be passed in as the second argument
- * synth.triggerAttackRelease(note, "8n", time);
- * }, [[0, "C2"], ["0:2", "C3"], ["0:3:2", "G2"]]);
- * @example
- * //use an array of objects as long as the object has a "time" attribute
- * var part = new Tone.Part(function(time, value){
- * //the value is an object which contains both the note and the velocity
- * synth.triggerAttackRelease(value.note, "8n", time, value.velocity);
- * }, [{"time" : 0, "note" : "C3", "velocity": 0.9},
- * {"time" : "0:2", "note" : "C4", "velocity": 0.5}
- * ]).start(0);
- */
- Tone.Part = function () {
- var options = Tone.defaults(arguments, [
- 'callback',
- 'events'
- ], Tone.Part);
- Tone.Event.call(this, options);
- /**
- * An array of Objects.
- * @type {Array}
- * @private
- */
- this._events = [];
- //add the events
- for (var i = 0; i < options.events.length; i++) {
- if (Array.isArray(options.events[i])) {
- this.add(options.events[i][0], options.events[i][1]);
- } else {
- this.add(options.events[i]);
- }
- }
- };
- Tone.extend(Tone.Part, Tone.Event);
- /**
- * The default values
- * @type {Object}
- * @const
- */
- Tone.Part.defaults = {
- 'callback': Tone.noOp,
- 'loop': false,
- 'loopEnd': '1m',
- 'loopStart': 0,
- 'playbackRate': 1,
- 'probability': 1,
- 'humanize': false,
- 'mute': false,
- 'events': []
- };
- /**
- * Start the part at the given time.
- * @param {TransportTime} time When to start the part.
- * @param {Time=} offset The offset from the start of the part
- * to begin playing at.
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.start = function (time, offset) {
- var ticks = this.toTicks(time);
- if (this._state.getValueAtTime(ticks) !== Tone.State.Started) {
- if (this._loop) {
- offset = Tone.defaultArg(offset, this._loopStart);
- } else {
- offset = Tone.defaultArg(offset, 0);
- }
- offset = this.toTicks(offset);
- this._state.add({
- 'state': Tone.State.Started,
- 'time': ticks,
- 'offset': offset
- });
- this._forEach(function (event) {
- this._startNote(event, ticks, offset);
- });
- }
- return this;
- };
- /**
- * Start the event in the given event at the correct time given
- * the ticks and offset and looping.
- * @param {Tone.Event} event
- * @param {Ticks} ticks
- * @param {Ticks} offset
- * @private
- */
- Tone.Part.prototype._startNote = function (event, ticks, offset) {
- ticks -= offset;
- if (this._loop) {
- if (event.startOffset >= this._loopStart && event.startOffset < this._loopEnd) {
- if (event.startOffset < offset) {
- //start it on the next loop
- ticks += this._getLoopDuration();
- }
- event.start(Tone.TransportTime(ticks, 'i'));
- } else if (event.startOffset < this._loopStart && event.startOffset >= offset) {
- event.loop = false;
- event.start(Tone.TransportTime(ticks, 'i'));
- }
- } else {
- if (event.startOffset >= offset) {
- event.start(Tone.TransportTime(ticks, 'i'));
- }
- }
- };
- /**
- * The start from the scheduled start time
- * @type {Ticks}
- * @memberOf Tone.Part#
- * @name startOffset
- * @private
- */
- Object.defineProperty(Tone.Part.prototype, 'startOffset', {
- get: function () {
- return this._startOffset;
- },
- set: function (offset) {
- this._startOffset = offset;
- this._forEach(function (event) {
- event.startOffset += this._startOffset;
- });
- }
- });
- /**
- * Stop the part at the given time.
- * @param {TimelinePosition} time When to stop the part.
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.stop = function (time) {
- var ticks = this.toTicks(time);
- this._state.cancel(ticks);
- this._state.setStateAtTime(Tone.State.Stopped, ticks);
- this._forEach(function (event) {
- event.stop(time);
- });
- return this;
- };
- /**
- * Get/Set an Event's value at the given time.
- * If a value is passed in and no event exists at
- * the given time, one will be created with that value.
- * If two events are at the same time, the first one will
- * be returned.
- * @example
- * part.at("1m"); //returns the part at the first measure
- *
- * part.at("2m", "C2"); //set the value at "2m" to C2.
- * //if an event didn't exist at that time, it will be created.
- * @param {TransportTime} time The time of the event to get or set.
- * @param {*=} value If a value is passed in, the value of the
- * event at the given time will be set to it.
- * @return {Tone.Event} the event at the time
- */
- Tone.Part.prototype.at = function (time, value) {
- time = Tone.TransportTime(time);
- var tickTime = Tone.Time(1, 'i').toSeconds();
- for (var i = 0; i < this._events.length; i++) {
- var event = this._events[i];
- if (Math.abs(time.toTicks() - event.startOffset) < tickTime) {
- if (!Tone.isUndef(value)) {
- event.value = value;
- }
- return event;
- }
- }
- //if there was no event at that time, create one
- if (!Tone.isUndef(value)) {
- this.add(time, value);
- //return the new event
- return this._events[this._events.length - 1];
- } else {
- return null;
- }
- };
- /**
- * Add a an event to the part.
- * @param {Time} time The time the note should start.
- * If an object is passed in, it should
- * have a 'time' attribute and the rest
- * of the object will be used as the 'value'.
- * @param {Tone.Event|*} value
- * @returns {Tone.Part} this
- * @example
- * part.add("1m", "C#+11");
- */
- Tone.Part.prototype.add = function (time, value) {
- //extract the parameters
- if (time.hasOwnProperty('time')) {
- value = time;
- time = value.time;
- }
- time = this.toTicks(time);
- var event;
- if (value instanceof Tone.Event) {
- event = value;
- event.callback = this._tick.bind(this);
- } else {
- event = new Tone.Event({
- 'callback': this._tick.bind(this),
- 'value': value
- });
- }
- //the start offset
- event.startOffset = time;
- //initialize the values
- event.set({
- 'loopEnd': this.loopEnd,
- 'loopStart': this.loopStart,
- 'loop': this.loop,
- 'humanize': this.humanize,
- 'playbackRate': this.playbackRate,
- 'probability': this.probability
- });
- this._events.push(event);
- //start the note if it should be played right now
- this._restartEvent(event);
- return this;
- };
- /**
- * Restart the given event
- * @param {Tone.Event} event
- * @private
- */
- Tone.Part.prototype._restartEvent = function (event) {
- this._state.forEach(function (stateEvent) {
- if (stateEvent.state === Tone.State.Started) {
- this._startNote(event, stateEvent.time, stateEvent.offset);
- } else {
- //stop the note
- event.stop(Tone.TransportTime(stateEvent.time, 'i'));
- }
- }.bind(this));
- };
- /**
- * Remove an event from the part. Will recursively iterate
- * into nested parts to find the event.
- * @param {Time} time The time of the event
- * @param {*} value Optionally select only a specific event value
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.remove = function (time, value) {
- //extract the parameters
- if (time.hasOwnProperty('time')) {
- value = time;
- time = value.time;
- }
- time = this.toTicks(time);
- for (var i = this._events.length - 1; i >= 0; i--) {
- var event = this._events[i];
- if (event instanceof Tone.Part) {
- event.remove(time, value);
- } else {
- if (event.startOffset === time) {
- if (Tone.isUndef(value) || !Tone.isUndef(value) && event.value === value) {
- this._events.splice(i, 1);
- event.dispose();
- }
- }
- }
- }
- return this;
- };
- /**
- * Remove all of the notes from the group.
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.removeAll = function () {
- this._forEach(function (event) {
- event.dispose();
- });
- this._events = [];
- return this;
- };
- /**
- * Cancel scheduled state change events: i.e. "start" and "stop".
- * @param {TimelinePosition} after The time after which to cancel the scheduled events.
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.cancel = function (after) {
- this._forEach(function (event) {
- event.cancel(after);
- });
- this._state.cancel(this.toTicks(after));
- return this;
- };
- /**
- * Iterate over all of the events
- * @param {Function} callback
- * @param {Object} ctx The context
- * @private
- */
- Tone.Part.prototype._forEach = function (callback, ctx) {
- if (this._events) {
- ctx = Tone.defaultArg(ctx, this);
- for (var i = this._events.length - 1; i >= 0; i--) {
- var e = this._events[i];
- if (e instanceof Tone.Part) {
- e._forEach(callback, ctx);
- } else {
- callback.call(ctx, e);
- }
- }
- }
- return this;
- };
- /**
- * Set the attribute of all of the events
- * @param {String} attr the attribute to set
- * @param {*} value The value to set it to
- * @private
- */
- Tone.Part.prototype._setAll = function (attr, value) {
- this._forEach(function (event) {
- event[attr] = value;
- });
- };
- /**
- * Internal tick method
- * @param {Number} time The time of the event in seconds
- * @private
- */
- Tone.Part.prototype._tick = function (time, value) {
- if (!this.mute) {
- this.callback(time, value);
- }
- };
- /**
- * Determine if the event should be currently looping
- * given the loop boundries of this Part.
- * @param {Tone.Event} event The event to test
- * @private
- */
- Tone.Part.prototype._testLoopBoundries = function (event) {
- if (event.startOffset < this._loopStart || event.startOffset >= this._loopEnd) {
- event.cancel(0);
- } else {
- //reschedule it if it's stopped
- if (event.state === Tone.State.Stopped) {
- this._restartEvent(event);
- }
- }
- };
- /**
- * The probability of the notes being triggered.
- * @memberOf Tone.Part#
- * @type {NormalRange}
- * @name probability
- */
- Object.defineProperty(Tone.Part.prototype, 'probability', {
- get: function () {
- return this._probability;
- },
- set: function (prob) {
- this._probability = prob;
- this._setAll('probability', prob);
- }
- });
- /**
- * If set to true, will apply small random variation
- * to the callback time. If the value is given as a time, it will randomize
- * by that amount.
- * @example
- * event.humanize = true;
- * @type {Boolean|Time}
- * @name humanize
- */
- Object.defineProperty(Tone.Part.prototype, 'humanize', {
- get: function () {
- return this._humanize;
- },
- set: function (variation) {
- this._humanize = variation;
- this._setAll('humanize', variation);
- }
- });
- /**
- * If the part should loop or not
- * between Tone.Part.loopStart and
- * Tone.Part.loopEnd. An integer
- * value corresponds to the number of
- * loops the Part does after it starts.
- * @memberOf Tone.Part#
- * @type {Boolean|Positive}
- * @name loop
- * @example
- * //loop the part 8 times
- * part.loop = 8;
- */
- Object.defineProperty(Tone.Part.prototype, 'loop', {
- get: function () {
- return this._loop;
- },
- set: function (loop) {
- this._loop = loop;
- this._forEach(function (event) {
- event._loopStart = this._loopStart;
- event._loopEnd = this._loopEnd;
- event.loop = loop;
- this._testLoopBoundries(event);
- });
- }
- });
- /**
- * The loopEnd point determines when it will
- * loop if Tone.Part.loop is true.
- * @memberOf Tone.Part#
- * @type {TransportTime}
- * @name loopEnd
- */
- Object.defineProperty(Tone.Part.prototype, 'loopEnd', {
- get: function () {
- return Tone.TransportTime(this._loopEnd, 'i').toNotation();
- },
- set: function (loopEnd) {
- this._loopEnd = this.toTicks(loopEnd);
- if (this._loop) {
- this._forEach(function (event) {
- event.loopEnd = loopEnd;
- this._testLoopBoundries(event);
- });
- }
- }
- });
- /**
- * The loopStart point determines when it will
- * loop if Tone.Part.loop is true.
- * @memberOf Tone.Part#
- * @type {TransportTime}
- * @name loopStart
- */
- Object.defineProperty(Tone.Part.prototype, 'loopStart', {
- get: function () {
- return Tone.TransportTime(this._loopStart, 'i').toNotation();
- },
- set: function (loopStart) {
- this._loopStart = this.toTicks(loopStart);
- if (this._loop) {
- this._forEach(function (event) {
- event.loopStart = this.loopStart;
- this._testLoopBoundries(event);
- });
- }
- }
- });
- /**
- * The playback rate of the part
- * @memberOf Tone.Part#
- * @type {Positive}
- * @name playbackRate
- */
- Object.defineProperty(Tone.Part.prototype, 'playbackRate', {
- get: function () {
- return this._playbackRate;
- },
- set: function (rate) {
- this._playbackRate = rate;
- this._setAll('playbackRate', rate);
- }
- });
- /**
- * The number of scheduled notes in the part.
- * @memberOf Tone.Part#
- * @type {Positive}
- * @name length
- * @readOnly
- */
- Object.defineProperty(Tone.Part.prototype, 'length', {
- get: function () {
- return this._events.length;
- }
- });
- /**
- * Clean up
- * @return {Tone.Part} this
- */
- Tone.Part.prototype.dispose = function () {
- this.removeAll();
- this._state.dispose();
- this._state = null;
- this.callback = null;
- this._events = null;
- return this;
- };
- return Tone.Part;
- });
- Module(function (Tone) {
- /**
- * @class Tone.Pattern arpeggiates between the given notes
- * in a number of patterns. See Tone.CtrlPattern for
- * a full list of patterns.
- * @example
- * var pattern = new Tone.Pattern(function(time, note){
- * //the order of the notes passed in depends on the pattern
- * }, ["C2", "D4", "E5", "A6"], "upDown");
- * @extends {Tone.Loop}
- * @param {Function} callback The callback to invoke with the
- * event.
- * @param {Array} values The values to arpeggiate over.
- */
- Tone.Pattern = function () {
- var options = Tone.defaults(arguments, [
- 'callback',
- 'values',
- 'pattern'
- ], Tone.Pattern);
- Tone.Loop.call(this, options);
- /**
- * The pattern manager
- * @type {Tone.CtrlPattern}
- * @private
- */
- this._pattern = new Tone.CtrlPattern({
- 'values': options.values,
- 'type': options.pattern,
- 'index': options.index
- });
- };
- Tone.extend(Tone.Pattern, Tone.Loop);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Pattern.defaults = {
- 'pattern': Tone.CtrlPattern.Type.Up,
- 'callback': Tone.noOp,
- 'values': []
- };
- /**
- * Internal function called when the notes should be called
- * @param {Number} time The time the event occurs
- * @private
- */
- Tone.Pattern.prototype._tick = function (time) {
- this.callback(time, this._pattern.value);
- this._pattern.next();
- };
- /**
- * The current index in the values array.
- * @memberOf Tone.Pattern#
- * @type {Positive}
- * @name index
- */
- Object.defineProperty(Tone.Pattern.prototype, 'index', {
- get: function () {
- return this._pattern.index;
- },
- set: function (i) {
- this._pattern.index = i;
- }
- });
- /**
- * The array of events.
- * @memberOf Tone.Pattern#
- * @type {Array}
- * @name values
- */
- Object.defineProperty(Tone.Pattern.prototype, 'values', {
- get: function () {
- return this._pattern.values;
- },
- set: function (vals) {
- this._pattern.values = vals;
- }
- });
- /**
- * The current value of the pattern.
- * @memberOf Tone.Pattern#
- * @type {*}
- * @name value
- * @readOnly
- */
- Object.defineProperty(Tone.Pattern.prototype, 'value', {
- get: function () {
- return this._pattern.value;
- }
- });
- /**
- * The pattern type. See Tone.CtrlPattern for the full list of patterns.
- * @memberOf Tone.Pattern#
- * @type {String}
- * @name pattern
- */
- Object.defineProperty(Tone.Pattern.prototype, 'pattern', {
- get: function () {
- return this._pattern.type;
- },
- set: function (pattern) {
- this._pattern.type = pattern;
- }
- });
- /**
- * Clean up
- * @return {Tone.Pattern} this
- */
- Tone.Pattern.prototype.dispose = function () {
- Tone.Loop.prototype.dispose.call(this);
- this._pattern.dispose();
- this._pattern = null;
- };
- return Tone.Pattern;
- });
- Module(function (Tone) {
-
- /**
- * @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.isUndef(events)) {
- this._loopEnd = events.length * this._subdivision;
- }
- //defaults to looping
- this._loop = true;
- //add all of the events
- if (!Tone.isUndef(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.Time(this._subdivision, 'i').toNotation();
- }
- });
- /**
- * 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.Time(subSubdivision, 'i'));
- }
- 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.TransportTime(index * this._subdivision + this.startOffset, 'i');
- }
- };
- /**
- * Clean up.
- * @return {Tone.Sequence} this
- */
- Tone.Sequence.prototype.dispose = function () {
- Tone.Part.prototype.dispose.call(this);
- return this;
- };
- return Tone.Sequence;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.PulseOscillator is a pulse oscillator with control over pulse width,
- * also known as the duty cycle. At 50% duty cycle (width = 0.5) the wave is
- * a square and only odd-numbered harmonics are present. At all other widths
- * even-numbered harmonics are present. Read more
- * [here](https://wigglewave.wordpress.com/2014/08/16/pulse-waveforms-and-harmonics/).
- *
- * @constructor
- * @extends {Tone.Source}
- * @param {Frequency} [frequency] The frequency of the oscillator
- * @param {NormalRange} [width] The width of the pulse
- * @example
- * var pulse = new Tone.PulseOscillator("E5", 0.4).toMaster().start();
- */
- Tone.PulseOscillator = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'width'
- ], Tone.Oscillator);
- Tone.Source.call(this, options);
- /**
- * The width of the pulse.
- * @type {NormalRange}
- * @signal
- */
- this.width = new Tone.Signal(options.width, Tone.Type.NormalRange);
- /**
- * gate the width amount
- * @type {Tone.Gain}
- * @private
- */
- this._widthGate = new Tone.Gain();
- /**
- * the sawtooth oscillator
- * @type {Tone.Oscillator}
- * @private
- */
- this._sawtooth = new Tone.Oscillator({
- frequency: options.frequency,
- detune: options.detune,
- type: 'sawtooth',
- phase: options.phase
- });
- /**
- * The frequency control.
- * @type {Frequency}
- * @signal
- */
- this.frequency = this._sawtooth.frequency;
- /**
- * The detune in cents.
- * @type {Cents}
- * @signal
- */
- this.detune = this._sawtooth.detune;
- /**
- * Threshold the signal to turn it into a square
- * @type {Tone.WaveShaper}
- * @private
- */
- this._thresh = new Tone.WaveShaper(function (val) {
- if (val < 0) {
- return -1;
- } else {
- return 1;
- }
- });
- //connections
- this._sawtooth.chain(this._thresh, this.output);
- this.width.chain(this._widthGate, this._thresh);
- this._readOnly([
- 'width',
- 'frequency',
- 'detune'
- ]);
- };
- Tone.extend(Tone.PulseOscillator, Tone.Source);
- /**
- * The default parameters.
- * @static
- * @const
- * @type {Object}
- */
- Tone.PulseOscillator.defaults = {
- 'frequency': 440,
- 'detune': 0,
- 'phase': 0,
- 'width': 0.2
- };
- /**
- * start the oscillator
- * @param {Time} time
- * @private
- */
- Tone.PulseOscillator.prototype._start = function (time) {
- time = this.toSeconds(time);
- this._sawtooth.start(time);
- this._widthGate.gain.setValueAtTime(1, time);
- };
- /**
- * stop the oscillator
- * @param {Time} time
- * @private
- */
- Tone.PulseOscillator.prototype._stop = function (time) {
- time = this.toSeconds(time);
- this._sawtooth.stop(time);
- //the width is still connected to the output.
- //that needs to be stopped also
- this._widthGate.gain.setValueAtTime(0, time);
- };
- /**
- * The phase of the oscillator in degrees.
- * @memberOf Tone.PulseOscillator#
- * @type {Degrees}
- * @name phase
- */
- Object.defineProperty(Tone.PulseOscillator.prototype, 'phase', {
- get: function () {
- return this._sawtooth.phase;
- },
- set: function (phase) {
- this._sawtooth.phase = phase;
- }
- });
- /**
- * The type of the oscillator. Always returns "pulse".
- * @readOnly
- * @memberOf Tone.PulseOscillator#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.PulseOscillator.prototype, 'type', {
- get: function () {
- return 'pulse';
- }
- });
- /**
- * The partials of the waveform. Cannot set partials for this waveform type
- * @memberOf Tone.PulseOscillator#
- * @type {Array}
- * @name partials
- * @private
- */
- Object.defineProperty(Tone.PulseOscillator.prototype, 'partials', {
- get: function () {
- return [];
- }
- });
- /**
- * Clean up method.
- * @return {Tone.PulseOscillator} this
- */
- Tone.PulseOscillator.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this._sawtooth.dispose();
- this._sawtooth = null;
- this._writable([
- 'width',
- 'frequency',
- 'detune'
- ]);
- this.width.dispose();
- this.width = null;
- this._widthGate.dispose();
- this._widthGate = null;
- this._thresh.dispose();
- this._thresh = null;
- this.frequency = null;
- this.detune = null;
- return this;
- };
- return Tone.PulseOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.PWMOscillator modulates the width of a Tone.PulseOscillator
- * at the modulationFrequency. This has the effect of continuously
- * changing the timbre of the oscillator by altering the harmonics
- * generated.
- *
- * @extends {Tone.Source}
- * @constructor
- * @param {Frequency} frequency The starting frequency of the oscillator.
- * @param {Frequency} modulationFrequency The modulation frequency of the width of the pulse.
- * @example
- * var pwm = new Tone.PWMOscillator("Ab3", 0.3).toMaster().start();
- */
- Tone.PWMOscillator = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'modulationFrequency'
- ], Tone.PWMOscillator);
- Tone.Source.call(this, options);
- /**
- * the pulse oscillator
- * @type {Tone.PulseOscillator}
- * @private
- */
- this._pulse = new Tone.PulseOscillator(options.modulationFrequency);
- //change the pulse oscillator type
- this._pulse._sawtooth.type = 'sine';
- /**
- * the modulator
- * @type {Tone.Oscillator}
- * @private
- */
- this._modulator = new Tone.Oscillator({
- 'frequency': options.frequency,
- 'detune': options.detune,
- 'phase': options.phase
- });
- /**
- * Scale the oscillator so it doesn't go silent
- * at the extreme values.
- * @type {Tone.Multiply}
- * @private
- */
- this._scale = new Tone.Multiply(2);
- /**
- * The frequency control.
- * @type {Frequency}
- * @signal
- */
- this.frequency = this._modulator.frequency;
- /**
- * The detune of the oscillator.
- * @type {Cents}
- * @signal
- */
- this.detune = this._modulator.detune;
- /**
- * The modulation rate of the oscillator.
- * @type {Frequency}
- * @signal
- */
- this.modulationFrequency = this._pulse.frequency;
- //connections
- this._modulator.chain(this._scale, this._pulse.width);
- this._pulse.connect(this.output);
- this._readOnly([
- 'modulationFrequency',
- 'frequency',
- 'detune'
- ]);
- };
- Tone.extend(Tone.PWMOscillator, Tone.Source);
- /**
- * default values
- * @static
- * @type {Object}
- * @const
- */
- Tone.PWMOscillator.defaults = {
- 'frequency': 440,
- 'detune': 0,
- 'phase': 0,
- 'modulationFrequency': 0.4
- };
- /**
- * start the oscillator
- * @param {Time} [time=now]
- * @private
- */
- Tone.PWMOscillator.prototype._start = function (time) {
- time = this.toSeconds(time);
- this._modulator.start(time);
- this._pulse.start(time);
- };
- /**
- * stop the oscillator
- * @param {Time} time (optional) timing parameter
- * @private
- */
- Tone.PWMOscillator.prototype._stop = function (time) {
- time = this.toSeconds(time);
- this._modulator.stop(time);
- this._pulse.stop(time);
- };
- /**
- * The type of the oscillator. Always returns "pwm".
- * @readOnly
- * @memberOf Tone.PWMOscillator#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.PWMOscillator.prototype, 'type', {
- get: function () {
- return 'pwm';
- }
- });
- /**
- * The partials of the waveform. Cannot set partials for this waveform type
- * @memberOf Tone.PWMOscillator#
- * @type {Array}
- * @name partials
- * @private
- */
- Object.defineProperty(Tone.PWMOscillator.prototype, 'partials', {
- get: function () {
- return [];
- }
- });
- /**
- * The phase of the oscillator in degrees.
- * @memberOf Tone.PWMOscillator#
- * @type {number}
- * @name phase
- */
- Object.defineProperty(Tone.PWMOscillator.prototype, 'phase', {
- get: function () {
- return this._modulator.phase;
- },
- set: function (phase) {
- this._modulator.phase = phase;
- }
- });
- /**
- * Clean up.
- * @return {Tone.PWMOscillator} this
- */
- Tone.PWMOscillator.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this._pulse.dispose();
- this._pulse = null;
- this._scale.dispose();
- this._scale = null;
- this._modulator.dispose();
- this._modulator = null;
- this._writable([
- 'modulationFrequency',
- 'frequency',
- 'detune'
- ]);
- this.frequency = null;
- this.detune = null;
- this.modulationFrequency = null;
- return this;
- };
- return Tone.PWMOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.FMOscillator
- *
- * @extends {Tone.Source}
- * @constructor
- * @param {Frequency} frequency The starting frequency of the oscillator.
- * @param {String} type The type of the carrier oscillator.
- * @param {String} modulationType The type of the modulator oscillator.
- * @example
- * //a sine oscillator frequency-modulated by a square wave
- * var fmOsc = new Tone.FMOscillator("Ab3", "sine", "square").toMaster().start();
- */
- Tone.FMOscillator = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'type',
- 'modulationType'
- ], Tone.FMOscillator);
- Tone.Source.call(this, options);
- /**
- * The carrier oscillator
- * @type {Tone.Oscillator}
- * @private
- */
- this._carrier = new Tone.Oscillator(options.frequency, options.type);
- /**
- * The oscillator's frequency
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);
- /**
- * The detune control signal.
- * @type {Cents}
- * @signal
- */
- this.detune = this._carrier.detune;
- this.detune.value = options.detune;
- /**
- * 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.
- * @type {Positive}
- * @signal
- */
- this.modulationIndex = new Tone.Multiply(options.modulationIndex);
- this.modulationIndex.units = Tone.Type.Positive;
- /**
- * The modulating oscillator
- * @type {Tone.Oscillator}
- * @private
- */
- this._modulator = new Tone.Oscillator(options.frequency, options.modulationType);
- /**
- * 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.
- * @type {Positive}
- * @signal
- * @example
- * //pitch the modulator an octave below carrier
- * synth.harmonicity.value = 0.5;
- */
- this.harmonicity = new Tone.Multiply(options.harmonicity);
- this.harmonicity.units = Tone.Type.Positive;
- /**
- * the node where the modulation happens
- * @type {Tone.Gain}
- * @private
- */
- this._modulationNode = new Tone.Gain(0);
- //connections
- this.frequency.connect(this._carrier.frequency);
- this.frequency.chain(this.harmonicity, this._modulator.frequency);
- this.frequency.chain(this.modulationIndex, this._modulationNode);
- this._modulator.connect(this._modulationNode.gain);
- this._modulationNode.connect(this._carrier.frequency);
- this._carrier.connect(this.output);
- this.detune.connect(this._modulator.detune);
- this.phase = options.phase;
- this._readOnly([
- 'modulationIndex',
- 'frequency',
- 'detune',
- 'harmonicity'
- ]);
- };
- Tone.extend(Tone.FMOscillator, Tone.Source);
- /**
- * default values
- * @static
- * @type {Object}
- * @const
- */
- Tone.FMOscillator.defaults = {
- 'frequency': 440,
- 'detune': 0,
- 'phase': 0,
- 'modulationIndex': 2,
- 'modulationType': 'square',
- 'harmonicity': 1
- };
- /**
- * start the oscillator
- * @param {Time} [time=now]
- * @private
- */
- Tone.FMOscillator.prototype._start = function (time) {
- time = this.toSeconds(time);
- this._modulator.start(time);
- this._carrier.start(time);
- };
- /**
- * stop the oscillator
- * @param {Time} time (optional) timing parameter
- * @private
- */
- Tone.FMOscillator.prototype._stop = function (time) {
- time = this.toSeconds(time);
- this._modulator.stop(time);
- this._carrier.stop(time);
- };
- /**
- * The type of the carrier oscillator
- * @memberOf Tone.FMOscillator#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.FMOscillator.prototype, 'type', {
- get: function () {
- return this._carrier.type;
- },
- set: function (type) {
- this._carrier.type = type;
- }
- });
- /**
- * The type of the modulator oscillator
- * @memberOf Tone.FMOscillator#
- * @type {String}
- * @name modulationType
- */
- Object.defineProperty(Tone.FMOscillator.prototype, 'modulationType', {
- get: function () {
- return this._modulator.type;
- },
- set: function (type) {
- this._modulator.type = type;
- }
- });
- /**
- * The phase of the oscillator in degrees.
- * @memberOf Tone.FMOscillator#
- * @type {number}
- * @name phase
- */
- Object.defineProperty(Tone.FMOscillator.prototype, 'phase', {
- get: function () {
- return this._carrier.phase;
- },
- set: function (phase) {
- this._carrier.phase = phase;
- this._modulator.phase = phase;
- }
- });
- /**
- * The partials of the carrier 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".
- * @memberOf Tone.FMOscillator#
- * @type {Array}
- * @name partials
- * @example
- * osc.partials = [1, 0.2, 0.01];
- */
- Object.defineProperty(Tone.FMOscillator.prototype, 'partials', {
- get: function () {
- return this._carrier.partials;
- },
- set: function (partials) {
- this._carrier.partials = partials;
- }
- });
- /**
- * Clean up.
- * @return {Tone.FMOscillator} this
- */
- Tone.FMOscillator.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this._writable([
- 'modulationIndex',
- 'frequency',
- 'detune',
- 'harmonicity'
- ]);
- this.frequency.dispose();
- this.frequency = null;
- this.detune = null;
- this.harmonicity.dispose();
- this.harmonicity = null;
- this._carrier.dispose();
- this._carrier = null;
- this._modulator.dispose();
- this._modulator = null;
- this._modulationNode.dispose();
- this._modulationNode = null;
- this.modulationIndex.dispose();
- this.modulationIndex = null;
- return this;
- };
- return Tone.FMOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.AMOscillator
- *
- * @extends {Tone.Oscillator}
- * @constructor
- * @param {Frequency} frequency The starting frequency of the oscillator.
- * @param {String} type The type of the carrier oscillator.
- * @param {String} modulationType The type of the modulator oscillator.
- * @example
- * //a sine oscillator frequency-modulated by a square wave
- * var fmOsc = new Tone.AMOscillator("Ab3", "sine", "square").toMaster().start();
- */
- Tone.AMOscillator = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'type',
- 'modulationType'
- ], Tone.AMOscillator);
- Tone.Source.call(this, options);
- /**
- * The carrier oscillator
- * @type {Tone.Oscillator}
- * @private
- */
- this._carrier = new Tone.Oscillator(options.frequency, options.type);
- /**
- * The oscillator's frequency
- * @type {Frequency}
- * @signal
- */
- this.frequency = this._carrier.frequency;
- /**
- * The detune control signal.
- * @type {Cents}
- * @signal
- */
- this.detune = this._carrier.detune;
- this.detune.value = options.detune;
- /**
- * The modulating oscillator
- * @type {Tone.Oscillator}
- * @private
- */
- this._modulator = new Tone.Oscillator(options.frequency, options.modulationType);
- /**
- * convert the -1,1 output to 0,1
- * @type {Tone.AudioToGain}
- * @private
- */
- this._modulationScale = new Tone.AudioToGain();
- /**
- * 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.
- * @type {Positive}
- * @signal
- * @example
- * //pitch the modulator an octave below carrier
- * synth.harmonicity.value = 0.5;
- */
- this.harmonicity = new Tone.Multiply(options.harmonicity);
- this.harmonicity.units = Tone.Type.Positive;
- /**
- * the node where the modulation happens
- * @type {Tone.Gain}
- * @private
- */
- this._modulationNode = new Tone.Gain(0);
- //connections
- this.frequency.chain(this.harmonicity, this._modulator.frequency);
- this.detune.connect(this._modulator.detune);
- this._modulator.chain(this._modulationScale, this._modulationNode.gain);
- this._carrier.chain(this._modulationNode, this.output);
- this.phase = options.phase;
- this._readOnly([
- 'frequency',
- 'detune',
- 'harmonicity'
- ]);
- };
- Tone.extend(Tone.AMOscillator, Tone.Oscillator);
- /**
- * default values
- * @static
- * @type {Object}
- * @const
- */
- Tone.AMOscillator.defaults = {
- 'frequency': 440,
- 'detune': 0,
- 'phase': 0,
- 'modulationType': 'square',
- 'harmonicity': 1
- };
- /**
- * start the oscillator
- * @param {Time} [time=now]
- * @private
- */
- Tone.AMOscillator.prototype._start = function (time) {
- time = this.toSeconds(time);
- this._modulator.start(time);
- this._carrier.start(time);
- };
- /**
- * stop the oscillator
- * @param {Time} time (optional) timing parameter
- * @private
- */
- Tone.AMOscillator.prototype._stop = function (time) {
- time = this.toSeconds(time);
- this._modulator.stop(time);
- this._carrier.stop(time);
- };
- /**
- * The type of the carrier oscillator
- * @memberOf Tone.AMOscillator#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.AMOscillator.prototype, 'type', {
- get: function () {
- return this._carrier.type;
- },
- set: function (type) {
- this._carrier.type = type;
- }
- });
- /**
- * The type of the modulator oscillator
- * @memberOf Tone.AMOscillator#
- * @type {string}
- * @name modulationType
- */
- Object.defineProperty(Tone.AMOscillator.prototype, 'modulationType', {
- get: function () {
- return this._modulator.type;
- },
- set: function (type) {
- this._modulator.type = type;
- }
- });
- /**
- * The phase of the oscillator in degrees.
- * @memberOf Tone.AMOscillator#
- * @type {number}
- * @name phase
- */
- Object.defineProperty(Tone.AMOscillator.prototype, 'phase', {
- get: function () {
- return this._carrier.phase;
- },
- set: function (phase) {
- this._carrier.phase = phase;
- this._modulator.phase = phase;
- }
- });
- /**
- * The partials of the carrier 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".
- * @memberOf Tone.AMOscillator#
- * @type {Array}
- * @name partials
- * @example
- * osc.partials = [1, 0.2, 0.01];
- */
- Object.defineProperty(Tone.AMOscillator.prototype, 'partials', {
- get: function () {
- return this._carrier.partials;
- },
- set: function (partials) {
- this._carrier.partials = partials;
- }
- });
- /**
- * Clean up.
- * @return {Tone.AMOscillator} this
- */
- Tone.AMOscillator.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this._writable([
- 'frequency',
- 'detune',
- 'harmonicity'
- ]);
- this.frequency = null;
- this.detune = null;
- this.harmonicity.dispose();
- this.harmonicity = null;
- this._carrier.dispose();
- this._carrier = null;
- this._modulator.dispose();
- this._modulator = null;
- this._modulationNode.dispose();
- this._modulationNode = null;
- this._modulationScale.dispose();
- this._modulationScale = null;
- return this;
- };
- return Tone.AMOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.FatOscillator
- *
- * @extends {Tone.Source}
- * @constructor
- * @param {Frequency} frequency The starting frequency of the oscillator.
- * @param {String} type The type of the carrier oscillator.
- * @param {String} modulationType The type of the modulator oscillator.
- * @example
- * //a sine oscillator frequency-modulated by a square wave
- * var fmOsc = new Tone.FatOscillator("Ab3", "sine", "square").toMaster().start();
- */
- Tone.FatOscillator = function () {
- var options = Tone.defaults(arguments, [
- 'frequency',
- 'type',
- 'spread'
- ], Tone.FatOscillator);
- Tone.Source.call(this, options);
- /**
- * The oscillator's frequency
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);
- /**
- * The detune control signal.
- * @type {Cents}
- * @signal
- */
- this.detune = new Tone.Signal(options.detune, Tone.Type.Cents);
- /**
- * The array of oscillators
- * @type {Array}
- * @private
- */
- this._oscillators = [];
- /**
- * The total spread of the oscillators
- * @type {Cents}
- * @private
- */
- this._spread = options.spread;
- /**
- * The type of the oscillator
- * @type {String}
- * @private
- */
- this._type = options.type;
- /**
- * The phase of the oscillators
- * @type {Degrees}
- * @private
- */
- this._phase = options.phase;
- /**
- * The partials array
- * @type {Array}
- * @private
- */
- this._partials = Tone.defaultArg(options.partials, []);
- //set the count initially
- this.count = options.count;
- this._readOnly([
- 'frequency',
- 'detune'
- ]);
- };
- Tone.extend(Tone.FatOscillator, Tone.Source);
- /**
- * default values
- * @static
- * @type {Object}
- * @const
- */
- Tone.FatOscillator.defaults = {
- 'frequency': 440,
- 'detune': 0,
- 'phase': 0,
- 'spread': 20,
- 'count': 3,
- 'type': 'sawtooth'
- };
- /**
- * start the oscillator
- * @param {Time} [time=now]
- * @private
- */
- Tone.FatOscillator.prototype._start = function (time) {
- time = this.toSeconds(time);
- this._forEach(function (osc) {
- osc.start(time);
- });
- };
- /**
- * stop the oscillator
- * @param {Time} time (optional) timing parameter
- * @private
- */
- Tone.FatOscillator.prototype._stop = function (time) {
- time = this.toSeconds(time);
- this._forEach(function (osc) {
- osc.stop(time);
- });
- };
- /**
- * Iterate over all of the oscillators
- * @param {Function} iterator The iterator function
- * @private
- */
- Tone.FatOscillator.prototype._forEach = function (iterator) {
- for (var i = 0; i < this._oscillators.length; i++) {
- iterator.call(this, this._oscillators[i], i);
- }
- };
- /**
- * The type of the carrier oscillator
- * @memberOf Tone.FatOscillator#
- * @type {string}
- * @name type
- */
- Object.defineProperty(Tone.FatOscillator.prototype, 'type', {
- get: function () {
- return this._type;
- },
- set: function (type) {
- this._type = type;
- this._forEach(function (osc) {
- osc.type = type;
- });
- }
- });
- /**
- * 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.
- * @memberOf Tone.FatOscillator#
- * @type {Cents}
- * @name spread
- */
- Object.defineProperty(Tone.FatOscillator.prototype, 'spread', {
- get: function () {
- return this._spread;
- },
- set: function (spread) {
- this._spread = spread;
- if (this._oscillators.length > 1) {
- var start = -spread / 2;
- var step = spread / (this._oscillators.length - 1);
- this._forEach(function (osc, i) {
- osc.detune.value = start + step * i;
- });
- }
- }
- });
- /**
- * The number of detuned oscillators
- * @memberOf Tone.FatOscillator#
- * @type {Number}
- * @name count
- */
- Object.defineProperty(Tone.FatOscillator.prototype, 'count', {
- get: function () {
- return this._oscillators.length;
- },
- set: function (count) {
- count = Math.max(count, 1);
- if (this._oscillators.length !== count) {
- // var partials = this.partials;
- // var type = this.type;
- //dispose the previous oscillators
- this._forEach(function (osc) {
- osc.dispose();
- });
- this._oscillators = [];
- for (var i = 0; i < count; i++) {
- var osc = new Tone.Oscillator();
- if (this.type === Tone.Oscillator.Type.Custom) {
- osc.partials = this._partials;
- } else {
- osc.type = this._type;
- }
- osc.phase = this._phase;
- osc.volume.value = -6 - count;
- this.frequency.connect(osc.frequency);
- this.detune.connect(osc.detune);
- osc.connect(this.output);
- this._oscillators[i] = osc;
- }
- //set the spread
- this.spread = this._spread;
- if (this.state === Tone.State.Started) {
- this._forEach(function (osc) {
- osc.start();
- });
- }
- }
- }
- });
- /**
- * The phase of the oscillator in degrees.
- * @memberOf Tone.FatOscillator#
- * @type {Number}
- * @name phase
- */
- Object.defineProperty(Tone.FatOscillator.prototype, 'phase', {
- get: function () {
- return this._phase;
- },
- set: function (phase) {
- this._phase = phase;
- this._forEach(function (osc) {
- osc.phase = phase;
- });
- }
- });
- /**
- * The partials of the carrier 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".
- * @memberOf Tone.FatOscillator#
- * @type {Array}
- * @name partials
- * @example
- * osc.partials = [1, 0.2, 0.01];
- */
- Object.defineProperty(Tone.FatOscillator.prototype, 'partials', {
- get: function () {
- return this._partials;
- },
- set: function (partials) {
- this._partials = partials;
- this._type = Tone.Oscillator.Type.Custom;
- this._forEach(function (osc) {
- osc.partials = partials;
- });
- }
- });
- /**
- * Clean up.
- * @return {Tone.FatOscillator} this
- */
- Tone.FatOscillator.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this._writable([
- 'frequency',
- 'detune'
- ]);
- this.frequency.dispose();
- this.frequency = null;
- this.detune.dispose();
- this.detune = null;
- this._forEach(function (osc) {
- osc.dispose();
- });
- this._oscillators = null;
- this._partials = null;
- return this;
- };
- return Tone.FatOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @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);
- };
- /**
- * 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;
- }
- });
- /**
- * 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;
- };
- /**
- * 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 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;
- }
- }
- });
- /**
- * 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;
- }
- },
- 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;
- }
- },
- 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;
- }
- },
- 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
- */
- Object.defineProperty(Tone.OmniOscillator.prototype, 'modulationIndex', {
- get: function () {
- if (this._sourceType === OmniOscType.FM) {
- return this._oscillator.modulationIndex;
- }
- }
- });
- /**
- * 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;
- }
- }
- });
- /**
- * 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;
- }
- }
- });
- /**
- * 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;
- };
- return Tone.OmniOscillator;
- });
- Module(function (Tone) {
-
- /**
- * @class Base-class for all instruments
- *
- * @constructor
- * @extends {Tone.AudioNode}
- */
- Tone.Instrument = function (options) {
- //get the defaults
- options = Tone.defaultArg(options, Tone.Instrument.defaults);
- Tone.AudioNode.call(this);
- /**
- * The output and volume triming node
- * @type {Tone.Volume}
- * @private
- */
- this._volume = this.output = new Tone.Volume(options.volume);
- /**
- * The volume of the output in decibels.
- * @type {Decibels}
- * @signal
- * @example
- * source.volume.value = -6;
- */
- this.volume = this._volume.volume;
- this._readOnly('volume');
- };
- Tone.extend(Tone.Instrument, Tone.AudioNode);
- /**
- * the default attributes
- * @type {object}
- */
- Tone.Instrument.defaults = {
- /** the volume of the output in decibels */
- 'volume': 0
- };
- /**
- * @abstract
- * @param {string|number} note the note to trigger
- * @param {Time} [time=now] the time to trigger the ntoe
- * @param {number} [velocity=1] the velocity to trigger the note
- */
- Tone.Instrument.prototype.triggerAttack = Tone.noOp;
- /**
- * @abstract
- * @param {Time} [time=now] when to trigger the release
- */
- Tone.Instrument.prototype.triggerRelease = Tone.noOp;
- /**
- * Trigger the attack and then the release after the duration.
- * @param {Frequency} note The note to trigger.
- * @param {Time} duration How long the note should be held for before
- * triggering the release. This value must be greater than 0.
- * @param {Time} [time=now] When the note should be triggered.
- * @param {NormalRange} [velocity=1] The velocity the note should be triggered at.
- * @returns {Tone.Instrument} this
- * @example
- * //trigger "C4" for the duration of an 8th note
- * synth.triggerAttackRelease("C4", "8n");
- */
- Tone.Instrument.prototype.triggerAttackRelease = function (note, duration, time, velocity) {
- time = this.toSeconds(time);
- duration = this.toSeconds(duration);
- this.triggerAttack(note, time, velocity);
- this.triggerRelease(time + duration);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.Instrument} this
- */
- Tone.Instrument.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._volume.dispose();
- this._volume = null;
- this._writable(['volume']);
- this.volume = null;
- return this;
- };
- return Tone.Instrument;
- });
- Module(function (Tone) {
-
- /**
- * @class This is an abstract base class for other monophonic instruments to
- * extend. IMPORTANT: It does not make any sound on its own and
- * shouldn't be directly instantiated.
- *
- * @constructor
- * @abstract
- * @extends {Tone.Instrument}
- */
- Tone.Monophonic = function (options) {
- //get the defaults
- options = Tone.defaultArg(options, Tone.Monophonic.defaults);
- Tone.Instrument.call(this, options);
- /**
- * The glide time between notes.
- * @type {Time}
- */
- this.portamento = options.portamento;
- };
- Tone.extend(Tone.Monophonic, Tone.Instrument);
- /**
- * @static
- * @const
- * @type {Object}
- */
- Tone.Monophonic.defaults = { 'portamento': 0 };
- /**
- * Trigger the attack of the note optionally with a given velocity.
- *
- *
- * @param {Frequency} note The note to trigger.
- * @param {Time} [time=now] When the note should start.
- * @param {number} [velocity=1] velocity The velocity scaler
- * determines how "loud" the note
- * will be triggered.
- * @returns {Tone.Monophonic} this
- * @example
- * synth.triggerAttack("C4");
- * @example
- * //trigger the note a half second from now at half velocity
- * synth.triggerAttack("C4", "+0.5", 0.5);
- */
- Tone.Monophonic.prototype.triggerAttack = function (note, time, velocity) {
- time = this.toSeconds(time);
- this._triggerEnvelopeAttack(time, velocity);
- this.setNote(note, time);
- return this;
- };
- /**
- * Trigger the release portion of the envelope
- * @param {Time} [time=now] If no time is given, the release happens immediatly
- * @returns {Tone.Monophonic} this
- * @example
- * synth.triggerRelease();
- */
- Tone.Monophonic.prototype.triggerRelease = function (time) {
- time = this.toSeconds(time);
- this._triggerEnvelopeRelease(time);
- return this;
- };
- /**
- * override this method with the actual method
- * @abstract
- * @private
- */
- Tone.Monophonic.prototype._triggerEnvelopeAttack = function () {
- };
- /**
- * override this method with the actual method
- * @abstract
- * @private
- */
- Tone.Monophonic.prototype._triggerEnvelopeRelease = function () {
- };
- /**
- * Set the note at the given time. If no time is given, the note
- * will set immediately.
- * @param {Frequency} note The note to change to.
- * @param {Time} [time=now] The time when the note should be set.
- * @returns {Tone.Monophonic} this
- * @example
- * //change to F#6 in one quarter note from now.
- * synth.setNote("F#6", "+4n");
- * @example
- * //change to Bb4 right now
- * synth.setNote("Bb4");
- */
- Tone.Monophonic.prototype.setNote = function (note, time) {
- time = this.toSeconds(time);
- if (this.portamento > 0) {
- var currentNote = this.frequency.value;
- this.frequency.setValueAtTime(currentNote, time);
- var portTime = this.toSeconds(this.portamento);
- this.frequency.exponentialRampToValueAtTime(note, time + portTime);
- } else {
- this.frequency.setValueAtTime(note, time);
- }
- return this;
- };
- return Tone.Monophonic;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Synth is composed simply of a Tone.OmniOscillator
- * routed through a Tone.AmplitudeEnvelope.
- *
- *
- * @constructor
- * @extends {Tone.Monophonic}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var synth = new Tone.Synth().toMaster();
- * synth.triggerAttackRelease("C4", "8n");
- */
- Tone.Synth = function (options) {
- //get the defaults
- options = Tone.defaultArg(options, Tone.Synth.defaults);
- Tone.Monophonic.call(this, options);
- /**
- * The oscillator.
- * @type {Tone.OmniOscillator}
- */
- this.oscillator = new Tone.OmniOscillator(options.oscillator);
- /**
- * The frequency control.
- * @type {Frequency}
- * @signal
- */
- this.frequency = this.oscillator.frequency;
- /**
- * The detune control.
- * @type {Cents}
- * @signal
- */
- this.detune = this.oscillator.detune;
- /**
- * The amplitude envelope.
- * @type {Tone.AmplitudeEnvelope}
- */
- this.envelope = new Tone.AmplitudeEnvelope(options.envelope);
- //connect the oscillators to the output
- this.oscillator.chain(this.envelope, this.output);
- //start the oscillators
- this.oscillator.start();
- this._readOnly([
- 'oscillator',
- 'frequency',
- 'detune',
- 'envelope'
- ]);
- };
- Tone.extend(Tone.Synth, Tone.Monophonic);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.Synth.defaults = {
- 'oscillator': { 'type': 'triangle' },
- 'envelope': {
- 'attack': 0.005,
- 'decay': 0.1,
- 'sustain': 0.3,
- 'release': 1
- }
- };
- /**
- * start the attack portion of the envelope
- * @param {Time} [time=now] the time the attack should start
- * @param {number} [velocity=1] the velocity of the note (0-1)
- * @returns {Tone.Synth} this
- * @private
- */
- Tone.Synth.prototype._triggerEnvelopeAttack = function (time, velocity) {
- //the envelopes
- this.envelope.triggerAttack(time, velocity);
- return this;
- };
- /**
- * start the release portion of the envelope
- * @param {Time} [time=now] the time the release should start
- * @returns {Tone.Synth} this
- * @private
- */
- Tone.Synth.prototype._triggerEnvelopeRelease = function (time) {
- this.envelope.triggerRelease(time);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.Synth} this
- */
- Tone.Synth.prototype.dispose = function () {
- Tone.Monophonic.prototype.dispose.call(this);
- this._writable([
- 'oscillator',
- 'frequency',
- 'detune',
- 'envelope'
- ]);
- this.oscillator.dispose();
- this.oscillator = null;
- this.envelope.dispose();
- this.envelope = null;
- this.frequency = null;
- this.detune = null;
- return this;
- };
- return Tone.Synth;
- });
- Module(function (Tone) {
-
- /**
- * @class AMSynth uses the output of one Tone.Synth to modulate the
- * amplitude of another Tone.Synth. The harmonicity (the ratio between
- * the two signals) affects the timbre of the output signal greatly.
- * Read more about Amplitude Modulation Synthesis on
- * [SoundOnSound](https://web.archive.org/web/20160404103653/http://www.soundonsound.com:80/sos/mar00/articles/synthsecrets.htm).
- *
- *
- * @constructor
- * @extends {Tone.Monophonic}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var synth = new Tone.AMSynth().toMaster();
- * synth.triggerAttackRelease("C4", "4n");
- */
- Tone.AMSynth = function (options) {
- options = Tone.defaultArg(options, Tone.AMSynth.defaults);
- Tone.Monophonic.call(this, options);
- /**
- * The carrier voice.
- * @type {Tone.Synth}
- * @private
- */
- this._carrier = new Tone.Synth();
- this._carrier.volume.value = -10;
- /**
- * The carrier's oscillator
- * @type {Tone.Oscillator}
- */
- this.oscillator = this._carrier.oscillator;
- /**
- * The carrier's envelope
- * @type {Tone.AmplitudeEnvelope}
- */
- this.envelope = this._carrier.envelope.set(options.envelope);
- /**
- * The modulator voice.
- * @type {Tone.Synth}
- * @private
- */
- this._modulator = new Tone.Synth();
- this._modulator.volume.value = -10;
- /**
- * The modulator's oscillator which is applied
- * to the amplitude of the oscillator
- * @type {Tone.Oscillator}
- */
- this.modulation = this._modulator.oscillator.set(options.modulation);
- /**
- * The modulator's envelope
- * @type {Tone.AmplitudeEnvelope}
- */
- this.modulationEnvelope = this._modulator.envelope.set(options.modulationEnvelope);
- /**
- * The frequency.
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(440, Tone.Type.Frequency);
- /**
- * The detune in cents
- * @type {Cents}
- * @signal
- */
- this.detune = new Tone.Signal(options.detune, Tone.Type.Cents);
- /**
- * Harmonicity is the ratio between the two voices. A harmonicity of
- * 1 is no change. Harmonicity = 2 means a change of an octave.
- * @type {Positive}
- * @signal
- * @example
- * //pitch voice1 an octave below voice0
- * synth.harmonicity.value = 0.5;
- */
- this.harmonicity = new Tone.Multiply(options.harmonicity);
- this.harmonicity.units = Tone.Type.Positive;
- /**
- * convert the -1,1 output to 0,1
- * @type {Tone.AudioToGain}
- * @private
- */
- this._modulationScale = new Tone.AudioToGain();
- /**
- * the node where the modulation happens
- * @type {Tone.Gain}
- * @private
- */
- this._modulationNode = new Tone.Gain();
- //control the two voices frequency
- this.frequency.connect(this._carrier.frequency);
- this.frequency.chain(this.harmonicity, this._modulator.frequency);
- this.detune.fan(this._carrier.detune, this._modulator.detune);
- this._modulator.chain(this._modulationScale, this._modulationNode.gain);
- this._carrier.chain(this._modulationNode, this.output);
- this._readOnly([
- 'frequency',
- 'harmonicity',
- 'oscillator',
- 'envelope',
- 'modulation',
- 'modulationEnvelope',
- 'detune'
- ]);
- };
- Tone.extend(Tone.AMSynth, Tone.Monophonic);
- /**
- * @static
- * @type {Object}
- */
- Tone.AMSynth.defaults = {
- 'harmonicity': 3,
- 'detune': 0,
- 'oscillator': { 'type': 'sine' },
- 'envelope': {
- 'attack': 0.01,
- 'decay': 0.01,
- 'sustain': 1,
- 'release': 0.5
- },
- 'modulation': { 'type': 'square' },
- 'modulationEnvelope': {
- 'attack': 0.5,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- }
- };
- /**
- * trigger the attack portion of the note
- *
- * @param {Time} [time=now] the time the note will occur
- * @param {NormalRange} [velocity=1] the velocity of the note
- * @private
- * @returns {Tone.AMSynth} this
- */
- Tone.AMSynth.prototype._triggerEnvelopeAttack = function (time, velocity) {
- //the port glide
- time = this.toSeconds(time);
- //the envelopes
- this.envelope.triggerAttack(time, velocity);
- this.modulationEnvelope.triggerAttack(time, velocity);
- return this;
- };
- /**
- * trigger the release portion of the note
- *
- * @param {Time} [time=now] the time the note will release
- * @private
- * @returns {Tone.AMSynth} this
- */
- Tone.AMSynth.prototype._triggerEnvelopeRelease = function (time) {
- this.envelope.triggerRelease(time);
- this.modulationEnvelope.triggerRelease(time);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.AMSynth} this
- */
- Tone.AMSynth.prototype.dispose = function () {
- Tone.Monophonic.prototype.dispose.call(this);
- this._writable([
- 'frequency',
- 'harmonicity',
- 'oscillator',
- 'envelope',
- 'modulation',
- 'modulationEnvelope',
- 'detune'
- ]);
- this._carrier.dispose();
- this._carrier = null;
- this._modulator.dispose();
- this._modulator = null;
- this.frequency.dispose();
- this.frequency = null;
- this.detune.dispose();
- this.detune = null;
- this.harmonicity.dispose();
- this.harmonicity = null;
- this._modulationScale.dispose();
- this._modulationScale = null;
- this._modulationNode.dispose();
- this._modulationNode = null;
- this.oscillator = null;
- this.envelope = null;
- this.modulationEnvelope = null;
- this.modulation = null;
- return this;
- };
- return Tone.AMSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.MonoSynth is composed of one oscillator, one filter, and two envelopes.
- * The amplitude of the Tone.Oscillator and the cutoff frequency of the
- * Tone.Filter are controlled by Tone.Envelopes.
- *
- *
- * @constructor
- * @extends {Tone.Monophonic}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var synth = new Tone.MonoSynth({
- * "oscillator" : {
- * "type" : "square"
- * },
- * "envelope" : {
- * "attack" : 0.1
- * }
- * }).toMaster();
- * synth.triggerAttackRelease("C4", "8n");
- */
- Tone.MonoSynth = function (options) {
- //get the defaults
- options = Tone.defaultArg(options, Tone.MonoSynth.defaults);
- Tone.Monophonic.call(this, options);
- /**
- * The oscillator.
- * @type {Tone.OmniOscillator}
- */
- this.oscillator = new Tone.OmniOscillator(options.oscillator);
- /**
- * The frequency control.
- * @type {Frequency}
- * @signal
- */
- this.frequency = this.oscillator.frequency;
- /**
- * The detune control.
- * @type {Cents}
- * @signal
- */
- this.detune = this.oscillator.detune;
- /**
- * The filter.
- * @type {Tone.Filter}
- */
- this.filter = new Tone.Filter(options.filter);
- /**
- * The filter envelope.
- * @type {Tone.FrequencyEnvelope}
- */
- this.filterEnvelope = new Tone.FrequencyEnvelope(options.filterEnvelope);
- /**
- * The amplitude envelope.
- * @type {Tone.AmplitudeEnvelope}
- */
- this.envelope = new Tone.AmplitudeEnvelope(options.envelope);
- //connect the oscillators to the output
- this.oscillator.chain(this.filter, this.envelope, this.output);
- //start the oscillators
- this.oscillator.start();
- //connect the filter envelope
- this.filterEnvelope.connect(this.filter.frequency);
- this._readOnly([
- 'oscillator',
- 'frequency',
- 'detune',
- 'filter',
- 'filterEnvelope',
- 'envelope'
- ]);
- };
- Tone.extend(Tone.MonoSynth, Tone.Monophonic);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.MonoSynth.defaults = {
- 'frequency': 'C4',
- 'detune': 0,
- 'oscillator': { 'type': 'square' },
- 'filter': {
- 'Q': 6,
- 'type': 'lowpass',
- 'rolloff': -24
- },
- 'envelope': {
- 'attack': 0.005,
- 'decay': 0.1,
- 'sustain': 0.9,
- 'release': 1
- },
- 'filterEnvelope': {
- 'attack': 0.06,
- 'decay': 0.2,
- 'sustain': 0.5,
- 'release': 2,
- 'baseFrequency': 200,
- 'octaves': 7,
- 'exponent': 2
- }
- };
- /**
- * start the attack portion of the envelope
- * @param {Time} [time=now] the time the attack should start
- * @param {NormalRange} [velocity=1] the velocity of the note (0-1)
- * @returns {Tone.MonoSynth} this
- * @private
- */
- Tone.MonoSynth.prototype._triggerEnvelopeAttack = function (time, velocity) {
- //the envelopes
- this.envelope.triggerAttack(time, velocity);
- this.filterEnvelope.triggerAttack(time);
- return this;
- };
- /**
- * start the release portion of the envelope
- * @param {Time} [time=now] the time the release should start
- * @returns {Tone.MonoSynth} this
- * @private
- */
- Tone.MonoSynth.prototype._triggerEnvelopeRelease = function (time) {
- this.envelope.triggerRelease(time);
- this.filterEnvelope.triggerRelease(time);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.MonoSynth} this
- */
- Tone.MonoSynth.prototype.dispose = function () {
- Tone.Monophonic.prototype.dispose.call(this);
- this._writable([
- 'oscillator',
- 'frequency',
- 'detune',
- 'filter',
- 'filterEnvelope',
- 'envelope'
- ]);
- this.oscillator.dispose();
- this.oscillator = null;
- this.envelope.dispose();
- this.envelope = null;
- this.filterEnvelope.dispose();
- this.filterEnvelope = null;
- this.filter.dispose();
- this.filter = null;
- this.frequency = null;
- this.detune = null;
- return this;
- };
- return Tone.MonoSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.DuoSynth is a monophonic synth composed of two
- * MonoSynths run in parallel with control over the
- * frequency ratio between the two voices and vibrato effect.
- *
- *
- * @constructor
- * @extends {Tone.Monophonic}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var duoSynth = new Tone.DuoSynth().toMaster();
- * duoSynth.triggerAttackRelease("C4", "2n");
- */
- Tone.DuoSynth = function (options) {
- options = Tone.defaultArg(options, Tone.DuoSynth.defaults);
- Tone.Monophonic.call(this, options);
- /**
- * the first voice
- * @type {Tone.MonoSynth}
- */
- this.voice0 = new Tone.MonoSynth(options.voice0);
- this.voice0.volume.value = -10;
- /**
- * the second voice
- * @type {Tone.MonoSynth}
- */
- this.voice1 = new Tone.MonoSynth(options.voice1);
- this.voice1.volume.value = -10;
- /**
- * The vibrato LFO.
- * @type {Tone.LFO}
- * @private
- */
- this._vibrato = new Tone.LFO(options.vibratoRate, -50, 50);
- this._vibrato.start();
- /**
- * the vibrato frequency
- * @type {Frequency}
- * @signal
- */
- this.vibratoRate = this._vibrato.frequency;
- /**
- * the vibrato gain
- * @type {Tone.Gain}
- * @private
- */
- this._vibratoGain = new Tone.Gain(options.vibratoAmount, Tone.Type.Positive);
- /**
- * The amount of vibrato
- * @type {Positive}
- * @signal
- */
- this.vibratoAmount = this._vibratoGain.gain;
- /**
- * the frequency control
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(440, Tone.Type.Frequency);
- /**
- * Harmonicity is the ratio between the two voices. A harmonicity of
- * 1 is no change. Harmonicity = 2 means a change of an octave.
- * @type {Positive}
- * @signal
- * @example
- * //pitch voice1 an octave below voice0
- * duoSynth.harmonicity.value = 0.5;
- */
- this.harmonicity = new Tone.Multiply(options.harmonicity);
- this.harmonicity.units = Tone.Type.Positive;
- //control the two voices frequency
- this.frequency.connect(this.voice0.frequency);
- this.frequency.chain(this.harmonicity, this.voice1.frequency);
- this._vibrato.connect(this._vibratoGain);
- this._vibratoGain.fan(this.voice0.detune, this.voice1.detune);
- this.voice0.connect(this.output);
- this.voice1.connect(this.output);
- this._readOnly([
- 'voice0',
- 'voice1',
- 'frequency',
- 'vibratoAmount',
- 'vibratoRate'
- ]);
- };
- Tone.extend(Tone.DuoSynth, Tone.Monophonic);
- /**
- * @static
- * @type {Object}
- */
- Tone.DuoSynth.defaults = {
- 'vibratoAmount': 0.5,
- 'vibratoRate': 5,
- 'harmonicity': 1.5,
- 'voice0': {
- 'volume': -10,
- 'portamento': 0,
- 'oscillator': { 'type': 'sine' },
- 'filterEnvelope': {
- 'attack': 0.01,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- },
- 'envelope': {
- 'attack': 0.01,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- }
- },
- 'voice1': {
- 'volume': -10,
- 'portamento': 0,
- 'oscillator': { 'type': 'sine' },
- 'filterEnvelope': {
- 'attack': 0.01,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- },
- 'envelope': {
- 'attack': 0.01,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- }
- }
- };
- /**
- * start the attack portion of the envelopes
- *
- * @param {Time} [time=now] the time the attack should start
- * @param {NormalRange} [velocity=1] the velocity of the note (0-1)
- * @returns {Tone.DuoSynth} this
- * @private
- */
- Tone.DuoSynth.prototype._triggerEnvelopeAttack = function (time, velocity) {
- time = this.toSeconds(time);
- this.voice0.envelope.triggerAttack(time, velocity);
- this.voice1.envelope.triggerAttack(time, velocity);
- this.voice0.filterEnvelope.triggerAttack(time);
- this.voice1.filterEnvelope.triggerAttack(time);
- return this;
- };
- /**
- * start the release portion of the envelopes
- *
- * @param {Time} [time=now] the time the release should start
- * @returns {Tone.DuoSynth} this
- * @private
- */
- Tone.DuoSynth.prototype._triggerEnvelopeRelease = function (time) {
- this.voice0.triggerRelease(time);
- this.voice1.triggerRelease(time);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.DuoSynth} this
- */
- Tone.DuoSynth.prototype.dispose = function () {
- Tone.Monophonic.prototype.dispose.call(this);
- this._writable([
- 'voice0',
- 'voice1',
- 'frequency',
- 'vibratoAmount',
- 'vibratoRate'
- ]);
- this.voice0.dispose();
- this.voice0 = null;
- this.voice1.dispose();
- this.voice1 = null;
- this.frequency.dispose();
- this.frequency = null;
- this._vibratoGain.dispose();
- this._vibratoGain = null;
- this._vibrato = null;
- this.harmonicity.dispose();
- this.harmonicity = null;
- this.vibratoAmount.dispose();
- this.vibratoAmount = null;
- this.vibratoRate = null;
- return this;
- };
- return Tone.DuoSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class FMSynth is composed of two Tone.Synths where one Tone.Synth modulates
- * the frequency of a second Tone.Synth. A lot of spectral content
- * can be explored using the modulationIndex parameter. Read more about
- * frequency modulation synthesis on Sound On Sound: [Part 1](https://web.archive.org/web/20160403123704/http://www.soundonsound.com/sos/apr00/articles/synthsecrets.htm), [Part 2](https://web.archive.org/web/20160403115835/http://www.soundonsound.com/sos/may00/articles/synth.htm).
- *
- *
- * @constructor
- * @extends {Tone.Monophonic}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var fmSynth = new Tone.FMSynth().toMaster();
- * fmSynth.triggerAttackRelease("C5", "4n");
- */
- Tone.FMSynth = function (options) {
- options = Tone.defaultArg(options, Tone.FMSynth.defaults);
- Tone.Monophonic.call(this, options);
- /**
- * The carrier voice.
- * @type {Tone.Synth}
- * @private
- */
- this._carrier = new Tone.Synth(options.carrier);
- this._carrier.volume.value = -10;
- /**
- * The carrier's oscillator
- * @type {Tone.Oscillator}
- */
- this.oscillator = this._carrier.oscillator;
- /**
- * The carrier's envelope
- * @type {Tone.Oscillator}
- */
- this.envelope = this._carrier.envelope.set(options.envelope);
- /**
- * The modulator voice.
- * @type {Tone.Synth}
- * @private
- */
- this._modulator = new Tone.Synth(options.modulator);
- this._modulator.volume.value = -10;
- /**
- * The modulator's oscillator which is applied
- * to the amplitude of the oscillator
- * @type {Tone.Oscillator}
- */
- this.modulation = this._modulator.oscillator.set(options.modulation);
- /**
- * The modulator's envelope
- * @type {Tone.Oscillator}
- */
- this.modulationEnvelope = this._modulator.envelope.set(options.modulationEnvelope);
- /**
- * The frequency control.
- * @type {Frequency}
- * @signal
- */
- this.frequency = new Tone.Signal(440, Tone.Type.Frequency);
- /**
- * The detune in cents
- * @type {Cents}
- * @signal
- */
- this.detune = new Tone.Signal(options.detune, Tone.Type.Cents);
- /**
- * Harmonicity is the ratio between the two voices. A harmonicity of
- * 1 is no change. Harmonicity = 2 means a change of an octave.
- * @type {Positive}
- * @signal
- * @example
- * //pitch voice1 an octave below voice0
- * synth.harmonicity.value = 0.5;
- */
- this.harmonicity = new Tone.Multiply(options.harmonicity);
- this.harmonicity.units = Tone.Type.Positive;
- /**
- * The modulation index which essentially the depth or amount of the modulation. It is the
- * ratio of the frequency of the modulating signal (mf) to the amplitude of the
- * modulating signal (ma) -- as in ma/mf.
- * @type {Positive}
- * @signal
- */
- this.modulationIndex = new Tone.Multiply(options.modulationIndex);
- this.modulationIndex.units = Tone.Type.Positive;
- /**
- * the node where the modulation happens
- * @type {GainNode}
- * @private
- */
- this._modulationNode = new Tone.Gain(0);
- //control the two voices frequency
- this.frequency.connect(this._carrier.frequency);
- this.frequency.chain(this.harmonicity, this._modulator.frequency);
- this.frequency.chain(this.modulationIndex, this._modulationNode);
- this.detune.fan(this._carrier.detune, this._modulator.detune);
- this._modulator.connect(this._modulationNode.gain);
- this._modulationNode.connect(this._carrier.frequency);
- this._carrier.connect(this.output);
- this._readOnly([
- 'frequency',
- 'harmonicity',
- 'modulationIndex',
- 'oscillator',
- 'envelope',
- 'modulation',
- 'modulationEnvelope',
- 'detune'
- ]);
- };
- Tone.extend(Tone.FMSynth, Tone.Monophonic);
- /**
- * @static
- * @type {Object}
- */
- Tone.FMSynth.defaults = {
- 'harmonicity': 3,
- 'modulationIndex': 10,
- 'detune': 0,
- 'oscillator': { 'type': 'sine' },
- 'envelope': {
- 'attack': 0.01,
- 'decay': 0.01,
- 'sustain': 1,
- 'release': 0.5
- },
- 'modulation': { 'type': 'square' },
- 'modulationEnvelope': {
- 'attack': 0.5,
- 'decay': 0,
- 'sustain': 1,
- 'release': 0.5
- }
- };
- /**
- * trigger the attack portion of the note
- *
- * @param {Time} [time=now] the time the note will occur
- * @param {number} [velocity=1] the velocity of the note
- * @returns {Tone.FMSynth} this
- * @private
- */
- Tone.FMSynth.prototype._triggerEnvelopeAttack = function (time, velocity) {
- time = this.toSeconds(time);
- //the envelopes
- this.envelope.triggerAttack(time, velocity);
- this.modulationEnvelope.triggerAttack(time);
- return this;
- };
- /**
- * trigger the release portion of the note
- *
- * @param {Time} [time=now] the time the note will release
- * @returns {Tone.FMSynth} this
- * @private
- */
- Tone.FMSynth.prototype._triggerEnvelopeRelease = function (time) {
- time = this.toSeconds(time);
- this.envelope.triggerRelease(time);
- this.modulationEnvelope.triggerRelease(time);
- return this;
- };
- /**
- * clean up
- * @returns {Tone.FMSynth} this
- */
- Tone.FMSynth.prototype.dispose = function () {
- Tone.Monophonic.prototype.dispose.call(this);
- this._writable([
- 'frequency',
- 'harmonicity',
- 'modulationIndex',
- 'oscillator',
- 'envelope',
- 'modulation',
- 'modulationEnvelope',
- 'detune'
- ]);
- this._carrier.dispose();
- this._carrier = null;
- this._modulator.dispose();
- this._modulator = null;
- this.frequency.dispose();
- this.frequency = null;
- this.detune.dispose();
- this.detune = null;
- this.modulationIndex.dispose();
- this.modulationIndex = null;
- this.harmonicity.dispose();
- this.harmonicity = null;
- this._modulationNode.dispose();
- this._modulationNode = null;
- this.oscillator = null;
- this.envelope = null;
- this.modulationEnvelope = null;
- this.modulation = null;
- return this;
- };
- return Tone.FMSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.MembraneSynth makes kick and tom sounds using a single oscillator
- * with an amplitude envelope and frequency ramp. A Tone.OmniOscillator
- * is routed through a Tone.AmplitudeEnvelope to the output. The drum
- * quality of the sound comes from the frequency envelope applied
- * during during Tone.MembraneSynth.triggerAttack(note). The frequency
- * envelope starts at note * .octaves
and ramps to
- * note
over the duration of .pitchDecay
.
- *
- * @constructor
- * @extends {Tone.Instrument}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var synth = new Tone.MembraneSynth().toMaster();
- * synth.triggerAttackRelease("C2", "8n");
- */
- Tone.MembraneSynth = function (options) {
- options = Tone.defaultArg(options, Tone.MembraneSynth.defaults);
- Tone.Instrument.call(this, options);
- /**
- * The oscillator.
- * @type {Tone.OmniOscillator}
- */
- this.oscillator = new Tone.OmniOscillator(options.oscillator).start();
- /**
- * The amplitude envelope.
- * @type {Tone.AmplitudeEnvelope}
- */
- this.envelope = new Tone.AmplitudeEnvelope(options.envelope);
- /**
- * The number of octaves the pitch envelope ramps.
- * @type {Positive}
- */
- this.octaves = options.octaves;
- /**
- * The amount of time the frequency envelope takes.
- * @type {Time}
- */
- this.pitchDecay = options.pitchDecay;
- this.oscillator.chain(this.envelope, this.output);
- this._readOnly([
- 'oscillator',
- 'envelope'
- ]);
- };
- Tone.extend(Tone.MembraneSynth, Tone.Instrument);
- /**
- * @static
- * @type {Object}
- */
- Tone.MembraneSynth.defaults = {
- 'pitchDecay': 0.05,
- 'octaves': 10,
- 'oscillator': { 'type': 'sine' },
- 'envelope': {
- 'attack': 0.001,
- 'decay': 0.4,
- 'sustain': 0.01,
- 'release': 1.4,
- 'attackCurve': 'exponential'
- }
- };
- /**
- * Trigger the note at the given time with the given velocity.
- *
- * @param {Frequency} note the note
- * @param {Time} [time=now] the time, if not given is now
- * @param {number} [velocity=1] velocity defaults to 1
- * @returns {Tone.MembraneSynth} this
- * @example
- * kick.triggerAttack(60);
- */
- Tone.MembraneSynth.prototype.triggerAttack = function (note, time, velocity) {
- time = this.toSeconds(time);
- note = this.toFrequency(note);
- var maxNote = note * this.octaves;
- this.oscillator.frequency.setValueAtTime(maxNote, time);
- this.oscillator.frequency.exponentialRampToValueAtTime(note, time + this.toSeconds(this.pitchDecay));
- this.envelope.triggerAttack(time, velocity);
- return this;
- };
- /**
- * Trigger the release portion of the note.
- *
- * @param {Time} [time=now] the time the note will release
- * @returns {Tone.MembraneSynth} this
- */
- Tone.MembraneSynth.prototype.triggerRelease = function (time) {
- this.envelope.triggerRelease(time);
- return this;
- };
- /**
- * Clean up.
- * @returns {Tone.MembraneSynth} this
- */
- Tone.MembraneSynth.prototype.dispose = function () {
- Tone.Instrument.prototype.dispose.call(this);
- this._writable([
- 'oscillator',
- 'envelope'
- ]);
- this.oscillator.dispose();
- this.oscillator = null;
- this.envelope.dispose();
- this.envelope = null;
- return this;
- };
- return Tone.MembraneSynth;
- });
- Module(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,
- 1.483,
- 1.932,
- 2.546,
- 2.63,
- 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](https://web.archive.org/web/20160610143924/https://www.soundonsound.com/sos/jul02/articles/synthsecrets0702.asp).
- *
- * @constructor
- * @extends {Tone.Instrument}
- * @param {Object} [options] The options availble for the synth
- * see defaults below
- */
- Tone.MetalSynth = function (options) {
- options = Tone.defaultArg(options, Tone.MetalSynth.defaults);
- 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': -3.0102999566398125
- }).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': 'linear',
- '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;
- };
- Tone.extend(Tone.MetalSynth, Tone.Instrument);
- /**
- * default values
- * @static
- * @const
- * @type {Object}
- */
- Tone.MetalSynth.defaults = {
- 'frequency': 200,
- 'envelope': {
- 'attack': 0.001,
- '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} [velocity=1] The velocity that the envelope should be triggered at.
- * @return {Tone.MetalSynth} this
- */
- Tone.MetalSynth.prototype.triggerAttack = function (time, vel) {
- time = this.toSeconds(time);
- vel = Tone.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.
- * @return {Tone.MetalSynth} this
- */
- Tone.MetalSynth.prototype.triggerRelease = function (time) {
- 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} [velocity=1] The velocity that the envelope should be triggered at.
- * @return {Tone.MetalSynth} this
- */
- Tone.MetalSynth.prototype.triggerAttackRelease = function (duration, time, velocity) {
- time = this.toSeconds(time);
- duration = this.toSeconds(duration);
- this.triggerAttack(time, velocity);
- this.triggerRelease(time + duration);
- return this;
- };
- /**
- * The modulationIndex of the oscillators which make up the source.
- * see Tone.FMOscillator.modulationIndex
- * @memberOf Tone.MetalSynth#
- * @type {Positive}
- * @name modulationIndex
- */
- Object.defineProperty(Tone.MetalSynth.prototype, 'modulationIndex', {
- 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
- * @memberOf Tone.MetalSynth#
- * @type {Positive}
- * @name harmonicity
- */
- Object.defineProperty(Tone.MetalSynth.prototype, 'harmonicity', {
- 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
- * @memberOf Tone.MetalSynth#
- * @type {Frequency}
- * @name resonance
- */
- Object.defineProperty(Tone.MetalSynth.prototype, 'resonance', {
- 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
- * @memberOf Tone.MetalSynth#
- * @type {Number}
- * @name octaves
- */
- Object.defineProperty(Tone.MetalSynth.prototype, 'octaves', {
- get: function () {
- return this._octaves;
- },
- set: function (octs) {
- this._octaves = octs;
- this._filterFreqScaler.max = this._filterFreqScaler.min * Math.pow(2, octs);
- }
- });
- /**
- * Clean up
- * @returns {Tone.MetalSynth} this
- */
- Tone.MetalSynth.prototype.dispose = function () {
- 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;
- };
- return Tone.MetalSynth;
- });
- Module(function (Tone) {
- /**
- * BufferSource polyfill
- */
- if (window.AudioBufferSourceNode && !AudioBufferSourceNode.prototype.start) {
- AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn;
- AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff;
- }
- /**
- * @class Wrapper around the native BufferSourceNode.
- * @extends {Tone.AudioNode}
- * @param {AudioBuffer|Tone.Buffer} buffer The buffer to play
- * @param {Function} onload The callback to invoke when the
- * buffer is done playing.
- */
- Tone.BufferSource = function () {
- var options = Tone.defaults(arguments, [
- 'buffer',
- 'onload'
- ], Tone.BufferSource);
- Tone.AudioNode.call(this);
- /**
- * The callback to invoke after the
- * buffer source is done playing.
- * @type {Function}
- */
- this.onended = options.onended;
- /**
- * The time that the buffer was started.
- * @type {Number}
- * @private
- */
- this._startTime = -1;
- /**
- * The time that the buffer is scheduled to stop.
- * @type {Number}
- * @private
- */
- this._stopTime = -1;
- /**
- * The gain node which envelopes the BufferSource
- * @type {Tone.Gain}
- * @private
- */
- this._gainNode = this.output = new Tone.Gain();
- /**
- * The buffer source
- * @type {AudioBufferSourceNode}
- * @private
- */
- this._source = this.context.createBufferSource();
- this._source.connect(this._gainNode);
- /**
- * The private buffer instance
- * @type {Tone.Buffer}
- * @private
- */
- this._buffer = new Tone.Buffer(options.buffer, options.onload);
- /**
- * The playbackRate of the buffer
- * @type {Positive}
- * @signal
- */
- this.playbackRate = new Tone.Param(this._source.playbackRate, Tone.Type.Positive);
- /**
- * The fadeIn time of the amplitude envelope.
- * @type {Time}
- */
- this.fadeIn = options.fadeIn;
- /**
- * The fadeOut time of the amplitude envelope.
- * @type {Time}
- */
- this.fadeOut = options.fadeOut;
- /**
- * The value that the buffer ramps to
- * @type {Gain}
- * @private
- */
- this._gain = 1;
- /**
- * The onended timeout
- * @type {Number}
- * @private
- */
- this._onendedTimeout = -1;
- this.loop = options.loop;
- this.loopStart = options.loopStart;
- this.loopEnd = options.loopEnd;
- this.playbackRate.value = options.playbackRate;
- };
- Tone.extend(Tone.BufferSource, Tone.AudioNode);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.BufferSource.defaults = {
- 'onended': Tone.noOp,
- 'onload': Tone.noOp,
- 'loop': false,
- 'loopStart': 0,
- 'loopEnd': 0,
- 'fadeIn': 0,
- 'fadeOut': 0,
- 'playbackRate': 1
- };
- /**
- * Returns the playback state of the source, either "started" or "stopped".
- * @type {Tone.State}
- * @readOnly
- * @memberOf Tone.BufferSource#
- * @name state
- */
- Object.defineProperty(Tone.BufferSource.prototype, 'state', {
- get: function () {
- var now = this.now();
- if (this._startTime !== -1 && now >= this._startTime && now < this._stopTime) {
- return Tone.State.Started;
- } else {
- return Tone.State.Stopped;
- }
- }
- });
- /**
- * Start the buffer
- * @param {Time} [startTime=now] When the player should start.
- * @param {Time} [offset=0] The offset from the beginning of the sample
- * to start at.
- * @param {Time=} duration How long the sample should play. If no duration
- * is given, it will default to the full length
- * of the sample (minus any offset)
- * @param {Gain} [gain=1] The gain to play the buffer back at.
- * @param {Time=} fadeInTime The optional fadeIn ramp time.
- * @return {Tone.BufferSource} this
- */
- Tone.BufferSource.prototype.start = function (time, offset, duration, gain, fadeInTime) {
- if (this._startTime !== -1) {
- throw new Error('Tone.BufferSource can only be started once.');
- }
- if (this.buffer.loaded) {
- time = this.toSeconds(time);
- //if it's a loop the default offset is the loopstart point
- if (this.loop) {
- offset = Tone.defaultArg(offset, this.loopStart);
- } else {
- //otherwise the default offset is 0
- offset = Tone.defaultArg(offset, 0);
- }
- offset = this.toSeconds(offset);
- //the values in seconds
- time = this.toSeconds(time);
- gain = Tone.defaultArg(gain, 1);
- this._gain = gain;
- //the fadeIn time
- if (Tone.isUndef(fadeInTime)) {
- fadeInTime = this.toSeconds(this.fadeIn);
- } else {
- fadeInTime = this.toSeconds(fadeInTime);
- }
- if (fadeInTime > 0) {
- this._gainNode.gain.setValueAtTime(0, time);
- this._gainNode.gain.linearRampToValueAtTime(this._gain, time + fadeInTime);
- } else {
- this._gainNode.gain.setValueAtTime(gain, time);
- }
- this._startTime = time + fadeInTime;
- var computedDur = Tone.defaultArg(duration, this.buffer.duration - offset);
- computedDur = this.toSeconds(computedDur);
- computedDur = Math.max(computedDur, 0);
- if (!this.loop || this.loop && !Tone.isUndef(duration)) {
- //clip the duration when not looping
- if (!this.loop) {
- computedDur = Math.min(computedDur, this.buffer.duration - offset);
- }
- this.stop(time + computedDur + fadeInTime, this.fadeOut);
- }
- //start the buffer source
- if (this.loop) {
- //modify the offset if it's greater than the loop time
- var loopEnd = this.loopEnd || this.buffer.duration;
- var loopStart = this.loopStart;
- var loopDuration = loopEnd - loopStart;
- //move the offset back
- if (offset > loopEnd) {
- offset = (offset - loopStart) % loopDuration + loopStart;
- }
- }
- this._source.buffer = this.buffer.get();
- this._source.loopEnd = this.loopEnd || this.buffer.duration;
- this._source.start(time, offset);
- } else {
- throw new Error('Tone.BufferSource: buffer is either not set or not loaded.');
- }
- return this;
- };
- /**
- * Stop the buffer. Optionally add a ramp time to fade the
- * buffer out.
- * @param {Time=} time The time the buffer should stop.
- * @param {Time=} fadeOutTime How long the gain should fade out for
- * @return {Tone.BufferSource} this
- */
- Tone.BufferSource.prototype.stop = function (time, fadeOutTime) {
- if (this.buffer.loaded) {
- time = this.toSeconds(time);
- //the fadeOut time
- if (Tone.isUndef(fadeOutTime)) {
- fadeOutTime = this.toSeconds(this.fadeOut);
- } else {
- fadeOutTime = this.toSeconds(fadeOutTime);
- }
- //only stop if the last stop was scheduled later
- if (this._stopTime === -1 || this._stopTime > time) {
- this._stopTime = time;
- //cancel the end curve
- this._gainNode.gain.cancelScheduledValues(this._startTime + this.sampleTime);
- time = Math.max(this._startTime, time);
- //set a new one
- if (fadeOutTime > 0) {
- var startFade = Math.max(this._startTime, time - fadeOutTime);
- this._gainNode.gain.setValueAtTime(this._gain, startFade);
- this._gainNode.gain.linearRampToValueAtTime(0, time);
- } else {
- this._gainNode.gain.setValueAtTime(0, time);
- }
- Tone.context.clearTimeout(this._onendedTimeout);
- this._onendedTimeout = Tone.context.setTimeout(this._onended.bind(this), this._stopTime - this.now());
- }
- } else {
- throw new Error('Tone.BufferSource: buffer is either not set or not loaded.');
- }
- return this;
- };
- /**
- * Internal callback when the buffer is ended.
- * Invokes `onended` and disposes the node.
- * @private
- */
- Tone.BufferSource.prototype._onended = function () {
- this.onended(this);
- };
- /**
- * If loop is true, the loop will start at this position.
- * @memberOf Tone.BufferSource#
- * @type {Time}
- * @name loopStart
- */
- Object.defineProperty(Tone.BufferSource.prototype, 'loopStart', {
- get: function () {
- return this._source.loopStart;
- },
- set: function (loopStart) {
- this._source.loopStart = this.toSeconds(loopStart);
- }
- });
- /**
- * If loop is true, the loop will end at this position.
- * @memberOf Tone.BufferSource#
- * @type {Time}
- * @name loopEnd
- */
- Object.defineProperty(Tone.BufferSource.prototype, 'loopEnd', {
- get: function () {
- return this._source.loopEnd;
- },
- set: function (loopEnd) {
- this._source.loopEnd = this.toSeconds(loopEnd);
- }
- });
- /**
- * The audio buffer belonging to the player.
- * @memberOf Tone.BufferSource#
- * @type {Tone.Buffer}
- * @name buffer
- */
- Object.defineProperty(Tone.BufferSource.prototype, 'buffer', {
- get: function () {
- return this._buffer;
- },
- set: function (buffer) {
- this._buffer.set(buffer);
- }
- });
- /**
- * If the buffer should loop once it's over.
- * @memberOf Tone.BufferSource#
- * @type {Boolean}
- * @name loop
- */
- Object.defineProperty(Tone.BufferSource.prototype, 'loop', {
- get: function () {
- return this._source.loop;
- },
- set: function (loop) {
- this._source.loop = loop;
- }
- });
- /**
- * Clean up.
- * @return {Tone.BufferSource} this
- */
- Tone.BufferSource.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this.onended = null;
- this._source.disconnect();
- this._source = null;
- this._gainNode.dispose();
- this._gainNode = null;
- this._buffer.dispose();
- this._buffer = null;
- this._startTime = -1;
- this.playbackRate = null;
- Tone.context.clearTimeout(this._onendedTimeout);
- return this;
- };
- return Tone.BufferSource;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Noise is a noise generator. It uses looped noise buffers to save on performance.
- * Tone.Noise supports the noise types: "pink", "white", and "brown". Read more about
- * colors of noise on [Wikipedia](https://en.wikipedia.org/wiki/Colors_of_noise).
- *
- * @constructor
- * @extends {Tone.Source}
- * @param {string} type the noise type (white|pink|brown)
- * @example
- * //initialize the noise and start
- * var noise = new Tone.Noise("pink").start();
- *
- * //make an autofilter to shape the noise
- * var autoFilter = new Tone.AutoFilter({
- * "frequency" : "8m",
- * "min" : 800,
- * "max" : 15000
- * }).connect(Tone.Master);
- *
- * //connect the noise
- * noise.connect(autoFilter);
- * //start the autofilter LFO
- * autoFilter.start()
- */
- Tone.Noise = function () {
- var options = Tone.defaults(arguments, ['type'], Tone.Noise);
- Tone.Source.call(this, options);
- /**
- * @private
- * @type {AudioBufferSourceNode}
- */
- this._source = null;
- /**
- * the buffer
- * @private
- * @type {AudioBuffer}
- */
- this._type = options.type;
- /**
- * The playback rate of the noise. Affects
- * the "frequency" of the noise.
- * @type {Positive}
- * @signal
- */
- this._playbackRate = options.playbackRate;
- };
- Tone.extend(Tone.Noise, Tone.Source);
- /**
- * the default parameters
- *
- * @static
- * @const
- * @type {Object}
- */
- Tone.Noise.defaults = {
- 'type': 'white',
- 'playbackRate': 1
- };
- /**
- * The type of the noise. Can be "white", "brown", or "pink".
- * @memberOf Tone.Noise#
- * @type {string}
- * @name type
- * @example
- * noise.type = "white";
- */
- Object.defineProperty(Tone.Noise.prototype, 'type', {
- get: function () {
- return this._type;
- },
- set: function (type) {
- if (this._type !== type) {
- if (type in _noiseBuffers) {
- this._type = type;
- //if it's playing, stop and restart it
- if (this.state === Tone.State.Started) {
- var now = this.now();
- this._stop(now);
- this._start(now);
- }
- } else {
- throw new TypeError('Tone.Noise: invalid type: ' + type);
- }
- }
- }
- });
- /**
- * The playback rate of the noise. Affects
- * the "frequency" of the noise.
- * @type {Positive}
- * @signal
- */
- Object.defineProperty(Tone.Noise.prototype, 'playbackRate', {
- get: function () {
- return this._playbackRate;
- },
- set: function (rate) {
- this._playbackRate = rate;
- if (this._source) {
- this._source.playbackRate.value = rate;
- }
- }
- });
- /**
- * internal start method
- *
- * @param {Time} time
- * @private
- */
- Tone.Noise.prototype._start = function (time) {
- var buffer = _noiseBuffers[this._type];
- this._source = new Tone.BufferSource(buffer).connect(this.output);
- this._source.loop = true;
- this._source.playbackRate.value = this._playbackRate;
- this._source.start(this.toSeconds(time), Math.random() * (buffer.duration - 0.001));
- };
- /**
- * internal stop method
- *
- * @param {Time} time
- * @private
- */
- Tone.Noise.prototype._stop = function (time) {
- if (this._source) {
- this._source.stop(this.toSeconds(time));
- this._source = null;
- }
- };
- /**
- * Clean up.
- * @returns {Tone.Noise} this
- */
- Tone.Noise.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- if (this._source !== null) {
- this._source.disconnect();
- this._source = null;
- }
- this._buffer = null;
- return this;
- };
- ///////////////////////////////////////////////////////////////////////////
- // THE BUFFERS
- ///////////////////////////////////////////////////////////////////////////
- //Noise buffer stats
- var bufferLength = 44100 * 5;
- var channels = 2;
- /**
- * The noise arrays. Generated on initialization.
- * borrowed heavily from https://github.com/zacharydenton/noise.js
- * (c) 2013 Zach Denton (MIT)
- * @static
- * @private
- * @type {Array}
- */
- var _noiseArrays = {
- 'pink': function () {
- var buffer = [];
- for (var channelNum = 0; channelNum < channels; channelNum++) {
- var channel = new Float32Array(bufferLength);
- buffer[channelNum] = channel;
- var b0, b1, b2, b3, b4, b5, b6;
- b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0;
- for (var i = 0; i < bufferLength; i++) {
- var white = Math.random() * 2 - 1;
- b0 = 0.99886 * b0 + white * 0.0555179;
- b1 = 0.99332 * b1 + white * 0.0750759;
- b2 = 0.969 * b2 + white * 0.153852;
- b3 = 0.8665 * b3 + white * 0.3104856;
- b4 = 0.55 * b4 + white * 0.5329522;
- b5 = -0.7616 * b5 - white * 0.016898;
- channel[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
- channel[i] *= 0.11;
- // (roughly) compensate for gain
- b6 = white * 0.115926;
- }
- }
- return buffer;
- }(),
- 'brown': function () {
- var buffer = [];
- for (var channelNum = 0; channelNum < channels; channelNum++) {
- var channel = new Float32Array(bufferLength);
- buffer[channelNum] = channel;
- var lastOut = 0;
- for (var i = 0; i < bufferLength; i++) {
- var white = Math.random() * 2 - 1;
- channel[i] = (lastOut + 0.02 * white) / 1.02;
- lastOut = channel[i];
- channel[i] *= 3.5; // (roughly) compensate for gain
- }
- }
- return buffer;
- }(),
- 'white': function () {
- var buffer = [];
- for (var channelNum = 0; channelNum < channels; channelNum++) {
- var channel = new Float32Array(bufferLength);
- buffer[channelNum] = channel;
- for (var i = 0; i < bufferLength; i++) {
- channel[i] = Math.random() * 2 - 1;
- }
- }
- return buffer;
- }()
- };
- /**
- * static noise buffers
- * @static
- * @private
- * @type {Tone.Buffer}
- */
- var _noiseBuffers = {};
- //create the Tone.Buffers
- function createBuffers() {
- for (var type in _noiseArrays) {
- _noiseBuffers[type] = new Tone.Buffer().fromArray(_noiseArrays[type]);
- }
- }
- //create the noise buffers
- Tone.getContext(createBuffers);
- Tone.Context.on('init', createBuffers);
- return Tone.Noise;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.NoiseSynth is composed of a noise generator (Tone.Noise), one filter (Tone.Filter),
- * and two envelopes (Tone.Envelop). One envelope controls the amplitude
- * of the noise and the other is controls the cutoff frequency of the filter.
- *
- *
- * @constructor
- * @extends {Tone.Instrument}
- * @param {Object} [options] the options available for the synth
- * see defaults below
- * @example
- * var noiseSynth = new Tone.NoiseSynth().toMaster();
- * noiseSynth.triggerAttackRelease("8n");
- */
- Tone.NoiseSynth = function (options) {
- //get the defaults
- options = Tone.defaultArg(options, Tone.NoiseSynth.defaults);
- Tone.Instrument.call(this, options);
- /**
- * The noise source.
- * @type {Tone.Noise}
- * @example
- * noiseSynth.set("noise.type", "brown");
- */
- this.noise = new Tone.Noise();
- /**
- * The amplitude envelope.
- * @type {Tone.AmplitudeEnvelope}
- */
- this.envelope = new Tone.AmplitudeEnvelope(options.envelope);
- //connect the noise to the output
- this.noise.chain(this.envelope, this.output);
- //start the noise
- this.noise.start();
- this._readOnly([
- 'noise',
- 'envelope'
- ]);
- };
- Tone.extend(Tone.NoiseSynth, Tone.Instrument);
- /**
- * @const
- * @static
- * @type {Object}
- */
- Tone.NoiseSynth.defaults = {
- 'noise': { 'type': 'white' },
- 'envelope': {
- 'attack': 0.005,
- 'decay': 0.1,
- 'sustain': 0
- }
- };
- /**
- * Start the attack portion of the envelopes. Unlike other
- * instruments, Tone.NoiseSynth doesn't have a note.
- * @param {Time} [time=now] the time the attack should start
- * @param {number} [velocity=1] the velocity of the note (0-1)
- * @returns {Tone.NoiseSynth} this
- * @example
- * noiseSynth.triggerAttack();
- */
- Tone.NoiseSynth.prototype.triggerAttack = function (time, velocity) {
- //the envelopes
- this.envelope.triggerAttack(time, velocity);
- return this;
- };
- /**
- * Start the release portion of the envelopes.
- * @param {Time} [time=now] the time the release should start
- * @returns {Tone.NoiseSynth} this
- */
- Tone.NoiseSynth.prototype.triggerRelease = function (time) {
- this.envelope.triggerRelease(time);
- return this;
- };
- /**
- * Trigger the attack and then the release.
- * @param {Time} duration the duration of the note
- * @param {Time} [time=now] the time of the attack
- * @param {number} [velocity=1] the velocity
- * @returns {Tone.NoiseSynth} this
- */
- Tone.NoiseSynth.prototype.triggerAttackRelease = function (duration, time, velocity) {
- time = this.toSeconds(time);
- duration = this.toSeconds(duration);
- this.triggerAttack(time, velocity);
- this.triggerRelease(time + duration);
- return this;
- };
- /**
- * Clean up.
- * @returns {Tone.NoiseSynth} this
- */
- Tone.NoiseSynth.prototype.dispose = function () {
- Tone.Instrument.prototype.dispose.call(this);
- this._writable([
- 'noise',
- 'envelope'
- ]);
- this.noise.dispose();
- this.noise = null;
- this.envelope.dispose();
- this.envelope = null;
- return this;
- };
- return Tone.NoiseSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class Karplus-String string synthesis. Often out of tune.
- * Will change when the AudioWorkerNode is available across
- * browsers.
- *
- * @constructor
- * @extends {Tone.Instrument}
- * @param {Object} [options] see the defaults
- * @example
- * var plucky = new Tone.PluckSynth().toMaster();
- * plucky.triggerAttack("C4");
- */
- Tone.PluckSynth = function (options) {
- options = Tone.defaultArg(options, Tone.PluckSynth.defaults);
- Tone.Instrument.call(this, options);
- /**
- * @type {Tone.Noise}
- * @private
- */
- this._noise = new Tone.Noise('pink');
- /**
- * The amount of noise at the attack.
- * Nominal range of [0.1, 20]
- * @type {number}
- */
- this.attackNoise = options.attackNoise;
- /**
- * the LFCF
- * @type {Tone.LowpassCombFilter}
- * @private
- */
- this._lfcf = new Tone.LowpassCombFilter({
- 'resonance': options.resonance,
- 'dampening': options.dampening
- });
- /**
- * The resonance control.
- * @type {NormalRange}
- * @signal
- */
- this.resonance = this._lfcf.resonance;
- /**
- * The dampening control. i.e. the lowpass filter frequency of the comb filter
- * @type {Frequency}
- * @signal
- */
- this.dampening = this._lfcf.dampening;
- //connections
- this._noise.connect(this._lfcf);
- this._lfcf.connect(this.output);
- this._readOnly([
- 'resonance',
- 'dampening'
- ]);
- };
- Tone.extend(Tone.PluckSynth, Tone.Instrument);
- /**
- * @static
- * @const
- * @type {Object}
- */
- Tone.PluckSynth.defaults = {
- 'attackNoise': 1,
- 'dampening': 4000,
- 'resonance': 0.9
- };
- /**
- * Trigger the note.
- * @param {Frequency} note The note to trigger.
- * @param {Time} [time=now] When the note should be triggered.
- * @returns {Tone.PluckSynth} this
- */
- Tone.PluckSynth.prototype.triggerAttack = function (note, time) {
- note = this.toFrequency(note);
- time = this.toSeconds(time);
- var delayAmount = 1 / note;
- this._lfcf.delayTime.setValueAtTime(delayAmount, time);
- this._noise.start(time);
- this._noise.stop(time + delayAmount * this.attackNoise);
- return this;
- };
- /**
- * Clean up.
- * @returns {Tone.PluckSynth} this
- */
- Tone.PluckSynth.prototype.dispose = function () {
- Tone.Instrument.prototype.dispose.call(this);
- this._noise.dispose();
- this._lfcf.dispose();
- this._noise = null;
- this._lfcf = null;
- this._writable([
- 'resonance',
- 'dampening'
- ]);
- this.dampening = null;
- this.resonance = null;
- return this;
- };
- return Tone.PluckSynth;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.PolySynth handles voice creation and allocation for any
- * 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.
- *
- * @constructor
- * @extends {Tone.Instrument}
- * @param {number|Object} [polyphony=4] The number of voices to create
- * @param {function} [voice=Tone.Synth] The constructor of the voices
- * uses Tone.Synth by default.
- * @example
- * //a polysynth composed of 6 Voices of Synth
- * var synth = new Tone.PolySynth(6, Tone.Synth).toMaster();
- * //set the attributes using the set interface
- * synth.set("detune", -1200);
- * //play a chord
- * synth.triggerAttackRelease(["C4", "E4", "A4"], "4n");
- */
- Tone.PolySynth = function () {
- var options = Tone.defaults(arguments, [
- 'polyphony',
- 'voice'
- ], Tone.PolySynth);
- Tone.Instrument.call(this, options);
- options = Tone.defaultArg(options, Tone.Instrument.defaults);
- //max polyphony
- options.polyphony = Math.min(Tone.PolySynth.MAX_POLYPHONY, options.polyphony);
- /**
- * the array of voices
- * @type {Array}
- */
- this.voices = new Array(options.polyphony);
- /**
- * The queue of voices with data about last trigger
- * and the triggered note
- * @private
- * @type {Array}
- */
- this._triggers = new Array(options.polyphony);
- /**
- * The detune in cents
- * @type {Cents}
- * @signal
- */
- this.detune = new Tone.Signal(options.detune, Tone.Type.Cents);
- this._readOnly('detune');
- //create the voices
- for (var i = 0; i < options.polyphony; i++) {
- var v = new options.voice(arguments[2], arguments[3]);
- this.voices[i] = v;
- v.connect(this.output);
- if (v.hasOwnProperty('detune')) {
- this.detune.connect(v.detune);
- }
- this._triggers[i] = {
- release: -1,
- note: null,
- voice: v
- };
- }
- };
- Tone.extend(Tone.PolySynth, Tone.Instrument);
- /**
- * the defaults
- * @const
- * @static
- * @type {Object}
- */
- Tone.PolySynth.defaults = {
- 'polyphony': 4,
- 'volume': 0,
- 'detune': 0,
- 'voice': Tone.Synth
- };
- /**
- * 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.
- * @returns {Tone.PolySynth} this
- * @example
- * //trigger a chord immediately with a velocity of 0.2
- * poly.triggerAttack(["Ab3", "C4", "F5"], undefined, 0.2);
- */
- Tone.PolySynth.prototype.triggerAttack = function (notes, time, velocity) {
- if (!Array.isArray(notes)) {
- notes = [notes];
- }
- time = this.toSeconds(time);
- for (var i = 0; i < notes.length; i++) {
- var val = notes[i];
- //trigger the oldest voice
- var oldest = this._triggers[0];
- var oldestIndex = 0;
- for (var j = 1; j < this._triggers.length; j++) {
- if (this._triggers[j].release < oldest.release) {
- oldest = this._triggers[j];
- oldestIndex = j;
- }
- }
- oldest.release = Infinity;
- oldest.note = JSON.stringify(val);
- oldest.voice.triggerAttack(val, time, velocity);
- }
- return this;
- };
- /**
- * Trigger the attack and release after the specified duration
- *
- * @param {Frequency|Array} notes The notes to play. Accepts a single
- * Frequency or an array of frequencies.
- * @param {Time} duration the duration of the note
- * @param {Time} [time=now] if no time is given, defaults to now
- * @param {number} [velocity=1] the velocity of the attack (0-1)
- * @returns {Tone.PolySynth} this
- * @example
- * //trigger a chord for a duration of a half note
- * poly.triggerAttackRelease(["Eb3", "G4", "C5"], "2n");
- * @example
- * //can pass in an array of durations as well
- * poly.triggerAttackRelease(["Eb3", "G4", "C5"], ["2n", "4n", "4n"]);
- */
- Tone.PolySynth.prototype.triggerAttackRelease = function (notes, duration, time, velocity) {
- time = this.toSeconds(time);
- this.triggerAttack(notes, time, velocity);
- if (Tone.isArray(duration) && Tone.isArray(notes)) {
- for (var i = 0; i < notes.length; i++) {
- var d = duration[Math.min(i, duration.length - 1)];
- this.triggerRelease(notes[i], time + this.toSeconds(d));
- }
- } else {
- this.triggerRelease(notes, time + this.toSeconds(duration));
- }
- return this;
- };
- /**
- * Trigger the release of the note. Unlike monophonic instruments,
- * 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.
- * @param {Time} [time=now] When the release will be triggered.
- * @returns {Tone.PolySynth} this
- * @example
- * poly.triggerRelease(["Ab3", "C4", "F5"], "+2n");
- */
- Tone.PolySynth.prototype.triggerRelease = function (notes, time) {
- if (!Array.isArray(notes)) {
- notes = [notes];
- }
- time = this.toSeconds(time);
- for (var i = 0; i < notes.length; i++) {
- //get the voice
- var stringified = JSON.stringify(notes[i]);
- 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;
- }
- }
- }
- return this;
- };
- /**
- * Set a member/attribute of the voices.
- * @param {Object|string} params
- * @param {number=} value
- * @param {Time=} rampTime
- * @returns {Tone.PolySynth} this
- * @example
- * poly.set({
- * "filter" : {
- * "type" : "highpass"
- * },
- * "envelope" : {
- * "attack" : 0.25
- * }
- * });
- */
- Tone.PolySynth.prototype.set = function (params, value, rampTime) {
- for (var i = 0; i < this.voices.length; i++) {
- this.voices[i].set(params, value, rampTime);
- }
- return this;
- };
- /**
- * 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.
- * @param {Array=} params the parameters to get, otherwise will return
- * all available.
- */
- Tone.PolySynth.prototype.get = function (params) {
- return this.voices[0].get(params);
- };
- /**
- * 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) {
- 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);
- }
- }
- return this;
- };
- /**
- * Clean up.
- * @returns {Tone.PolySynth} this
- */
- Tone.PolySynth.prototype.dispose = function () {
- Tone.Instrument.prototype.dispose.call(this);
- for (var i = 0; i < this.voices.length; i++) {
- this.voices[i].dispose();
- this.voices[i] = null;
- }
- this._writable('detune');
- this.detune.dispose();
- this.detune = null;
- this.voices = null;
- this._triggers = null;
- return this;
- };
- /**
- * The maximum number of notes that can be allocated
- * to a polysynth.
- * @type {Number}
- * @static
- */
- Tone.PolySynth.MAX_POLYPHONY = 20;
- return Tone.PolySynth;
- });
- Module(function (Tone) {
- /**
- * @class Automatically interpolates between a set of pitched samples. Pass in an object which maps the note's pitch or midi value to the url, then you can trigger the attack and release of that note like other instruments. By automatically repitching the samples, it is possible to play pitches which were not explicitly included which can save loading time.
- * For sample or buffer playback where repitching is not necessary, use [Tone.Player](https://tonejs.github.io/docs/Player).
- * @param {Object} samples An object of samples mapping either Midi
- * Note Numbers or Scientific Pitch Notation
- * to the url of that sample.
- * @example
- * var sampler = new Tone.Sampler({
- * "C3" : "path/to/C3.mp3",
- * "D#3" : "path/to/Dsharp3.mp3",
- * "F#3" : "path/to/Fsharp3.mp3",
- * "A3" : "path/to/A3.mp3",
- * }, function(){
- * //sampler will repitch the closest sample
- * sampler.triggerAttack("D3")
- * })
- * @extends {Tone.Instrument}
- */
- Tone.Sampler = function (urls) {
- // shift arguments over one. Those are the remainder of the options
- var args = Array.prototype.slice.call(arguments);
- args.shift();
- var options = Tone.defaults(args, [
- 'onload',
- 'baseUrl'
- ], Tone.Sampler);
- Tone.Instrument.call(this, options);
- var urlMap = {};
- for (var note in urls) {
- if (Tone.isNote(note)) {
- //convert the note name to MIDI
- var mid = Tone.Frequency(note).toMidi();
- urlMap[mid] = urls[note];
- } else if (!isNaN(parseFloat(note))) {
- //otherwise if it's numbers assume it's midi
- urlMap[note] = urls[note];
- } else {
- throw new Error('Tone.Sampler: url keys must be the note\'s pitch');
- }
- }
- /**
- * The stored and loaded buffers
- * @type {Tone.Buffers}
- * @private
- */
- this._buffers = new Tone.Buffers(urlMap, options.onload, options.baseUrl);
- /**
- * The object of all currently playing BufferSources
- * @type {Object}
- * @private
- */
- this._activeSources = {};
- /**
- * The envelope applied to the beginning of the sample.
- * @type {Time}
- */
- this.attack = options.attack;
- /**
- * The envelope applied to the end of the envelope.
- * @type {Time}
- */
- this.release = options.release;
- };
- Tone.extend(Tone.Sampler, Tone.Instrument);
- /**
- * The defaults
- * @const
- * @type {Object}
- */
- Tone.Sampler.defaults = {
- attack: 0,
- release: 0.1,
- onload: Tone.noOp,
- baseUrl: ''
- };
- /**
- * Returns the difference in steps between the given midi note at the closets sample.
- * @param {Midi} midi
- * @return {Interval}
- * @private
- */
- Tone.Sampler.prototype._findClosest = function (midi) {
- var MAX_INTERVAL = 24;
- var interval = 0;
- while (interval < MAX_INTERVAL) {
- // check above and below
- if (this._buffers.has(midi + interval)) {
- return -interval;
- } else if (this._buffers.has(midi - interval)) {
- return interval;
- }
- interval++;
- }
- return null;
- };
- /**
- * @param {Frequency} note The note to play
- * @param {Time=} time When to play the note
- * @param {NormalRange=} velocity The velocity to play the sample back.
- * @return {Tone.Sampler} this
- */
- Tone.Sampler.prototype.triggerAttack = function (note, time, velocity) {
- var midi = Tone.Frequency(note).toMidi();
- // find the closest note pitch
- var difference = this._findClosest(midi);
- if (difference !== null) {
- var closestNote = midi - difference;
- var buffer = this._buffers.get(closestNote);
- // play that note
- var source = new Tone.BufferSource({
- 'buffer': buffer,
- 'playbackRate': Tone.intervalToFrequencyRatio(difference),
- 'fadeIn': this.attack,
- 'fadeOut': this.release
- }).connect(this.output);
- source.start(time, 0, buffer.duration, velocity);
- // add it to the active sources
- if (!Tone.isArray(this._activeSources[midi])) {
- this._activeSources[midi] = [];
- }
- this._activeSources[midi].push({
- note: midi,
- source: source
- });
- }
- return this;
- };
- /**
- * @param {Frequency} note The note to release.
- * @param {Time=} time When to release the note.
- * @return {Tone.Sampler} this
- */
- Tone.Sampler.prototype.triggerRelease = function (note, time) {
- var midi = Tone.Frequency(note).toMidi();
- // find the note
- if (this._activeSources[midi] && this._activeSources[midi].length) {
- var source = this._activeSources[midi].shift().source;
- time = this.toSeconds(time);
- source.stop(time + this.release, this.release);
- }
- };
- /**
- * Invoke the attack phase, then after the duration, invoke the release.
- * @param {Frequency} note The note to play
- * @param {Time} duration The time the note should be held
- * @param {Time=} time When to start the attack
- * @param {NormalRange} [velocity=1] The velocity of the attack
- * @return {Tone.Sampler} this
- */
- Tone.Sampler.prototype.triggerAttackRelease = function (note, duration, time, velocity) {
- time = this.toSeconds(time);
- duration = this.toSeconds(duration);
- this.triggerAttack(note, time, velocity);
- this.triggerRelease(note, time + duration);
- return this;
- };
- /**
- * Add a note to the sampler.
- * @param {Note|Midi} note The buffer's pitch.
- * @param {String|Tone.Buffer|Audiobuffer} url Either the url of the bufer,
- * or a buffer which will be added
- * with the given name.
- * @param {Function=} callback The callback to invoke
- * when the url is loaded.
- */
- Tone.Sampler.prototype.add = function (note, url, callback) {
- if (Tone.isNote(note)) {
- //convert the note name to MIDI
- var mid = Tone.Frequency(note).toMidi();
- this._buffers.add(mid, url, callback);
- } else if (!isNaN(parseFloat(note))) {
- //otherwise if it's numbers assume it's midi
- this._buffers.add(note, url, callback);
- } else {
- throw new Error('Tone.Sampler: note must be the note\'s pitch. Instead got ' + note);
- }
- };
- /**
- * If the buffers are loaded or not
- * @memberOf Tone.Sampler#
- * @type {Boolean}
- * @name loaded
- * @readOnly
- */
- Object.defineProperty(Tone.Sampler.prototype, 'loaded', {
- get: function () {
- return this._buffers.loaded;
- }
- });
- /**
- * Clean up
- * @return {Tone.Sampler} this
- */
- Tone.Sampler.prototype.dispose = function () {
- Tone.Instrument.prototype.dispose.call(this);
- this._buffers.dispose();
- this._buffers = null;
- for (var midi in this._activeSources) {
- this._activeSources[midi].forEach(function (event) {
- event.source.dispose();
- });
- }
- this._activeSources = null;
- return this;
- };
- return Tone.Sampler;
- });
- Module(function (Tone) {
-
- /**
- * @class Maps a NormalRange [0, 1] to an AudioRange [-1, 1].
- * See also Tone.AudioToGain.
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @example
- * var g2a = new Tone.GainToAudio();
- */
- Tone.GainToAudio = function () {
- Tone.SignalBase.call(this);
- /**
- * @type {WaveShaperNode}
- * @private
- */
- this._norm = this.input = this.output = new Tone.WaveShaper(function (x) {
- return Math.abs(x) * 2 - 1;
- });
- };
- Tone.extend(Tone.GainToAudio, Tone.SignalBase);
- /**
- * clean up
- * @returns {Tone.GainToAudio} this
- */
- Tone.GainToAudio.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._norm.dispose();
- this._norm = null;
- return this;
- };
- return Tone.GainToAudio;
- });
- Module(function (Tone) {
-
- /**
- * @class Normalize takes an input min and max and maps it linearly to NormalRange [0,1]
- *
- * @extends {Tone.SignalBase}
- * @constructor
- * @param {number} inputMin the min input value
- * @param {number} inputMax the max input value
- * @example
- * var norm = new Tone.Normalize(2, 4);
- * var sig = new Tone.Signal(3).connect(norm);
- * //output of norm is 0.5.
- */
- Tone.Normalize = function (inputMin, inputMax) {
- Tone.SignalBase.call(this);
- /**
- * the min input value
- * @type {number}
- * @private
- */
- this._inputMin = Tone.defaultArg(inputMin, 0);
- /**
- * the max input value
- * @type {number}
- * @private
- */
- this._inputMax = Tone.defaultArg(inputMax, 1);
- /**
- * subtract the min from the input
- * @type {Tone.Add}
- * @private
- */
- this._sub = this.input = new Tone.Add(0);
- /**
- * divide by the difference between the input and output
- * @type {Tone.Multiply}
- * @private
- */
- this._div = this.output = new Tone.Multiply(1);
- this._sub.connect(this._div);
- this._setRange();
- };
- Tone.extend(Tone.Normalize, Tone.SignalBase);
- /**
- * The minimum value the input signal will reach.
- * @memberOf Tone.Normalize#
- * @type {number}
- * @name min
- */
- Object.defineProperty(Tone.Normalize.prototype, 'min', {
- get: function () {
- return this._inputMin;
- },
- set: function (min) {
- this._inputMin = min;
- this._setRange();
- }
- });
- /**
- * The maximum value the input signal will reach.
- * @memberOf Tone.Normalize#
- * @type {number}
- * @name max
- */
- Object.defineProperty(Tone.Normalize.prototype, 'max', {
- get: function () {
- return this._inputMax;
- },
- set: function (max) {
- this._inputMax = max;
- this._setRange();
- }
- });
- /**
- * set the values
- * @private
- */
- Tone.Normalize.prototype._setRange = function () {
- this._sub.value = -this._inputMin;
- this._div.value = 1 / (this._inputMax - this._inputMin);
- };
- /**
- * clean up
- * @returns {Tone.Normalize} this
- */
- Tone.Normalize.prototype.dispose = function () {
- Tone.SignalBase.prototype.dispose.call(this);
- this._sub.dispose();
- this._sub = null;
- this._div.dispose();
- this._div = null;
- return this;
- };
- return Tone.Normalize;
- });
- Module(function (Tone) {
- /**
- * @class Tone.TransportTimelineSignal extends Tone.TimelineSignal, but adds the ability to synchronize the signal to the signal to the Tone.Transport
- * @extends {Tone.TimelineSignal}
- */
- Tone.TransportTimelineSignal = function () {
- Tone.TimelineSignal.apply(this, arguments);
- /**
- * The real signal output
- * @type {Tone.Signal}
- * @private
- */
- this.output = this._outputSig = new Tone.Signal(this._initial);
- /**
- * Keep track of the last value. (small optimization)
- * @private
- * @type {Number}
- */
- this._lastVal = this.value;
- /**
- * The event id of the tick update loop
- * @private
- * @type {Number}
- */
- this._synced = Tone.Transport.scheduleRepeat(this._onTick.bind(this), '1i');
- /**
- * A bound version of the anchor value methods
- * @type {Function}
- * @private
- */
- this._bindAnchorValue = this._anchorValue.bind(this);
- Tone.Transport.on('start stop pause', this._bindAnchorValue);
- this._events.memory = Infinity;
- };
- Tone.extend(Tone.TransportTimelineSignal, Tone.TimelineSignal);
- /**
- * Callback which is invoked every tick.
- * @private
- * @param {Number} time
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype._onTick = function (time) {
- var val = this.getValueAtTime(Tone.Transport.seconds);
- if (this._lastVal !== val) {
- this._lastVal = val;
- //approximate ramp curves with linear ramps
- this._outputSig.linearRampToValueAtTime(val, time);
- }
- };
- /**
- * Anchor the value at the start and stop of the Transport
- * @param {Number} time The time of the event
- * @return {Tone.TransportTimelineSignal} this
- * @private
- */
- Tone.TransportTimelineSignal.prototype._anchorValue = function (time) {
- var val = this.getValueAtTime(Tone.Transport.ticks);
- this._lastVal = val;
- this._outputSig.cancelScheduledValues(time);
- this._outputSig.setValueAtTime(val, time);
- return this;
- };
- /**
- * Get the scheduled value at the given time. This will
- * return the unconverted (raw) value.
- * @param {TransportTime} time The time in seconds.
- * @return {Number} The scheduled value at the given time.
- */
- Tone.TransportTimelineSignal.prototype.getValueAtTime = function (time) {
- time = this.toTicks(time);
- return Tone.TimelineSignal.prototype.getValueAtTime.call(this, time);
- };
- /**
- * Set the output of the signal at the given time
- * @param {Number} value The value to change to at the given time
- * @param {TransportTime} time The time to change the signal
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.setValueAtTime = function (value, time) {
- time = this.toTicks(time);
- Tone.TimelineSignal.prototype.setValueAtTime.call(this, value, time);
- return this;
- };
- /**
- * Linear ramp to the given value from the previous scheduled point to the given value
- * @param {Number} value The value to change to at the given time
- * @param {TransportTime} time The time to change the signal
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.linearRampToValueAtTime = function (value, time) {
- time = this.toTicks(time);
- Tone.TimelineSignal.prototype.linearRampToValueAtTime.call(this, value, time);
- return this;
- };
- /**
- * Exponential ramp to the given value from the previous scheduled point to the given value
- * @param {Number} value The value to change to at the given time
- * @param {TransportTime} time The time to change the signal
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.exponentialRampToValueAtTime = function (value, time) {
- time = this.toTicks(time);
- Tone.TimelineSignal.prototype.exponentialRampToValueAtTime.call(this, value, time);
- return this;
- };
- /**
- * Start exponentially approaching the target value at the given time with
- * a rate having the given time constant.
- * @param {number} value
- * @param {TransportTime} startTime
- * @param {number} timeConstant
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
- startTime = this.toTicks(startTime);
- Tone.TimelineSignal.prototype.setTargetAtTime.call(this, value, startTime, timeConstant);
- return this;
- };
- /**
- * Cancels all scheduled parameter changes with times greater than or
- * equal to startTime.
- * @param {TransportTime} startTime
- * @returns {Tone.Param} this
- */
- Tone.TransportTimelineSignal.prototype.cancelScheduledValues = function (startTime) {
- startTime = this.toTicks(startTime);
- Tone.TimelineSignal.prototype.cancelScheduledValues.call(this, startTime);
- return this;
- };
- /**
- * Set an array of arbitrary values starting at the given time for the given duration.
- * @param {Float32Array} values
- * @param {Time} startTime
- * @param {Time} duration
- * @param {NormalRange} [scaling=1] If the values in the curve should be scaled by some value
- * @returns {Tone.TimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.setValueCurveAtTime = function (values, startTime, duration, scaling) {
- startTime = this.toTicks(startTime);
- duration = this.toTicks(duration);
- Tone.TimelineSignal.prototype.setValueCurveAtTime.call(this, values, startTime, duration, scaling);
- return this;
- };
- /**
- * Dispose and disconnect
- * @return {Tone.TransportTimelineSignal} this
- */
- Tone.TransportTimelineSignal.prototype.dispose = function () {
- Tone.Transport.clear(this._synced);
- Tone.Transport.off('start stop pause', this._syncedCallback);
- this._events.cancel(0);
- Tone.TimelineSignal.prototype.dispose.call(this);
- this._outputSig.dispose();
- this._outputSig = null;
- };
- return Tone.TransportTimelineSignal;
- });
- Module(function (Tone) {
- /**
- * @class Tone.MultiPlayer is well suited for one-shots, multi-sampled instruments
- * or any time you need to play a bunch of audio buffers.
- *
- * @deprecated Use [Tone.Players](Players) instead.
- * @param {Object|Array|Tone.Buffers} buffers The buffers which are available
- * to the MultiPlayer
- * @param {Function} onload The callback to invoke when all of the buffers are loaded.
- * @extends {Tone}
- * @example
- * var multiPlayer = new MultiPlayer({
- * "kick" : "path/to/kick.mp3",
- * "snare" : "path/to/snare.mp3",
- * }, function(){
- * multiPlayer.start("kick");
- * });
- * @example
- * //can also store the values in an array
- * var multiPlayer = new MultiPlayer(["path/to/kick.mp3", "path/to/snare.mp3"],
- * function(){
- * //if an array is passed in, the samples are referenced to by index
- * multiPlayer.start(1);
- * });
- */
- Tone.MultiPlayer = function (urls) {
- console.warn('Tone.MultiPlayer is deprecated. Use Tone.Players instead.');
- //remove the urls from the options
- if (arguments.length === 1 && !Tone.isUndef(arguments[0]) && !arguments[0].hasOwnProperty('urls')) {
- urls = { 'urls': urls };
- }
- var options = Tone.defaults(arguments, [
- 'urls',
- 'onload'
- ], Tone.MultiPlayer);
- Tone.Source.call(this, options);
- if (options.urls instanceof Tone.Buffers) {
- /**
- * All the buffers belonging to the player.
- * @type {Tone.Buffers}
- */
- this.buffers = options.urls;
- } else {
- this.buffers = new Tone.Buffers(options.urls, options.onload);
- }
- /**
- * Keeps track of the currently playing sources.
- * @type {Object}
- * @private
- */
- this._activeSources = {};
- /**
- * The fade in envelope which is applied
- * to the beginning of the BufferSource
- * @type {Time}
- */
- this.fadeIn = options.fadeIn;
- /**
- * The fade out envelope which is applied
- * to the end of the BufferSource
- * @type {Time}
- */
- this.fadeOut = options.fadeOut;
- };
- Tone.extend(Tone.MultiPlayer, Tone.Source);
- /**
- * The defaults
- * @type {Object}
- */
- Tone.MultiPlayer.defaults = {
- 'onload': Tone.noOp,
- 'fadeIn': 0,
- 'fadeOut': 0
- };
- /**
- * Make the source from the buffername
- * @param {String} bufferName
- * @return {Tone.BufferSource}
- * @private
- */
- Tone.MultiPlayer.prototype._makeSource = function (bufferName) {
- var buffer;
- if (Tone.isString(bufferName) || Tone.isNumber(bufferName)) {
- buffer = this.buffers.get(bufferName).get();
- } else if (bufferName instanceof Tone.Buffer) {
- buffer = bufferName.get();
- } else if (bufferName instanceof AudioBuffer) {
- buffer = bufferName;
- }
- var source = new Tone.BufferSource(buffer).connect(this.output);
- if (!this._activeSources.hasOwnProperty(bufferName)) {
- this._activeSources[bufferName] = [];
- }
- this._activeSources[bufferName].push(source);
- return source;
- };
- /**
- * Start a buffer by name. The `start` method allows a number of options
- * to be passed in such as offset, interval, and gain. This is good for multi-sampled
- * instruments and sound sprites where samples are repitched played back at different velocities.
- * @param {String} bufferName The name of the buffer to start.
- * @param {Time} time When to start the buffer.
- * @param {Time} [offset=0] The offset into the buffer to play from.
- * @param {Time=} duration How long to play the buffer for.
- * @param {Interval} [pitch=0] The interval to repitch the buffer.
- * @param {Gain} [gain=1] The gain to play the sample at.
- * @return {Tone.MultiPlayer} this
- */
- Tone.MultiPlayer.prototype.start = function (bufferName, time, offset, duration, pitch, gain) {
- time = this.toSeconds(time);
- var source = this._makeSource(bufferName);
- source.start(time, offset, duration, Tone.defaultArg(gain, 1), this.fadeIn);
- if (duration) {
- source.stop(time + this.toSeconds(duration), this.fadeOut);
- }
- pitch = Tone.defaultArg(pitch, 0);
- source.playbackRate.value = Tone.intervalToFrequencyRatio(pitch);
- return this;
- };
- /**
- * Start a looping buffer by name. Similar to `start`, but the buffer
- * is looped instead of played straight through. Can still be stopped with `stop`.
- * @param {String} bufferName The name of the buffer to start.
- * @param {Time} time When to start the buffer.
- * @param {Time} [offset=0] The offset into the buffer to play from.
- * @param {Time=} loopStart The start of the loop.
- * @param {Time=} loopEnd The end of the loop.
- * @param {Interval} [pitch=0] The interval to repitch the buffer.
- * @param {Gain} [gain=1] The gain to play the sample at.
- * @return {Tone.MultiPlayer} this
- */
- Tone.MultiPlayer.prototype.startLoop = function (bufferName, time, offset, loopStart, loopEnd, pitch, gain) {
- time = this.toSeconds(time);
- var source = this._makeSource(bufferName);
- source.loop = true;
- source.loopStart = this.toSeconds(Tone.defaultArg(loopStart, 0));
- source.loopEnd = this.toSeconds(Tone.defaultArg(loopEnd, 0));
- source.start(time, offset, undefined, Tone.defaultArg(gain, 1), this.fadeIn);
- pitch = Tone.defaultArg(pitch, 0);
- source.playbackRate.value = Tone.intervalToFrequencyRatio(pitch);
- return this;
- };
- /**
- * Stop the first played instance of the buffer name.
- * @param {String} bufferName The buffer to stop.
- * @param {Time=} time When to stop the buffer
- * @return {Tone.MultiPlayer} this
- */
- Tone.MultiPlayer.prototype.stop = function (bufferName, time) {
- if (this._activeSources[bufferName] && this._activeSources[bufferName].length) {
- time = this.toSeconds(time);
- this._activeSources[bufferName].shift().stop(time, this.fadeOut);
- } else {
- throw new Error('Tone.MultiPlayer: cannot stop a buffer that hasn\'t been started or is already stopped');
- }
- return this;
- };
- /**
- * Stop all currently playing buffers at the given time.
- * @param {Time=} time When to stop the buffers.
- * @return {Tone.MultiPlayer} this
- */
- Tone.MultiPlayer.prototype.stopAll = function (time) {
- time = this.toSeconds(time);
- for (var bufferName in this._activeSources) {
- var sources = this._activeSources[bufferName];
- for (var i = 0; i < sources.length; i++) {
- sources[i].stop(time);
- }
- }
- return this;
- };
- /**
- * Add another buffer to the available buffers.
- * @param {String} name The name to that the buffer is refered
- * to in start/stop methods.
- * @param {String|Tone.Buffer} url The url of the buffer to load
- * or the buffer.
- * @param {Function} callback The function to invoke after the buffer is loaded.
- */
- Tone.MultiPlayer.prototype.add = function (name, url, callback) {
- this.buffers.add(name, url, callback);
- return this;
- };
- /**
- * Returns the playback state of the source. "started"
- * if there are any buffers playing. "stopped" otherwise.
- * @type {Tone.State}
- * @readOnly
- * @memberOf Tone.MultiPlayer#
- * @name state
- */
- Object.defineProperty(Tone.MultiPlayer.prototype, 'state', {
- get: function () {
- return this._activeSources.length > 0 ? Tone.State.Started : Tone.State.Stopped;
- }
- });
- /**
- * Mute the output.
- * @memberOf Tone.MultiPlayer#
- * @type {boolean}
- * @name mute
- * @example
- * //mute the output
- * source.mute = true;
- */
- Object.defineProperty(Tone.MultiPlayer.prototype, 'mute', {
- get: function () {
- return this._volume.mute;
- },
- set: function (mute) {
- this._volume.mute = mute;
- }
- });
- /**
- * Clean up.
- * @return {Tone.MultiPlayer} this
- */
- Tone.MultiPlayer.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- for (var bufferName in this._activeSources) {
- this._activeSources[bufferName].forEach(function (source) {
- source.dispose();
- });
- }
- this.buffers.dispose();
- this.buffers = null;
- this._activeSources = null;
- return this;
- };
- return Tone.MultiPlayer;
- });
- Module(function (Tone) {
- /**
- * @class Tone.GrainPlayer implements [granular synthesis](https://en.wikipedia.org/wiki/Granular_synthesis).
- * Granular Synthesis enables you to adjust pitch and playback rate independently. The grainSize is the
- * amount of time each small chunk of audio is played for and the overlap is the
- * amount of crossfading transition time between successive grains.
- * @extends {Tone.Source}
- * @param {String|Tone.Buffer} url The url to load, or the Tone.Buffer to play.
- * @param {Function=} callback The callback to invoke after the url is loaded.
- */
- Tone.GrainPlayer = function () {
- var options = Tone.defaults(arguments, [
- 'url',
- 'onload'
- ], Tone.GrainPlayer);
- Tone.Source.call(this, options);
- /**
- * The audio buffer belonging to the player.
- * @type {Tone.Buffer}
- */
- this.buffer = new Tone.Buffer(options.url, options.onload);
- /**
- * Create a repeating tick to schedule
- * the grains.
- * @type {Tone.Clock}
- * @private
- */
- this._clock = new Tone.Clock(this._tick.bind(this), options.grainSize);
- /**
- * @type {Number}
- * @private
- */
- this._loopStart = 0;
- /**
- * @type {Number}
- * @private
- */
- this._loopEnd = 0;
- /**
- * All of the currently playing BufferSources
- * @type {Array}
- * @private
- */
- this._activeSources = [];
- /**
- * @type {Number}
- * @private
- */
- this._playbackRate = options.playbackRate;
- /**
- * @type {Number}
- * @private
- */
- this._grainSize = options.grainSize;
- /**
- * @private
- * @type {Number}
- */
- this._overlap = options.overlap;
- /**
- * Adjust the pitch independently of the playbackRate.
- * @type {Cents}
- */
- this.detune = options.detune;
- //setup
- this.overlap = options.overlap;
- this.loop = options.loop;
- this.playbackRate = options.playbackRate;
- this.grainSize = options.grainSize;
- this.loopStart = options.loopStart;
- this.loopEnd = options.loopEnd;
- this.reverse = options.reverse;
- this._clock.on('stop', this._onstop.bind(this));
- };
- Tone.extend(Tone.GrainPlayer, Tone.Source);
- /**
- * the default parameters
- * @static
- * @const
- * @type {Object}
- */
- Tone.GrainPlayer.defaults = {
- 'onload': Tone.noOp,
- 'overlap': 0.1,
- 'grainSize': 0.2,
- 'playbackRate': 1,
- 'detune': 0,
- 'loop': false,
- 'loopStart': 0,
- 'loopEnd': 0,
- 'reverse': false
- };
- /**
- * Play the buffer at the given startTime. Optionally add an offset
- * and/or duration which will play the buffer from a position
- * within the buffer for the given duration.
- *
- * @param {Time} [startTime=now] When the player should start.
- * @param {Time} [offset=0] The offset from the beginning of the sample
- * to start at.
- * @param {Time=} duration How long the sample should play. If no duration
- * is given, it will default to the full length
- * of the sample (minus any offset)
- * @returns {Tone.GrainPlayer} this
- * @memberOf Tone.GrainPlayer#
- * @method start
- * @name start
- */
- /**
- * Internal start method
- * @param {Time} time
- * @param {Time} offset
- * @private
- */
- Tone.GrainPlayer.prototype._start = function (time, offset, duration) {
- offset = Tone.defaultArg(offset, 0);
- offset = this.toSeconds(offset);
- time = this.toSeconds(time);
- this._offset = offset;
- this._clock.start(time);
- if (duration) {
- this.stop(time + this.toSeconds(duration));
- }
- };
- /**
- * Internal start method
- * @param {Time} time
- * @private
- */
- Tone.GrainPlayer.prototype._stop = function (time) {
- this._clock.stop(time);
- };
- /**
- * Invoked when the clock is stopped
- * @param {Number} time
- * @private
- */
- Tone.GrainPlayer.prototype._onstop = function (time) {
- //stop the players
- this._activeSources.forEach(function (source) {
- source.stop(time, 0);
- });
- };
- /**
- * Invoked on each clock tick. scheduled a new
- * grain at this time.
- * @param {Time} time
- * @private
- */
- Tone.GrainPlayer.prototype._tick = function (time) {
- var fadeIn = this._offset < this._overlap ? 0 : this._overlap;
- var source = new Tone.BufferSource({
- 'buffer': this.buffer,
- 'fadeIn': fadeIn,
- 'fadeOut': this._overlap,
- 'loop': this.loop,
- 'loopStart': this._loopStart,
- 'loopEnd': this._loopEnd,
- 'playbackRate': Tone.intervalToFrequencyRatio(this.detune / 100)
- }).connect(this.output);
- source.start(time, this._offset);
- this._offset += this.grainSize;
- source.stop(time + this.grainSize);
- //add it to the active sources
- this._activeSources.push(source);
- //remove it when it's done
- source.onended = function () {
- var index = this._activeSources.indexOf(source);
- if (index !== -1) {
- this._activeSources.splice(index, 1);
- }
- }.bind(this);
- };
- /**
- * Jump to a specific time and play it.
- * @param {Time} offset The offset to jump to.
- * @param {Time=} time When to make the jump.
- * @return {Tone.GrainPlayer} this
- */
- Tone.GrainPlayer.prototype.seek = function (offset, time) {
- this._offset = this.toSeconds(offset);
- this._tick(this.toSeconds(time));
- return this;
- };
- /**
- * The playback rate of the sample
- * @memberOf Tone.GrainPlayer#
- * @type {Positive}
- * @name playbackRate
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'playbackRate', {
- get: function () {
- return this._playbackRate;
- },
- set: function (rate) {
- this._playbackRate = rate;
- this.grainSize = this._grainSize;
- }
- });
- /**
- * The loop start time.
- * @memberOf Tone.GrainPlayer#
- * @type {Time}
- * @name loopStart
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'loopStart', {
- get: function () {
- return this._loopStart;
- },
- set: function (time) {
- this._loopStart = this.toSeconds(time);
- }
- });
- /**
- * The loop end time.
- * @memberOf Tone.GrainPlayer#
- * @type {Time}
- * @name loopEnd
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'loopEnd', {
- get: function () {
- return this._loopEnd;
- },
- set: function (time) {
- this._loopEnd = this.toSeconds(time);
- }
- });
- /**
- * The direction the buffer should play in
- * @memberOf Tone.GrainPlayer#
- * @type {boolean}
- * @name reverse
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'reverse', {
- get: function () {
- return this.buffer.reverse;
- },
- set: function (rev) {
- this.buffer.reverse = rev;
- }
- });
- /**
- * The size of each chunk of audio that the
- * buffer is chopped into and played back at.
- * @memberOf Tone.GrainPlayer#
- * @type {Time}
- * @name grainSize
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'grainSize', {
- get: function () {
- return this._grainSize;
- },
- set: function (size) {
- this._grainSize = this.toSeconds(size);
- this._clock.frequency.value = this._playbackRate / this._grainSize;
- }
- });
- /**
- * This is the duration of the cross-fade between
- * sucessive grains.
- * @memberOf Tone.GrainPlayer#
- * @type {Time}
- * @name overlap
- */
- Object.defineProperty(Tone.GrainPlayer.prototype, 'overlap', {
- get: function () {
- return this._overlap;
- },
- set: function (time) {
- this._overlap = this.toSeconds(time);
- }
- });
- /**
- * Clean up
- * @return {Tone.GrainPlayer} this
- */
- Tone.GrainPlayer.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- this.buffer.dispose();
- this.buffer = null;
- this._clock.dispose();
- this._clock = null;
- this._activeSources.forEach(function (source) {
- source.dispose();
- });
- this._activeSources = null;
- return this;
- };
- return Tone.GrainPlayer;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Player is an audio file player with start, loop, and stop functions.
- *
- * @constructor
- * @extends {Tone.Source}
- * @param {string|AudioBuffer} url Either the AudioBuffer or the url from
- * which to load the AudioBuffer
- * @param {function=} onload The function to invoke when the buffer is loaded.
- * Recommended to use Tone.Buffer.on('load') instead.
- * @example
- * var player = new Tone.Player("./path/to/sample.mp3").toMaster();
- * //play as soon as the buffer is loaded
- * player.autostart = true;
- */
- Tone.Player = function (url) {
- var options;
- if (url instanceof Tone.Buffer) {
- url = url.get();
- options = Tone.Player.defaults;
- } else {
- options = Tone.defaults(arguments, [
- 'url',
- 'onload'
- ], Tone.Player);
- }
- Tone.Source.call(this, options);
- /**
- * @private
- * @type {AudioBufferSourceNode}
- */
- this._source = null;
- /**
- * If the file should play as soon
- * as the buffer is loaded.
- * @type {boolean}
- * @example
- * //will play as soon as it's loaded
- * var player = new Tone.Player({
- * "url" : "./path/to/sample.mp3",
- * "autostart" : true,
- * }).toMaster();
- */
- this.autostart = options.autostart;
- /**
- * the buffer
- * @private
- * @type {Tone.Buffer}
- */
- this._buffer = new Tone.Buffer({
- 'url': options.url,
- 'onload': this._onload.bind(this, options.onload),
- 'reverse': options.reverse
- });
- if (url instanceof AudioBuffer) {
- this._buffer.set(url);
- }
- /**
- * if the buffer should loop once it's over
- * @type {boolean}
- * @private
- */
- this._loop = options.loop;
- /**
- * if 'loop' is true, the loop will start at this position
- * @type {Time}
- * @private
- */
- this._loopStart = options.loopStart;
- /**
- * if 'loop' is true, the loop will end at this position
- * @type {Time}
- * @private
- */
- this._loopEnd = options.loopEnd;
- /**
- * the playback rate
- * @private
- * @type {number}
- */
- this._playbackRate = options.playbackRate;
- /**
- * Enabling retrigger will allow a player to be restarted
- * before the the previous 'start' is done playing. Otherwise,
- * successive calls to Tone.Player.start will only start
- * the sample if it had played all the way through.
- * @type {boolean}
- */
- this.retrigger = options.retrigger;
- /**
- * The fadeIn time of the amplitude envelope.
- * @type {Time}
- */
- this.fadeIn = options.fadeIn;
- /**
- * The fadeOut time of the amplitude envelope.
- * @type {Time}
- */
- this.fadeOut = options.fadeOut;
- };
- Tone.extend(Tone.Player, Tone.Source);
- /**
- * the default parameters
- * @static
- * @const
- * @type {Object}
- */
- Tone.Player.defaults = {
- 'onload': Tone.noOp,
- 'playbackRate': 1,
- 'loop': false,
- 'autostart': false,
- 'loopStart': 0,
- 'loopEnd': 0,
- 'retrigger': false,
- 'reverse': false,
- 'fadeIn': 0,
- 'fadeOut': 0
- };
- /**
- * Load the audio file as an audio buffer.
- * Decodes the audio asynchronously and invokes
- * the callback once the audio buffer loads.
- * Note: this does not need to be called if a url
- * was passed in to the constructor. Only use this
- * if you want to manually load a new url.
- * @param {string} url The url of the buffer to load.
- * Filetype support depends on the
- * browser.
- * @param {function=} callback The function to invoke once
- * the sample is loaded.
- * @returns {Promise}
- */
- Tone.Player.prototype.load = function (url, callback) {
- return this._buffer.load(url, this._onload.bind(this, callback));
- };
- /**
- * Internal callback when the buffer is loaded.
- * @private
- */
- Tone.Player.prototype._onload = function (callback) {
- callback = Tone.defaultArg(callback, Tone.noOp);
- callback(this);
- if (this.autostart) {
- this.start();
- }
- };
- /**
- * Play the buffer at the given startTime. Optionally add an offset
- * and/or duration which will play the buffer from a position
- * within the buffer for the given duration.
- *
- * @param {Time} [startTime=now] When the player should start.
- * @param {Time} [offset=0] The offset from the beginning of the sample
- * to start at.
- * @param {Time=} duration How long the sample should play. If no duration
- * is given, it will default to the full length
- * of the sample (minus any offset)
- * @returns {Tone.Player} this
- * @memberOf Tone.Player#
- * @method start
- * @name start
- */
- /**
- * Internal start method
- * @private
- */
- Tone.Player.prototype._start = function (startTime, offset, duration) {
- //if it's a loop the default offset is the loopstart point
- if (this._loop) {
- offset = Tone.defaultArg(offset, this._loopStart);
- } else {
- //otherwise the default offset is 0
- offset = Tone.defaultArg(offset, 0);
- }
- //compute the values in seconds
- offset = this.toSeconds(offset);
- duration = Tone.defaultArg(duration, Math.max(this._buffer.duration - offset, 0));
- duration = this.toSeconds(duration);
- startTime = this.toSeconds(startTime);
- // //make the source
- this._source = new Tone.BufferSource({
- 'buffer': this._buffer,
- 'loop': this._loop,
- 'loopStart': this._loopStart,
- 'loopEnd': this._loopEnd,
- 'playbackRate': this._playbackRate,
- 'fadeIn': this.fadeIn,
- 'fadeOut': this.fadeOut
- }).connect(this.output);
- //set the looping properties
- if (!this._loop && !this._synced) {
- //if it's not looping, set the state change at the end of the sample
- this._state.setStateAtTime(Tone.State.Stopped, startTime + duration);
- }
- //start it
- if (this._loop) {
- this._source.start(startTime, offset);
- } else {
- this._source.start(startTime, offset, duration);
- }
- return this;
- };
- /**
- * Stop playback.
- * @private
- * @param {Time} [time=now]
- * @returns {Tone.Player} this
- */
- Tone.Player.prototype._stop = function (time) {
- if (this._source) {
- this._source.stop(this.toSeconds(time));
- }
- return this;
- };
- /**
- * Seek to a specific time in the player's buffer. If the
- * source is no longer playing at that time, it will stop.
- * If you seek to a time that
- * @param {Time} offset The time to seek to.
- * @param {Time=} time The time for the seek event to occur.
- * @return {Tone.Player} this
- * @example
- * source.start(0.2);
- * source.stop(0.4);
- */
- Tone.Player.prototype.seek = function (offset, time) {
- time = this.toSeconds(time);
- if (this._state.getValueAtTime(time) === Tone.State.Started) {
- offset = this.toSeconds(offset);
- // if it's currently playing, stop it
- this._stop(time);
- //restart it at the given time
- this._start(time, offset);
- }
- return this;
- };
- /**
- * Set the loop start and end. Will only loop if loop is
- * set to true.
- * @param {Time} loopStart The loop end time
- * @param {Time} loopEnd The loop end time
- * @returns {Tone.Player} this
- * @example
- * //loop 0.1 seconds of the file.
- * player.setLoopPoints(0.2, 0.3);
- * player.loop = true;
- */
- Tone.Player.prototype.setLoopPoints = function (loopStart, loopEnd) {
- this.loopStart = loopStart;
- this.loopEnd = loopEnd;
- return this;
- };
- /**
- * If loop is true, the loop will start at this position.
- * @memberOf Tone.Player#
- * @type {Time}
- * @name loopStart
- */
- Object.defineProperty(Tone.Player.prototype, 'loopStart', {
- get: function () {
- return this._loopStart;
- },
- set: function (loopStart) {
- this._loopStart = loopStart;
- if (this._source) {
- this._source.loopStart = this.toSeconds(loopStart);
- }
- }
- });
- /**
- * If loop is true, the loop will end at this position.
- * @memberOf Tone.Player#
- * @type {Time}
- * @name loopEnd
- */
- Object.defineProperty(Tone.Player.prototype, 'loopEnd', {
- get: function () {
- return this._loopEnd;
- },
- set: function (loopEnd) {
- this._loopEnd = loopEnd;
- if (this._source) {
- this._source.loopEnd = this.toSeconds(loopEnd);
- }
- }
- });
- /**
- * The audio buffer belonging to the player.
- * @memberOf Tone.Player#
- * @type {Tone.Buffer}
- * @name buffer
- */
- Object.defineProperty(Tone.Player.prototype, 'buffer', {
- get: function () {
- return this._buffer;
- },
- set: function (buffer) {
- this._buffer.set(buffer);
- }
- });
- /**
- * If the buffer should loop once it's over.
- * @memberOf Tone.Player#
- * @type {boolean}
- * @name loop
- */
- Object.defineProperty(Tone.Player.prototype, 'loop', {
- get: function () {
- return this._loop;
- },
- set: function (loop) {
- this._loop = loop;
- if (this._source) {
- this._source.loop = loop;
- }
- }
- });
- /**
- * The playback speed. 1 is normal speed. This is not a signal because
- * Safari and iOS currently don't support playbackRate as a signal.
- * @memberOf Tone.Player#
- * @type {number}
- * @name playbackRate
- */
- Object.defineProperty(Tone.Player.prototype, 'playbackRate', {
- get: function () {
- return this._playbackRate;
- },
- set: function (rate) {
- this._playbackRate = rate;
- if (this._source) {
- this._source.playbackRate.value = rate;
- }
- }
- });
- /**
- * The direction the buffer should play in
- * @memberOf Tone.Player#
- * @type {boolean}
- * @name reverse
- */
- Object.defineProperty(Tone.Player.prototype, 'reverse', {
- get: function () {
- return this._buffer.reverse;
- },
- set: function (rev) {
- this._buffer.reverse = rev;
- }
- });
- /**
- * If all the buffer is loaded
- * @memberOf Tone.Player#
- * @type {Boolean}
- * @name loaded
- * @readOnly
- */
- Object.defineProperty(Tone.Player.prototype, 'loaded', {
- get: function () {
- return this._buffer.loaded;
- }
- });
- /**
- * Dispose and disconnect.
- * @return {Tone.Player} this
- */
- Tone.Player.prototype.dispose = function () {
- Tone.Source.prototype.dispose.call(this);
- if (this._source !== null) {
- this._source.disconnect();
- this._source = null;
- }
- this._buffer.dispose();
- this._buffer = null;
- return this;
- };
- return Tone.Player;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.Players combines multiple [Tone.Player](Player) objects.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Object} urls An object mapping a name to a url.
- * @param {function=} onload The function to invoke when the buffer is loaded.
- */
- Tone.Players = function (urls) {
- var args = Array.prototype.slice.call(arguments);
- args.shift();
- var options = Tone.defaults(args, ['onload'], Tone.Players);
- Tone.call(this);
- /**
- * The output volume node
- * @type {Tone.Volume}
- * @private
- */
- this._volume = this.output = new Tone.Volume(options.volume);
- /**
- * The volume of the output in decibels.
- * @type {Decibels}
- * @signal
- * @example
- * source.volume.value = -6;
- */
- this.volume = this._volume.volume;
- this._readOnly('volume');
- //make the output explicitly stereo
- this._volume.output.output.channelCount = 2;
- this._volume.output.output.channelCountMode = 'explicit';
- //mute initially
- this.mute = options.mute;
- /**
- * The container of all of the players
- * @type {Object}
- * @private
- */
- this._players = {};
- /**
- * The loading count
- * @type {Number}
- * @private
- */
- this._loadingCount = 0;
- /**
- * private holder of the fadeIn time
- * @type {Time}
- * @private
- */
- this._fadeIn = options.fadeIn;
- /**
- * private holder of the fadeOut time
- * @type {Time}
- * @private
- */
- this._fadeOut = options.fadeOut;
- //add all of the players
- for (var name in urls) {
- this._loadingCount++;
- this.add(name, urls[name], this._bufferLoaded.bind(this, options.onload));
- }
- };
- Tone.extend(Tone.Players, Tone.AudioNode);
- /**
- * The default values
- * @type {Object}
- */
- Tone.Players.defaults = {
- 'volume': 0,
- 'mute': false,
- 'onload': Tone.noOp,
- 'fadeIn': 0,
- 'fadeOut': 0
- };
- /**
- * A buffer was loaded. decrement the counter.
- * @param {Function} callback
- * @private
- */
- Tone.Players.prototype._bufferLoaded = function (callback) {
- this._loadingCount--;
- if (this._loadingCount === 0 && callback) {
- callback(this);
- }
- };
- /**
- * Mute the output.
- * @memberOf Tone.Source#
- * @type {boolean}
- * @name mute
- * @example
- * //mute the output
- * source.mute = true;
- */
- Object.defineProperty(Tone.Players.prototype, 'mute', {
- get: function () {
- return this._volume.mute;
- },
- set: function (mute) {
- this._volume.mute = mute;
- }
- });
- /**
- * The fadeIn time of the amplitude envelope.
- * @memberOf Tone.Source#
- * @type {Time}
- * @name fadeIn
- */
- Object.defineProperty(Tone.Players.prototype, 'fadeIn', {
- get: function () {
- return this._fadeIn;
- },
- set: function (fadeIn) {
- this._fadeIn = fadeIn;
- this._forEach(function (player) {
- player.fadeIn = fadeIn;
- });
- }
- });
- /**
- * The fadeOut time of the amplitude envelope.
- * @memberOf Tone.Source#
- * @type {Time}
- * @name fadeOut
- */
- Object.defineProperty(Tone.Players.prototype, 'fadeOut', {
- get: function () {
- return this._fadeOut;
- },
- set: function (fadeOut) {
- this._fadeOut = fadeOut;
- this._forEach(function (player) {
- player.fadeOut = fadeOut;
- });
- }
- });
- /**
- * The state of the players object. Returns "started" if any of the players are playing.
- * @memberOf Tone.Players#
- * @type {String}
- * @name state
- * @readOnly
- */
- Object.defineProperty(Tone.Players.prototype, 'state', {
- get: function () {
- var playing = false;
- this._forEach(function (player) {
- playing = playing || player.state === Tone.State.Started;
- });
- return playing ? Tone.State.Started : Tone.State.Stopped;
- }
- });
- /**
- * True if the buffers object has a buffer by that name.
- * @param {String|Number} name The key or index of the
- * buffer.
- * @return {Boolean}
- */
- Tone.Players.prototype.has = function (name) {
- return this._players.hasOwnProperty(name);
- };
- /**
- * Get a player by name.
- * @param {String} name The players name as defined in
- * the constructor object or `add` method.
- * @return {Tone.Player}
- */
- Tone.Players.prototype.get = function (name) {
- if (this.has(name)) {
- return this._players[name];
- } else {
- throw new Error('Tone.Players: no player named ' + name);
- }
- };
- /**
- * Iterate over all of the players
- * @param {Function} callback
- * @return {Tone.Players} this
- * @private
- */
- Tone.Players.prototype._forEach = function (callback) {
- for (var playerName in this._players) {
- callback(this._players[playerName], playerName);
- }
- return this;
- };
- /**
- * If all the buffers are loaded or not
- * @memberOf Tone.Players#
- * @type {Boolean}
- * @name loaded
- * @readOnly
- */
- Object.defineProperty(Tone.Players.prototype, 'loaded', {
- get: function () {
- var isLoaded = true;
- this._forEach(function (player) {
- isLoaded = isLoaded && player.loaded;
- });
- return isLoaded;
- }
- });
- /**
- * Add a player by name and url to the Players
- * @param {String} name A unique name to give the player
- * @param {String|Tone.Buffer|Audiobuffer} url Either the url of the bufer,
- * or a buffer which will be added
- * with the given name.
- * @param {Function=} callback The callback to invoke
- * when the url is loaded.
- */
- Tone.Players.prototype.add = function (name, url, callback) {
- this._players[name] = new Tone.Player(url, callback).connect(this.output);
- this._players[name].fadeIn = this._fadeIn;
- this._players[name].fadeOut = this._fadeOut;
- return this;
- };
- /**
- * Stop all of the players at the given time
- * @param {Time} time The time to stop all of the players.
- * @return {Tone.Players} this
- */
- Tone.Players.prototype.stopAll = function (time) {
- this._forEach(function (player) {
- player.stop(time);
- });
- };
- /**
- * Dispose and disconnect.
- * @return {Tone.Players} this
- */
- Tone.Players.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this._volume.dispose();
- this._volume = null;
- this._writable('volume');
- this.volume = null;
- this.output = null;
- this._forEach(function (player) {
- player.dispose();
- });
- this._players = null;
- return this;
- };
- return Tone.Players;
- });
- Module(function (Tone) {
-
- /**
- * @class Tone.UserMedia uses MediaDevices.getUserMedia to open up
- * and external microphone or audio input. Check
- * [MediaDevices API Support](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
- * to see which browsers are supported. Access to an external input
- * is limited to secure (HTTPS) connections.
- *
- * @constructor
- * @extends {Tone.AudioNode}
- * @param {Decibels=} volume The level of the input
- * @example
- * //list the inputs and open the third one
- * var motu = new Tone.UserMedia();
- *
- * //opening the input asks the user to activate their mic
- * motu.open().then(function(){
- * //opening is activates the microphone
- * //starting lets audio through
- * motu.start(10);
- * });
- */
- Tone.UserMedia = function () {
- var options = Tone.defaults(arguments, ['volume'], Tone.UserMedia);
- Tone.AudioNode.call(this);
- /**
- * The MediaStreamNode
- * @type {MediaStreamAudioSourceNode}
- * @private
- */
- this._mediaStream = null;
- /**
- * The media stream created by getUserMedia.
- * @type {LocalMediaStream}
- * @private
- */
- this._stream = null;
- /**
- * The open device
- * @type {MediaDeviceInfo}
- * @private
- */
- this._device = null;
- /**
- * The output volume node
- * @type {Tone.Volume}
- * @private
- */
- this._volume = this.output = new Tone.Volume(options.volume);
- /**
- * The volume of the output in decibels.
- * @type {Decibels}
- * @signal
- * @example
- * input.volume.value = -6;
- */
- this.volume = this._volume.volume;
- this._readOnly('volume');
- this.mute = options.mute;
- };
- Tone.extend(Tone.UserMedia, Tone.AudioNode);
- /**
- * the default parameters
- * @type {Object}
- */
- Tone.UserMedia.defaults = {
- 'volume': 0,
- 'mute': false
- };
- /**
- * Open the media stream. If a string is passed in, it is assumed
- * to be the label or id of the stream, if a number is passed in,
- * it is the input number of the stream.
- * @param {String|Number} [labelOrId="default"] The label or id of the audio input media device.
- * With no argument, the default stream is opened.
- * @return {Promise} The promise is resolved when the stream is open.
- */
- Tone.UserMedia.prototype.open = function (labelOrId) {
- labelOrId = Tone.defaultArg(labelOrId, 'default');
- return Tone.UserMedia.enumerateDevices().then(function (devices) {
- var device;
- if (Tone.isNumber(labelOrId)) {
- device = devices[labelOrId];
- } else {
- device = devices.find(function (device) {
- return device.label === labelOrId || device.deviceId === labelOrId;
- });
- //didn't find a matching device
- if (!device) {
- throw new Error('Tone.UserMedia: no matching device: ' + labelOrId);
- }
- }
- this._device = device;
- //do getUserMedia
- var constraints = {
- audio: {
- 'deviceId': device.deviceId,
- 'echoCancellation': false,
- 'sampleRate': this.context.sampleRate
- }
- };
- return navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
- //start a new source only if the previous one is closed
- if (!this._stream) {
- this._stream = stream;
- //Wrap a MediaStreamSourceNode around the live input stream.
- this._mediaStream = this.context.createMediaStreamSource(stream);
- //Connect the MediaStreamSourceNode to a gate gain node
- this._mediaStream.connect(this.output);
- }
- return this;
- }.bind(this));
- }.bind(this));
- };
- /**
- * Close the media stream
- * @return {Tone.UserMedia} this
- */
- Tone.UserMedia.prototype.close = function () {
- if (this._stream) {
- this._stream.getAudioTracks().forEach(function (track) {
- track.stop();
- });
- this._stream = null;
- //remove the old media stream
- this._mediaStream.disconnect();
- this._mediaStream = null;
- }
- this._device = null;
- return this;
- };
- /**
- * Returns a promise which resolves with the list of audio input devices available.
- * @return {Promise} The promise that is resolved with the devices
- * @static
- * @example
- * Tone.UserMedia.enumerateDevices().then(function(devices){
- * console.log(devices)
- * })
- */
- Tone.UserMedia.enumerateDevices = function () {
- return navigator.mediaDevices.enumerateDevices().then(function (devices) {
- return devices.filter(function (device) {
- return device.kind === 'audioinput';
- });
- });
- };
- /**
- * Returns the playback state of the source, "started" when the microphone is open
- * and "stopped" when the mic is closed.
- * @type {Tone.State}
- * @readOnly
- * @memberOf Tone.UserMedia#
- * @name state
- */
- Object.defineProperty(Tone.UserMedia.prototype, 'state', {
- get: function () {
- return this._stream && this._stream.active ? Tone.State.Started : Tone.State.Stopped;
- }
- });
- /**
- * Returns an identifier for the represented device that is
- * persisted across sessions. It is un-guessable by other applications and
- * unique to the origin of the calling application. It is reset when the
- * user clears cookies (for Private Browsing, a different identifier is
- * used that is not persisted across sessions). Returns undefined when the
- * device is not open.
- * @type {String}
- * @readOnly
- * @memberOf Tone.UserMedia#
- * @name deviceId
- */
- Object.defineProperty(Tone.UserMedia.prototype, 'deviceId', {
- get: function () {
- if (this._device) {
- return this._device.deviceId;
- }
- }
- });
- /**
- * Returns a group identifier. Two devices have the
- * same group identifier if they belong to the same physical device.
- * Returns undefined when the device is not open.
- * @type {String}
- * @readOnly
- * @memberOf Tone.UserMedia#
- * @name groupId
- */
- Object.defineProperty(Tone.UserMedia.prototype, 'groupId', {
- get: function () {
- if (this._device) {
- return this._device.groupId;
- }
- }
- });
- /**
- * Returns a label describing this device (for example "Built-in Microphone").
- * Returns undefined when the device is not open or label is not available
- * because of permissions.
- * @type {String}
- * @readOnly
- * @memberOf Tone.UserMedia#
- * @name groupId
- */
- Object.defineProperty(Tone.UserMedia.prototype, 'label', {
- get: function () {
- if (this._device) {
- return this._device.label;
- }
- }
- });
- /**
- * Mute the output.
- * @memberOf Tone.UserMedia#
- * @type {boolean}
- * @name mute
- * @example
- * //mute the output
- * userMedia.mute = true;
- */
- Object.defineProperty(Tone.UserMedia.prototype, 'mute', {
- get: function () {
- return this._volume.mute;
- },
- set: function (mute) {
- this._volume.mute = mute;
- }
- });
- /**
- * Clean up.
- * @return {Tone.UserMedia} this
- */
- Tone.UserMedia.prototype.dispose = function () {
- Tone.AudioNode.prototype.dispose.call(this);
- this.close();
- this._writable('volume');
- this._volume.dispose();
- this._volume = null;
- this.volume = null;
- return this;
- };
- /**
- * If getUserMedia is supported by the browser.
- * @type {Boolean}
- * @memberOf Tone.UserMedia#
- * @name supported
- * @static
- * @readOnly
- */
- Object.defineProperty(Tone.UserMedia, 'supported', {
- get: function () {
- return !Tone.isUndef(navigator.mediaDevices) && Tone.isFunction(navigator.mediaDevices.getUserMedia);
- }
- });
- return Tone.UserMedia;
- });
-
- return Tone;
-}));
\ No newline at end of file
diff --git a/build/Tone.min.js b/build/Tone.min.js
deleted file mode 100644
index c029d0c0..00000000
--- a/build/Tone.min.js
+++ /dev/null
@@ -1,14 +0,0 @@
-!function(t,e){"function"==typeof define&&define.amd?define(function(){return e()}):"object"==typeof module?module.exports=e():t.Tone=e()}(this,function(){"use strict";function t(t){t(e)}var e;/**
- * Tone.js
- * @author Yotam Mann
- * @license http://opensource.org/licenses/MIT MIT License
- * @copyright 2014-2017 Yotam Mann
- */
-return function(t){e=t()}(function(){var t,e=function(){};return e.prototype.toString=function(){var t,i,n;for(t in e)if(i=t[0].match(/^[A-Z]$/),n=e[t]===this.constructor,e.isFunction(e[t])&&i&&n)return t;return"Tone"},e.prototype.dispose=function(){return this},e.prototype.set=function(t,i,n){var s,o,r,a,l,h,u;e.isObject(t)?n=i:e.isString(t)&&(s={},s[t]=i,t=s);t:for(o in t){if(i=t[o],r=this,-1!==o.indexOf(".")){for(a=o.split("."),l=0;lreturnType
is "byte" which returns values
in the range 0-255.
.rampTo(value, rampTime)
- smoothly changes the signal from the current value to the target value over the duration of the rampTime.
- This example uses .rampTo
in to smooth out changes in volume and frequency.
- .rampTo
in to smooth out changes in volume and frequency.