Skip to content

Commit 940f6fa

Browse files
committed
fix #266 polysynth voice count and indexing
1 parent 4048e0a commit 940f6fa

File tree

1 file changed

+36
-37
lines changed

1 file changed

+36
-37
lines changed

src/polysynth.js

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ define(function (require) {
66
var TimelineSignal = require('Tone/signal/TimelineSignal');
77
require('sndcore');
88

9+
910
/**
1011
* An AudioVoice is used as a single voice for sound synthesis.
1112
* The PolySynth class holds an array of AudioVoice, and deals
1213
* with voices allocations, with setting notes to be played, and
13-
* parameters to be set.
14+
* parameters to be set.
1415
*
1516
* @class p5.PolySynth
1617
* @constructor
17-
*
18+
*
1819
* @param {Number} [synthVoice] A monophonic synth voice inheriting
1920
* the AudioVoice class. Defaults to p5.MonoSynth
20-
* @param {Number} [polyValue] Number of voices, defaults to 8;
21+
* @param {Number} [maxVoices] Number of voices, defaults to 8;
2122
*
2223
*
2324
* @example
@@ -32,19 +33,19 @@ define(function (require) {
3233
* polysynth.play(74,1,0,3);
3334
* }
3435
* </code></div>
35-
*
36+
*
3637
**/
37-
p5.PolySynth = function(audioVoice, polyValue) {
38-
//audiovoices will contain polyValue many monophonic synths
38+
p5.PolySynth = function(audioVoice, maxVoices) {
39+
//audiovoices will contain maxVoices many monophonic synths
3940
this.audiovoices = [];
4041

4142
/**
42-
* An object that holds information about which notes have been played and
43+
* An object that holds information about which notes have been played and
4344
* which notes are currently being played. New notes are added as keys
4445
* on the fly. While a note has been attacked, but not released, the value of the
4546
* key is the audiovoice which is generating that note. When notes are released,
46-
* the value of the key becomes undefined.
47-
* @property notes
47+
* the value of the key becomes undefined.
48+
* @property notes
4849
*/
4950
this.notes = {};
5051

@@ -56,7 +57,7 @@ define(function (require) {
5657
* A PolySynth must have at least 1 voice, defaults to 8
5758
* @property polyvalue
5859
*/
59-
this.polyValue = polyValue || 8;
60+
this.maxVoices = maxVoices || 8;
6061

6162
/**
6263
* Monosynth that generates the sound for each note that is triggered. The
@@ -87,7 +88,7 @@ define(function (require) {
8788
* @method _allocateVoices
8889
*/
8990
p5.PolySynth.prototype._allocateVoices = function() {
90-
for(var i = 0; i< this.polyValue; i++) {
91+
for(var i = 0; i< this.maxVoices; i++) {
9192
this.audiovoices.push(new this.AudioVoice());
9293
this.audiovoices[i].disconnect();
9394
this.audiovoices[i].connect(this.output);
@@ -96,7 +97,7 @@ define(function (require) {
9697

9798
/**
9899
* Play a note by triggering noteAttack and noteRelease with sustain time
99-
*
100+
*
100101
* @method play
101102
* @param {Number} [note] midi note to play (ranging from 0 to 127 - 60 being a middle C)
102103
* @param {Number} [velocity] velocity of the note to play (ranging from 0 to 1)
@@ -110,12 +111,12 @@ define(function (require) {
110111
};
111112

112113

113-
/**
114+
/**
114115
* noteADSR sets the envelope for a specific note that has just been triggered.
115116
* Using this method modifies the envelope of whichever audiovoice is being used
116117
* to play the desired note. The envelope should be reset before noteRelease is called
117118
* in order to prevent the modified envelope from being used on other notes.
118-
*
119+
*
119120
* @method noteADSR
120121
* @param {Number} [note] Midi note on which ADSR should be set.
121122
* @param {Number} [attackTime] Time (in seconds before envelope
@@ -141,10 +142,10 @@ define(function (require) {
141142
};
142143

143144

144-
/**
145+
/**
145146
* Set the PolySynths global envelope. This method modifies the envelopes of each
146147
* monosynth so that all notes are played with this envelope.
147-
*
148+
*
148149
* @method setADSR
149150
* @param {Number} [note] Midi note on which ADSR should be set.
150151
* @param {Number} [attackTime] Time (in seconds before envelope
@@ -170,14 +171,14 @@ define(function (require) {
170171
/**
171172
* Trigger the Attack, and Decay portion of a MonoSynth.
172173
* Similar to holding down a key on a piano, but it will
173-
* hold the sustain level until you let go.
174+
* hold the sustain level until you let go.
174175
*
175176
* @method noteAttack
176177
* @param {Number} [note] midi note on which attack should be triggered.
177178
* @param {Number} [velocity] velocity of the note to play (ranging from 0 to 1)/
178179
* @param {Number} [secondsFromNow] time from now (in seconds)
179-
*
180-
*/
180+
*
181+
*/
181182
p5.PolySynth.prototype.noteAttack = function (_note, _velocity, secondsFromNow) {
182183
var now = p5sound.audiocontext.currentTime;
183184

@@ -201,8 +202,8 @@ define(function (require) {
201202
}
202203

203204
//Check to see how many voices are in use at the time the note will start
204-
if(this._voicesInUse.getValueAtTime(t) < this.polyValue) {
205-
currentVoice = this._voicesInUse.getValueAtTime(t);
205+
if(this._voicesInUse.getValueAtTime(t) < this.maxVoices) {
206+
currentVoice = Math.max(~~this._voicesInUse.getValueAtTime(t), 0);
206207
}
207208
//If we are exceeding the polyvalue, bump off the oldest notes and replace
208209
//with a new note
@@ -211,12 +212,11 @@ define(function (require) {
211212

212213
var oldestNote = p5.prototype.freqToMidi(this.audiovoices[this._oldest].oscillator.freq().value);
213214
this.noteRelease(oldestNote);
214-
this._oldest = ( this._oldest + 1 ) % (this.polyValue - 1);
215+
this._oldest = ( this._oldest + 1 ) % (this.maxVoices - 1);
215216
}
216217

217-
//Overrite the entry in the notes object. A note (frequency value)
218+
//Overrite the entry in the notes object. A note (frequency value)
218219
//corresponds to the index of the audiovoice that is playing it
219-
220220
this.notes[note] = new TimelineSignal();
221221
this.notes[note].setValueAtTime(currentVoice,t);
222222

@@ -229,7 +229,6 @@ define(function (require) {
229229
this._updateAfter(t, 1);
230230

231231
this._newest = currentVoice;
232-
233232
//The audiovoice handles the actual scheduling of the note
234233
if (typeof velocity === 'number') {
235234
var maxRange = 1 / this._voicesInUse.getValueAtTime(t) * 2;
@@ -241,7 +240,7 @@ define(function (require) {
241240
/**
242241
* Private method to ensure accurate values of this._voicesInUse
243242
* Any time a new value is scheduled, it is necessary to increment all subsequent
244-
* scheduledValues after attack, and decrement all subsequent
243+
* scheduledValues after attack, and decrement all subsequent
245244
* scheduledValues after release
246245
*
247246
* @private
@@ -250,7 +249,6 @@ define(function (require) {
250249
* @return {[type]} [description]
251250
*/
252251
p5.PolySynth.prototype._updateAfter = function(time, value) {
253-
254252
if(this._voicesInUse._searchAfter(time) === null) {
255253
return;
256254
} else{
@@ -265,12 +263,12 @@ define(function (require) {
265263
* Trigger the Release of an AudioVoice note. This is similar to releasing
266264
* the key on a piano and letting the sound fade according to the
267265
* release level and release time.
268-
*
266+
*
269267
* @method noteRelease
270268
* @param {Number} [note] midi note on which attack should be triggered.
271269
* @param {Number} [secondsFromNow] time to trigger the release
272-
*
273-
*/
270+
*
271+
*/
274272

275273
p5.PolySynth.prototype.noteRelease = function (_note,secondsFromNow) {
276274
//Make sure note is in frequency inorder to query the this.notes object
@@ -281,24 +279,25 @@ define(function (require) {
281279
var tFromNow = secondsFromNow || 0;
282280
var t = now + tFromNow;
283281

284-
282+
285283
if (this.notes[note].getValueAtTime(t) === null) {
286284
console.warn('Cannot release a note that is not already playing');
287285
} else {
288-
286+
289287

290288
//Find the scheduled change in this._voicesInUse that will be previous to this new note
291289
//subtract 1 and schedule this value at time 't', when this note will stop playing
292-
var previousVal = this._voicesInUse._searchBefore(t) === null ? 0 : this._voicesInUse._searchBefore(t).value;
290+
var previousVal = Math.max(~~this._voicesInUse.getValueAtTime(t).value, 1);
293291
this._voicesInUse.setValueAtTime(previousVal - 1, t);
294-
//Then update all scheduled values that follow to decrease by 1
295-
this._updateAfter(t, -1);
292+
//Then update all scheduled values that follow to decrease by 1 but never go below 0
293+
if (previousVal > 0) {
294+
this._updateAfter(t, -1);
295+
}
296296

297297
this.audiovoices[ this.notes[note].getValueAtTime(t) ].triggerRelease(tFromNow);
298-
299298
this.notes[note].setValueAtTime( null, t);
300299

301-
this._newest = this._newest === 0 ? 0 : (this._newest - 1) % (this.polyValue - 1);
300+
this._newest = this._newest === 0 ? 0 : (this._newest - 1) % (this.maxVoices - 1);
302301
}
303302

304303
};

0 commit comments

Comments
 (0)