Skip to content

Commit 401120d

Browse files
authored
Merge pull request #736 from KevinGrajeda/panner-rework
`Panner.js` rework
2 parents 3980882 + f302c0d commit 401120d

File tree

7 files changed

+134
-49
lines changed

7 files changed

+134
-49
lines changed

src/effect.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import CrossFade from 'Tone/component/CrossFade.js';
1313
* <a href="/reference/#/p5.Filter">p5.Filter</a>,
1414
* <a href="/reference/#/p5.Reverb">p5.Reverb</a>,
1515
* <a href="/reference/#/p5.EQ">p5.EQ</a>,
16+
* <a href="/reference/#/p5.Panner">p5.Panner</a>.
1617
* <a href="/reference/#/p5.Panner3D">p5.Panner3D</a>.
1718
*
1819
* @class p5.Effect

src/oscillator.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,14 @@ class Oscillator {
133133

134134
this.oscillator.connect(this.output);
135135
// stereo panning
136-
this.panPosition = 0.0;
137136
this.connection = p5sound.input; // connect to p5sound by default
138-
this.panner = new Panner(this.output, this.connection, 1);
139137

138+
if (typeof p5sound.audiocontext.createStereoPanner !== 'undefined') {
139+
this.panner = new Panner();
140+
this.output.connect(this.panner);
141+
} else {
142+
this.panner = new Panner(this.output, this.connection, 1);
143+
}
140144
//array of math operation signal chaining
141145
this.mathOps = [this.output];
142146

@@ -415,21 +419,20 @@ class Oscillator {
415419
* seconds from now
416420
*/
417421
pan(pval, tFromNow) {
418-
this.panPosition = pval;
419422
this.panner.pan(pval, tFromNow);
420423
}
421424

422425
/**
423-
* Returns the current value of panPosition , between Left (-1) and Right (1)
426+
* Returns the current value of pan position , between Left (-1) and Right (1)
424427
*
425428
* @method getPan
426429
* @for p5.Oscillator
427430
*
428-
* @returns {number} panPosition of oscillator , between Left (-1) and Right (1)
431+
* @returns {number} pan position of oscillator , between Left (-1) and Right (1)
429432
*/
430433

431434
getPan() {
432-
return this.panPosition;
435+
return this.panner.getPan();
433436
}
434437

435438
// get rid of the oscillator
@@ -442,6 +445,7 @@ class Oscillator {
442445
var now = p5sound.audiocontext.currentTime;
443446
this.stop(now);
444447
this.disconnect();
448+
this.panner.dispose();
445449
this.panner = null;
446450
this.oscillator = null;
447451
}

src/panner.js

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,85 @@
1+
import Effect from './effect.js';
2+
13
import p5sound from './main';
24
var ac = p5sound.audiocontext;
35
var panner;
46
// Stereo panner
57
// if there is a stereo panner node use it
68
if (typeof ac.createStereoPanner !== 'undefined') {
7-
class Panner {
8-
constructor(input, output) {
9-
this.stereoPanner = this.input = ac.createStereoPanner();
10-
input.connect(this.stereoPanner);
11-
this.stereoPanner.connect(output);
9+
/**
10+
* The Panner class allows you to control the stereo
11+
* panning of a sound source. It uses the [StereoPannerNode](https://developer.mozilla.org/en-US/docs/Web/API/StereoPannerNode),
12+
* which allows you to adjust the balance between the left and right channels of a sound source.
13+
*
14+
* This class extends <a href = "/reference/#/p5.Effect">p5.Effect</a>.
15+
* Methods <a href = "/reference/#/p5.Effect/amp">amp()</a>, <a href = "/reference/#/p5.Effect/chain">chain()</a>,
16+
* <a href = "/reference/#/p5.Effect/drywet">drywet()</a>, <a href = "/reference/#/p5.Effect/connect">connect()</a>, and
17+
* <a href = "/reference/#/p5.Effect/disconnect">disconnect()</a> are available.
18+
*
19+
* @class p5.Panner
20+
* @extends p5.Effect
21+
*/
22+
class Panner extends Effect {
23+
constructor() {
24+
super();
25+
this.stereoPanner = this.ac.createStereoPanner();
26+
27+
this.input.connect(this.stereoPanner);
28+
this.stereoPanner.connect(this.wet);
1229
}
1330

31+
/**
32+
* Set the stereo pan position, a value of -1 means the sound will be fully panned
33+
* to the left, a value of 0 means the sound will be centered, and a value of 1 means
34+
* the sound will be fully panned to the right.
35+
* @method pan
36+
* @for p5.Panner
37+
* @param {Number} value A value between -1 and 1 that sets the pan position.
38+
*
39+
* @param {Number} [time] time in seconds that it will take for the panning to change to the specified value.
40+
*/
1441
pan(val, tFromNow) {
15-
var time = tFromNow || 0;
16-
var t = ac.currentTime + time;
17-
18-
this.stereoPanner.pan.linearRampToValueAtTime(val, t);
42+
if (typeof val === 'number') {
43+
let time = tFromNow || 0;
44+
this.stereoPanner.pan.linearRampToValueAtTime(
45+
val,
46+
this.ac.currentTime + time
47+
);
48+
} else if (typeof val !== 'undefined') {
49+
val.connect(this.stereoPanner.pan);
50+
}
1951
}
2052

21-
//not implemented because stereopanner
22-
//node does not require this and will automatically
23-
//convert single channel or multichannel to stereo.
24-
//tested with single and stereo, not with (>2) multichannel
25-
inputChannels() {}
26-
27-
connect(obj) {
28-
this.stereoPanner.connect(obj);
53+
/**
54+
* Return the current panning value.
55+
*
56+
* @method getPan
57+
* @for p5.Panner
58+
* @return {Number} current panning value, number between -1 (left) and 1 (right).
59+
*/
60+
getPan() {
61+
return this.stereoPanner.pan.value;
2962
}
3063

31-
disconnect() {
64+
/**
65+
* Get rid of the Panner and free up its resources / memory.
66+
*
67+
* @method dispose
68+
* @for p5.Panner
69+
*/
70+
dispose() {
71+
super.dispose();
3272
if (this.stereoPanner) {
3373
this.stereoPanner.disconnect();
74+
delete this.stereoPanner;
3475
}
3576
}
77+
78+
//not implemented because stereopanner
79+
//node does not require this and will automatically
80+
//convert single channel or multichannel to stereo.
81+
//tested with single and stereo, not with (>2) multichannel
82+
inputChannels() {}
3683
}
3784

3885
panner = Panner;
@@ -45,6 +92,7 @@ if (typeof ac.createStereoPanner !== 'undefined') {
4592
this.input = ac.createGain();
4693
input.connect(this.input);
4794

95+
this.panValue = 0;
4896
this.left = ac.createGain();
4997
this.right = ac.createGain();
5098
this.left.channelInterpretation = 'discrete';
@@ -70,6 +118,7 @@ if (typeof ac.createStereoPanner !== 'undefined') {
70118

71119
// -1 is left, +1 is right
72120
pan(val, tFromNow) {
121+
this.panValue = val;
73122
var time = tFromNow || 0;
74123
var t = ac.currentTime + time;
75124
var v = (val + 1) / 2;
@@ -79,6 +128,10 @@ if (typeof ac.createStereoPanner !== 'undefined') {
79128
this.right.gain.linearRampToValueAtTime(rightVal, t);
80129
}
81130

131+
getPan() {
132+
return this.panValue;
133+
}
134+
82135
inputChannels(numChannels) {
83136
if (numChannels === 1) {
84137
this.input.disconnect();
@@ -104,6 +157,29 @@ if (typeof ac.createStereoPanner !== 'undefined') {
104157
this.output.disconnect();
105158
}
106159
}
160+
161+
dispose() {
162+
if (this.input) {
163+
this.input.disconnect();
164+
delete this.input;
165+
}
166+
if (this.output) {
167+
this.output.disconnect();
168+
delete this.output;
169+
}
170+
if (this.left) {
171+
this.left.disconnect();
172+
delete this.left;
173+
}
174+
if (this.right) {
175+
this.right.disconnect();
176+
delete this.right;
177+
}
178+
if (this.splitter) {
179+
this.splitter.disconnect();
180+
delete this.splitter;
181+
}
182+
}
107183
}
108184
panner = Panner;
109185
}

src/soundfile.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,12 @@ class SoundFile {
182182
this.startMillis = null;
183183

184184
// stereo panning
185-
this.panPosition = 0.0;
186-
this.panner = new Panner(this.output, p5sound.input, 2);
185+
if (typeof p5sound.audiocontext.createStereoPanner !== 'undefined') {
186+
this.panner = new Panner();
187+
this.output.connect(this.panner);
188+
} else {
189+
this.panner = new Panner(this.output, p5sound.input, 2);
190+
}
187191

188192
// it is possible to instantiate a soundfile with no path
189193
if (this.url || this.file) {
@@ -795,7 +799,6 @@ class SoundFile {
795799
* </div></code>
796800
*/
797801
pan(pval, tFromNow) {
798-
this.panPosition = pval;
799802
this.panner.pan(pval, tFromNow);
800803
}
801804

@@ -809,7 +812,7 @@ class SoundFile {
809812
* 0.0 is center and default.
810813
*/
811814
getPan() {
812-
return this.panPosition;
815+
return this.panner.getPan();
813816
}
814817

815818
/**
@@ -1264,7 +1267,7 @@ class SoundFile {
12641267
this.output = null;
12651268
}
12661269
if (this.panner) {
1267-
this.panner.disconnect();
1270+
this.panner.dispose();
12681271
this.panner = null;
12691272
}
12701273
}

test/tests/p5.Oscillator.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,8 @@ describe('p5.Oscillator', function () {
216216
it('can be panned without any delay', function (done) {
217217
let osc = new p5.Oscillator();
218218
let panner = osc.panner;
219-
expect(osc.panPosition).to.equal(0); //default value
220219
expect(osc.getPan()).to.equal(0);
221220
osc.pan(-0.3);
222-
expect(osc.panPosition).to.equal(-0.3);
223-
expect(osc.getPan()).to.equal(-0.3);
224221
if (typeof p5.soundOut.audiocontext.createStereoPanner !== 'undefined') {
225222
setTimeout(() => {
226223
expect(panner.stereoPanner.pan.value).to.be.approximately(-0.3, 0.01);
@@ -238,8 +235,6 @@ describe('p5.Oscillator', function () {
238235
let osc = new p5.Oscillator();
239236
osc.pan(0.7, 0.1);
240237
let panner = osc.panner;
241-
expect(osc.panPosition).to.equal(0.7);
242-
expect(osc.getPan()).to.equal(0.7);
243238
if (typeof p5.soundOut.audiocontext.createStereoPanner !== 'undefined') {
244239
setTimeout(() => {
245240
expect(panner.stereoPanner.pan.value).to.not.be.approximately(
@@ -252,7 +247,7 @@ describe('p5.Oscillator', function () {
252247
0.01
253248
);
254249
done();
255-
}, 50);
250+
}, 60);
256251
}, 50);
257252
} else {
258253
setTimeout(() => {
@@ -268,7 +263,7 @@ describe('p5.Oscillator', function () {
268263
expect(panner.left.gain.value).to.be.approximately(0.972, 0.001);
269264
expect(panner.right.gain.value).to.be.approximately(0.233, 0.001);
270265
done();
271-
}, 100);
266+
}, 60);
272267
}, 50);
273268
}
274269
});

test/tests/p5.Panner.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// const expect = chai.expect;
1+
const expect = chai.expect;
22

33
describe('p5.Panner', function () {
44
let ac, output, input;
@@ -8,22 +8,28 @@ describe('p5.Panner', function () {
88
input = ac.createGain();
99
});
1010
it('can be created', function () {
11-
new p5.Panner(input, output);
11+
new p5.Panner();
1212
});
1313
it('can be connected and disconnected', function () {
14-
let panner = new p5.Panner(input, output);
14+
let panner = new p5.Panner();
1515
panner.connect(input);
1616
panner.disconnect();
1717
});
18-
it('can be panned without a delay', function () {
19-
let panner = new p5.Panner(input, output);
18+
it('can be panned without a delay', function (done) {
19+
let panner = new p5.Panner();
2020
panner.pan(0.4);
21-
panner.pan(0.3, 0);
22-
//TODO: to check the value of left gain/ right gain (or) the stereoPanner.pan
21+
setTimeout(() => {
22+
expect(panner.getPan()).to.be.approximately(0.4, 0.01);
23+
done();
24+
}, 25);
2325
});
24-
it('can be panned with a delay', function () {
26+
it('can be panned with a delay', function (done) {
2527
let panner = new p5.Panner(input, output);
26-
panner.pan(0.4, 10);
28+
panner.pan(-0.7, 0.1);
29+
setTimeout(() => {
30+
expect(panner.getPan()).to.be.approximately(-0.7, 0.01);
31+
done();
32+
}, 125);
2733
});
2834
it('can take inputChannels as 1 or 2', function () {
2935
let panner = new p5.Panner(input, output);

test/tests/p5.SoundFile.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('p5.SoundFile', function () {
3030
expect(sf.pauseTime).to.equal(0);
3131
expect(sf.mode).to.equal('sustain');
3232
expect(sf.startMillis).to.be.null;
33-
expect(sf.panPosition).to.equal(0);
33+
expect(sf.getPan()).to.equal(0);
3434
expect(sf.panner).to.have.property('stereoPanner');
3535
expect(p5.soundOut.soundArray).to.include(sf);
3636

@@ -507,13 +507,13 @@ describe('p5.SoundFile', function () {
507507
let sf = new p5.SoundFile();
508508
expect(sf.getPan()).to.equal(0);
509509
sf.pan(0.32);
510-
expect(sf.panPosition).to.equal(0.32);
511-
expect(sf.getPan()).to.equal(0.32);
510+
setTimeout(() => {
511+
expect(sf.getPan()).to.equal(0.32);
512+
}, 5);
512513
//with delay
513514
let sf2 = new p5.SoundFile();
514515
sf2.pan(-0.89, 0.1);
515516
setTimeout(() => {
516-
expect(sf2.panPosition).to.equal(-0.89);
517517
expect(sf2.getPan()).to.equal(-0.89);
518518
}, 100);
519519
});

0 commit comments

Comments
 (0)