diff --git a/src/audioin.js b/src/audioin.js
index 07c63630..7200f5fc 100644
--- a/src/audioin.js
+++ b/src/audioin.js
@@ -50,40 +50,47 @@ p5sound.inputSources = [];
*/
class AudioIn {
constructor(errorCallback) {
- // set up audio input
/**
+ * Set up audio input
* @property {GainNode} input
*/
this.input = p5sound.audiocontext.createGain();
/**
+ * Send audio as an output, i.e. your computer's speaker.
* @property {GainNode} output
*/
this.output = p5sound.audiocontext.createGain();
-
/**
+ * Used to store the MediaStream object that is returned from the getUserMedia() API,
+ * which allows access to the user's microphone. The stream is used to create a MediaStreamAudioSourceNode,
+ * which is used as the audio source for the input and output gain nodes.
+ * The stream is also used to check if the browser supports the MediaStreamTrack and mediaDevices API,
+ * and if not, an errorCallback function is called or an alert is displayed.
* @property {MediaStream|null} stream
*/
this.stream = null;
/**
+ * Used to access the "audio input" from the user's microphone.
+ * It creates a MediaStream object that can be used to start and stop the mic and measure its volume using the getLevel() method or by connecting it to an FFT object.
+ * MediaStream object can also be use to check if the browser supports MediaStreamTrack and mediaDevices and to add the AudioIn object to the soundArray for disposal on close.
* @property {MediaStreamAudioSourceNode|null} mediaStream
*/
this.mediaStream = null;
/**
+ * Used to store the "current source of audio input", such as the user's microphone.
+ * Initially set to "null" and can be updated as the user selects different audio sources.
+ * Also used in conjunction with the "input" and "mediaStream" properties to control audio input.
* @property {Number|null} currentSource
*/
this.currentSource = null;
-
/**
* Client must allow browser to access their microphone / audioin source.
* Default: false. Will become true when the client enables access.
- *
* @property {Boolean} enabled
*/
this.enabled = false;
-
/**
* Input amplitude, connect to it by default but not to master out
- *
* @property {p5.Amplitude} amplitude
*/
this.amplitude = new Amplitude();
diff --git a/src/effect.js b/src/effect.js
index fee75547..fd17e01c 100644
--- a/src/effect.js
+++ b/src/effect.js
@@ -13,6 +13,7 @@ import CrossFade from 'Tone/component/CrossFade.js';
* p5.Filter,
* p5.Reverb,
* p5.EQ,
+ * p5.Panner.
* p5.Panner3D.
*
* @class p5.Effect
diff --git a/src/monosynth.js b/src/monosynth.js
index d0f2750f..a2e7b8d6 100644
--- a/src/monosynth.js
+++ b/src/monosynth.js
@@ -77,14 +77,21 @@ class MonoSynth extends AudioVoice {
* @for p5.MonoSynth
*/
/**
+ * Allows user to set the decay time of the envelope (ADSR) of the MonoSynth class.
+ * It is a getter and setter that can be used to retrieve or change the decay time.
+ * Used in conjunction with the attack, sustain, and release fields/functions to set the full envelope of the synthesizer.
* @property {Number} decay
* @for p5.MonoSynth
*/
/**
+ * Allows the user to retrieve and adjust the sustain level of the envelope,
+ * which controls the level at which the sound is sustained during the sustain phase of the envelope.
+ * The default sustain level is set to 0.15.
* @property {Number} sustain
* @for p5.MonoSynth
*/
/**
+ * Allows the user to access and change the release time of the envelope.
* @property {Number} release
* @for p5.MonoSynth
*/
diff --git a/src/oscillator.js b/src/oscillator.js
index 9bd17a40..79d72c55 100644
--- a/src/oscillator.js
+++ b/src/oscillator.js
@@ -133,10 +133,14 @@ class Oscillator {
this.oscillator.connect(this.output);
// stereo panning
- this.panPosition = 0.0;
this.connection = p5sound.input; // connect to p5sound by default
- this.panner = new Panner(this.output, this.connection, 1);
+ if (typeof p5sound.audiocontext.createStereoPanner !== 'undefined') {
+ this.panner = new Panner();
+ this.output.connect(this.panner);
+ } else {
+ this.panner = new Panner(this.output, this.connection, 1);
+ }
//array of math operation signal chaining
this.mathOps = [this.output];
@@ -415,21 +419,20 @@ class Oscillator {
* seconds from now
*/
pan(pval, tFromNow) {
- this.panPosition = pval;
this.panner.pan(pval, tFromNow);
}
/**
- * Returns the current value of panPosition , between Left (-1) and Right (1)
+ * Returns the current value of pan position , between Left (-1) and Right (1)
*
* @method getPan
* @for p5.Oscillator
*
- * @returns {number} panPosition of oscillator , between Left (-1) and Right (1)
+ * @returns {number} pan position of oscillator , between Left (-1) and Right (1)
*/
getPan() {
- return this.panPosition;
+ return this.panner.getPan();
}
// get rid of the oscillator
@@ -442,6 +445,7 @@ class Oscillator {
var now = p5sound.audiocontext.currentTime;
this.stop(now);
this.disconnect();
+ this.panner.dispose();
this.panner = null;
this.oscillator = null;
}
diff --git a/src/panner.js b/src/panner.js
index e946f788..e234d23d 100644
--- a/src/panner.js
+++ b/src/panner.js
@@ -1,38 +1,85 @@
+import Effect from './effect.js';
+
import p5sound from './main';
var ac = p5sound.audiocontext;
var panner;
// Stereo panner
// if there is a stereo panner node use it
if (typeof ac.createStereoPanner !== 'undefined') {
- class Panner {
- constructor(input, output) {
- this.stereoPanner = this.input = ac.createStereoPanner();
- input.connect(this.stereoPanner);
- this.stereoPanner.connect(output);
+ /**
+ * The Panner class allows you to control the stereo
+ * panning of a sound source. It uses the [StereoPannerNode](https://developer.mozilla.org/en-US/docs/Web/API/StereoPannerNode),
+ * which allows you to adjust the balance between the left and right channels of a sound source.
+ *
+ * This class extends p5.Effect.
+ * Methods amp(), chain(),
+ * drywet(), connect(), and
+ * disconnect() are available.
+ *
+ * @class p5.Panner
+ * @extends p5.Effect
+ */
+ class Panner extends Effect {
+ constructor() {
+ super();
+ this.stereoPanner = this.ac.createStereoPanner();
+
+ this.input.connect(this.stereoPanner);
+ this.stereoPanner.connect(this.wet);
}
+ /**
+ * Set the stereo pan position, a value of -1 means the sound will be fully panned
+ * to the left, a value of 0 means the sound will be centered, and a value of 1 means
+ * the sound will be fully panned to the right.
+ * @method pan
+ * @for p5.Panner
+ * @param {Number} value A value between -1 and 1 that sets the pan position.
+ *
+ * @param {Number} [time] time in seconds that it will take for the panning to change to the specified value.
+ */
pan(val, tFromNow) {
- var time = tFromNow || 0;
- var t = ac.currentTime + time;
-
- this.stereoPanner.pan.linearRampToValueAtTime(val, t);
+ if (typeof val === 'number') {
+ let time = tFromNow || 0;
+ this.stereoPanner.pan.linearRampToValueAtTime(
+ val,
+ this.ac.currentTime + time
+ );
+ } else if (typeof val !== 'undefined') {
+ val.connect(this.stereoPanner.pan);
+ }
}
- //not implemented because stereopanner
- //node does not require this and will automatically
- //convert single channel or multichannel to stereo.
- //tested with single and stereo, not with (>2) multichannel
- inputChannels() {}
-
- connect(obj) {
- this.stereoPanner.connect(obj);
+ /**
+ * Return the current panning value.
+ *
+ * @method getPan
+ * @for p5.Panner
+ * @return {Number} current panning value, number between -1 (left) and 1 (right).
+ */
+ getPan() {
+ return this.stereoPanner.pan.value;
}
- disconnect() {
+ /**
+ * Get rid of the Panner and free up its resources / memory.
+ *
+ * @method dispose
+ * @for p5.Panner
+ */
+ dispose() {
+ super.dispose();
if (this.stereoPanner) {
this.stereoPanner.disconnect();
+ delete this.stereoPanner;
}
}
+
+ //not implemented because stereopanner
+ //node does not require this and will automatically
+ //convert single channel or multichannel to stereo.
+ //tested with single and stereo, not with (>2) multichannel
+ inputChannels() {}
}
panner = Panner;
@@ -45,6 +92,7 @@ if (typeof ac.createStereoPanner !== 'undefined') {
this.input = ac.createGain();
input.connect(this.input);
+ this.panValue = 0;
this.left = ac.createGain();
this.right = ac.createGain();
this.left.channelInterpretation = 'discrete';
@@ -70,6 +118,7 @@ if (typeof ac.createStereoPanner !== 'undefined') {
// -1 is left, +1 is right
pan(val, tFromNow) {
+ this.panValue = val;
var time = tFromNow || 0;
var t = ac.currentTime + time;
var v = (val + 1) / 2;
@@ -79,6 +128,10 @@ if (typeof ac.createStereoPanner !== 'undefined') {
this.right.gain.linearRampToValueAtTime(rightVal, t);
}
+ getPan() {
+ return this.panValue;
+ }
+
inputChannels(numChannels) {
if (numChannels === 1) {
this.input.disconnect();
@@ -104,6 +157,29 @@ if (typeof ac.createStereoPanner !== 'undefined') {
this.output.disconnect();
}
}
+
+ dispose() {
+ if (this.input) {
+ this.input.disconnect();
+ delete this.input;
+ }
+ if (this.output) {
+ this.output.disconnect();
+ delete this.output;
+ }
+ if (this.left) {
+ this.left.disconnect();
+ delete this.left;
+ }
+ if (this.right) {
+ this.right.disconnect();
+ delete this.right;
+ }
+ if (this.splitter) {
+ this.splitter.disconnect();
+ delete this.splitter;
+ }
+ }
}
panner = Panner;
}
diff --git a/src/peakDetect.js b/src/peakDetect.js
index 02db7f61..744eda1e 100644
--- a/src/peakDetect.js
+++ b/src/peakDetect.js
@@ -111,10 +111,11 @@ class PeakDetect {
this.currentValue = 0;
/**
- * isDetected is set to true when a peak is detected.
- *
+ * It returns a boolean indicating whether a peak in the audio frequency spectrum has been detected or not.
* @attribute isDetected {Boolean}
* @default false
+ * @property {Number} isDetected
+ * @for p5.PeakDetect
*/
this.isDetected = false;
diff --git a/src/soundfile.js b/src/soundfile.js
index 11e2517c..602c5471 100644
--- a/src/soundfile.js
+++ b/src/soundfile.js
@@ -182,8 +182,12 @@ class SoundFile {
this.startMillis = null;
// stereo panning
- this.panPosition = 0.0;
- this.panner = new Panner(this.output, p5sound.input, 2);
+ if (typeof p5sound.audiocontext.createStereoPanner !== 'undefined') {
+ this.panner = new Panner();
+ this.output.connect(this.panner);
+ } else {
+ this.panner = new Panner(this.output, p5sound.input, 2);
+ }
// it is possible to instantiate a soundfile with no path
if (this.url || this.file) {
@@ -795,7 +799,6 @@ class SoundFile {
*
*/
pan(pval, tFromNow) {
- this.panPosition = pval;
this.panner.pan(pval, tFromNow);
}
@@ -809,7 +812,7 @@ class SoundFile {
* 0.0 is center and default.
*/
getPan() {
- return this.panPosition;
+ return this.panner.getPan();
}
/**
@@ -1264,7 +1267,7 @@ class SoundFile {
this.output = null;
}
if (this.panner) {
- this.panner.disconnect();
+ this.panner.dispose();
this.panner = null;
}
}
diff --git a/test/tests/p5.Oscillator.js b/test/tests/p5.Oscillator.js
index d5ce2f01..40f80fc5 100644
--- a/test/tests/p5.Oscillator.js
+++ b/test/tests/p5.Oscillator.js
@@ -214,11 +214,8 @@ describe('p5.Oscillator', function () {
it('can be panned without any delay', function (done) {
let osc = new p5.Oscillator();
let panner = osc.panner;
- expect(osc.panPosition).to.equal(0); //default value
expect(osc.getPan()).to.equal(0);
osc.pan(-0.3);
- expect(osc.panPosition).to.equal(-0.3);
- expect(osc.getPan()).to.equal(-0.3);
if (typeof p5.soundOut.audiocontext.createStereoPanner !== 'undefined') {
setTimeout(() => {
expect(panner.stereoPanner.pan.value).to.be.approximately(-0.3, 0.01);
@@ -236,8 +233,6 @@ describe('p5.Oscillator', function () {
let osc = new p5.Oscillator();
osc.pan(0.7, 0.1);
let panner = osc.panner;
- expect(osc.panPosition).to.equal(0.7);
- expect(osc.getPan()).to.equal(0.7);
if (typeof p5.soundOut.audiocontext.createStereoPanner !== 'undefined') {
setTimeout(() => {
expect(panner.stereoPanner.pan.value).to.not.be.approximately(
@@ -250,7 +245,7 @@ describe('p5.Oscillator', function () {
0.01
);
done();
- }, 50);
+ }, 60);
}, 50);
} else {
setTimeout(() => {
@@ -266,7 +261,7 @@ describe('p5.Oscillator', function () {
expect(panner.left.gain.value).to.be.approximately(0.972, 0.001);
expect(panner.right.gain.value).to.be.approximately(0.233, 0.001);
done();
- }, 100);
+ }, 60);
}, 50);
}
});
diff --git a/test/tests/p5.Panner.js b/test/tests/p5.Panner.js
index 2fe639df..d41f83fe 100644
--- a/test/tests/p5.Panner.js
+++ b/test/tests/p5.Panner.js
@@ -6,22 +6,28 @@ describe('p5.Panner', function () {
input = ac.createGain();
});
it('can be created', function () {
- new p5.Panner(input, output);
+ new p5.Panner();
});
it('can be connected and disconnected', function () {
- let panner = new p5.Panner(input, output);
+ let panner = new p5.Panner();
panner.connect(input);
panner.disconnect();
});
- it('can be panned without a delay', function () {
- let panner = new p5.Panner(input, output);
+ it('can be panned without a delay', function (done) {
+ let panner = new p5.Panner();
panner.pan(0.4);
- panner.pan(0.3, 0);
- //TODO: to check the value of left gain/ right gain (or) the stereoPanner.pan
+ setTimeout(() => {
+ expect(panner.getPan()).to.be.approximately(0.4, 0.01);
+ done();
+ }, 25);
});
- it('can be panned with a delay', function () {
+ it('can be panned with a delay', function (done) {
let panner = new p5.Panner(input, output);
- panner.pan(0.4, 10);
+ panner.pan(-0.7, 0.1);
+ setTimeout(() => {
+ expect(panner.getPan()).to.be.approximately(-0.7, 0.01);
+ done();
+ }, 125);
});
it('can take inputChannels as 1 or 2', function () {
let panner = new p5.Panner(input, output);
diff --git a/test/tests/p5.SoundFile.js b/test/tests/p5.SoundFile.js
index da745162..ba872b9a 100644
--- a/test/tests/p5.SoundFile.js
+++ b/test/tests/p5.SoundFile.js
@@ -30,7 +30,7 @@ describe('p5.SoundFile', function () {
expect(sf.pauseTime).to.equal(0);
expect(sf.mode).to.equal('sustain');
expect(sf.startMillis).to.be.null;
- expect(sf.panPosition).to.equal(0);
+ expect(sf.getPan()).to.equal(0);
expect(sf.panner).to.have.property('stereoPanner');
expect(p5.soundOut.soundArray).to.include(sf);
@@ -507,13 +507,13 @@ describe('p5.SoundFile', function () {
let sf = new p5.SoundFile();
expect(sf.getPan()).to.equal(0);
sf.pan(0.32);
- expect(sf.panPosition).to.equal(0.32);
- expect(sf.getPan()).to.equal(0.32);
+ setTimeout(() => {
+ expect(sf.getPan()).to.equal(0.32);
+ }, 5);
//with delay
let sf2 = new p5.SoundFile();
sf2.pan(-0.89, 0.1);
setTimeout(() => {
- expect(sf2.panPosition).to.equal(-0.89);
expect(sf2.getPan()).to.equal(-0.89);
}, 100);
});