Skip to content

Commit 8a3938b

Browse files
committed
added exponential envelopes, and altered the input settings for envelopes to conform to ADSR
1 parent 59cb8e8 commit 8a3938b

File tree

1 file changed

+110
-91
lines changed

1 file changed

+110
-91
lines changed

src/env.js

Lines changed: 110 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ define(function (require) {
55
var Add = require('Tone/signal/Add');
66
var Mult = require('Tone/signal/Multiply');
77
var Scale = require('Tone/signal/Scale');
8+
var TimelineSignal = require('Tone/signal/TimelineSignal');
89

910
var Tone = require('Tone/core/Tone');
1011
Tone.setContext( p5sound.audiocontext);
@@ -69,6 +70,7 @@ define(function (require) {
6970
* </code></div>
7071
*/
7172
p5.Env = function(t1, l1, t2, l2, t3, l3, t4, l4){
73+
var now = p5sound.audiocontext.currentTime;
7274

7375
/**
7476
* @property attackTime
@@ -82,38 +84,34 @@ define(function (require) {
8284
* @property decayTime
8385
*/
8486
this.dTime = t2 || 0;
85-
/**
86-
* @property decayLevel
87-
*/
88-
this.dLevel = l2 || 0;
89-
/**
90-
* @property sustainTime
91-
*/
92-
this.sTime = t3 || 0;
9387
/**
9488
* @property sustainLevel
9589
*/
96-
this.sLevel = l3 || 0;
90+
this.sLevel = l2 || 0;
9791
/**
9892
* @property releaseTime
9993
*/
100-
this.rTime = t4 || 0;
94+
this.rTime = t3 || 0;
10195
/**
10296
* @property releaseLevel
10397
*/
104-
this.rLevel = l4 || 0;
98+
this.rLevel = l3 || 0;
99+
105100

106101
this.output = p5sound.audiocontext.createGain();;
107102

108-
this.control = new p5.Signal();
109-
103+
this.control = new TimelineSignal();
110104
this.control.connect(this.output);
105+
this.control.setValueAtTime(0, now);
111106

112107
this.connection = null; // store connection
113108

114109
//array of math operation signal chaining
115110
this.mathOps = [this.control];
116111

112+
//whether envelope should be linear or exponential curve
113+
this.isExponential = false;
114+
117115
// oscillator or buffer source to clear on env complete
118116
// to save resources if/when it is retriggered
119117
this.sourceToClear = null;
@@ -135,22 +133,19 @@ define(function (require) {
135133
* @param {Number} aLevel Typically an amplitude between
136134
* 0.0 and 1.0
137135
* @param {Number} dTime Time
138-
* @param {Number} [dLevel] Amplitude (In a standard ADSR envelope,
136+
* @param {Number} [sLevel] Amplitude (In a standard ADSR envelope,
139137
* decayLevel = sustainLevel)
140-
* @param {Number} [sTime] Time (in seconds)
141-
* @param {Number} [sLevel] Amplitude 0.0 to 1.0
142-
* @param {Number} [rTime] Time (in seconds)
138+
* @param {Number} [rTime] Release Time (in seconds)
143139
* @param {Number} [rLevel] Amplitude 0.0 to 1.0
140+
144141
*/
145-
p5.Env.prototype.set = function(t1, l1, t2, l2, t3, l3, t4, l4){
142+
p5.Env.prototype.set = function(t1, l1, t2, l2, t3, l3){
146143
this.aTime = t1;
147144
this.aLevel = l1;
148145
this.dTime = t2 || 0;
149-
this.dLevel = l2 || 0;
150-
this.sTime = t3 || 0;
151-
this.sLevel = l3 || 0;
152-
this.rTime = t4 || 0;
153-
this.rLevel = l4 || 0;
146+
this.sLevel = l2 || 0;
147+
this.rTime = t3 || 0;
148+
this.rLevel = l3 || 0;
154149
};
155150

156151
/**
@@ -169,6 +164,19 @@ define(function (require) {
169164
}
170165
};
171166

167+
p5.Env.prototype.setExp = function(isExp){
168+
this.isExponential = isExp;
169+
}
170+
171+
//protect against zero values being sent to exponential functions
172+
p5.Env.prototype.checkExpInput = function(value) {
173+
if (value <= 0)
174+
{
175+
value = 0.0001;
176+
}
177+
return value;
178+
};
179+
172180
p5.Env.prototype.ctrl = function(unit){
173181
this.connect(unit);
174182
};
@@ -197,21 +205,8 @@ define(function (require) {
197205
}
198206
}
199207

200-
var currentVal = this.control.getValue();
201-
this.control.cancelScheduledValues(t);
202-
this.control.linearRampToValueAtTime(currentVal, t);
203-
204-
// attack
205-
this.control.linearRampToValueAtTime(this.aLevel, t + this.aTime);
206-
// decay to decay level
207-
this.control.linearRampToValueAtTime(this.dLevel, t + this.aTime + this.dTime);
208-
// hold sustain level
209-
this.control.linearRampToValueAtTime(this.sLevel, t + this.aTime + this.dTime + this.sTime);
210-
// release
211-
this.control.linearRampToValueAtTime(this.rLevel, t + this.aTime + this.dTime + this.sTime + this.rTime);
212-
213-
var clearTime = (t + this.aTime + this.dTime + this.sTime + this.rTime); //* 1000;
214-
208+
this.triggerAttack(unit, secondsFromNow);
209+
this.triggerRelease(unit, secondsFromNow + this.aTime + this.dTime);
215210
};
216211

217212
/**
@@ -233,27 +228,62 @@ define(function (require) {
233228
this.lastAttack = t;
234229
this.wasTriggered = true;
235230

236-
// we should set current value, but this is not working on Firefox
237-
var currentVal = this.control.getValue();
238-
console.log(currentVal);
239-
this.control.cancelScheduledValues(t);
240-
this.control.linearRampToValueAtTime(currentVal, t);
241-
242231
if (unit) {
243232
if (this.connection !== unit) {
244233
this.connect(unit);
245234
}
246235
}
247236

248-
this.control.linearRampToValueAtTime(this.aLevel, t + this.aTime);
237+
// get and set value (with linear ramp) to anchor automation
238+
var valToSet = this.control.getValueAtTime(t);
239+
if (this.isExponential == true)
240+
{
241+
this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
242+
}
243+
else
244+
{
245+
this.control.linearRampToValueAtTime(valToSet, t);
246+
}
247+
248+
// after each ramp completes, cancel scheduled values
249+
// (so they can be overridden in case env has been re-triggered)
250+
// then, set current value (with linearRamp to avoid click)
251+
// then, schedule the next automation...
249252

250253
// attack
251-
this.control.linearRampToValueAtTime(this.aLevel, t + this.aTime);
252-
// decay to sustain level
253-
this.control.linearRampToValueAtTime(this.dLevel, t + this.aTime + this.dTime);
254-
255-
this.control.linearRampToValueAtTime(this.sLevel, t + this.aTime + this.dTime + this.sTime);
254+
t += this.aTime;
255+
256+
if (this.isExponential == true)
257+
{
258+
this.control.exponentialRampToValueAtTime(this.checkExpInput(this.aLevel), t);
259+
valToSet = this.checkExpInput(this.control.getValueAtTime(t));
260+
this.control.cancelScheduledValues(t);
261+
this.control.exponentialRampToValueAtTime(valToSet, t);
262+
}
263+
else
264+
{
265+
this.control.linearRampToValueAtTime(this.aLevel, t);
266+
valToSet = this.control.getValueAtTime(t);
267+
this.control.cancelScheduledValues(t);
268+
this.control.linearRampToValueAtTime(valToSet, t);
269+
}
256270

271+
// decay to decay level
272+
t += this.dTime;
273+
if (this.isExponential == true)
274+
{
275+
this.control.exponentialRampToValueAtTime(this.checkExpInput(this.sLevel), t);
276+
valToSet = this.checkExpInput(this.control.getValueAtTime(t));
277+
this.control.cancelScheduledValues(t);
278+
this.control.exponentialRampToValueAtTime(valToSet, t);
279+
}
280+
else
281+
{
282+
this.control.linearRampToValueAtTime(this.sLevel, t);
283+
valToSet = this.control.getValueAtTime(t);
284+
this.control.cancelScheduledValues(t);
285+
this.control.linearRampToValueAtTime(valToSet, t);
286+
}
257287
};
258288

259289
/**
@@ -275,57 +305,40 @@ define(function (require) {
275305
var now = p5sound.audiocontext.currentTime;
276306
var tFromNow = secondsFromNow || 0;
277307
var t = now + tFromNow;
278-
var relTime;
279308

280309
if (unit) {
281310
if (this.connection !== unit) {
282311
this.connect(unit);
283312
}
284313
}
285314

286-
this.control.cancelScheduledValues(t);
287-
288-
// ideally would get & set currentValue here,
289-
// but this.control._scalar.gain.value not working in firefox
290-
291-
// release based on how much time has passed since this.lastAttack
292-
if ( (t - this.lastAttack) < (this.aTime) ) {
293-
var a = this.aTime - (t - this.lastAttack);
294-
this.control.linearRampToValueAtTime(this.aLevel, t + a);
295-
this.control.linearRampToValueAtTime(this.dLevel, t + a + this.dTime);
296-
this.control.linearRampToValueAtTime(this.sLevel, t + a + this.dTime + this.sTime);
297-
this.control.linearRampToValueAtTime(this.rLevel, t + a + this.dTime + this.sTime + this.rTime);
298-
relTime = t + this.dTime + this.sTime + this.rTime;
315+
// get and set value (with linear or exponential ramp) to anchor automation
316+
var valToSet = this.control.getValueAtTime(t);
317+
if (this.isExponential == true)
318+
{
319+
this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
299320
}
300-
else if ( (t - this.lastAttack) < (this.aTime + this.dTime) ) {
301-
var d = this.aTime + this.dTime - (now - this.lastAttack);
302-
this.control.linearRampToValueAtTime(this.dLevel, t + d);
303-
// this.control.linearRampToValueAtTime(this.sLevel, t + d + this.sTime);
304-
this.control.linearRampToValueAtTime(this.sLevel, t + d + 0.01);
305-
this.control.linearRampToValueAtTime(this.rLevel, t + d + 0.01 + this.rTime);
306-
relTime = t + this.sTime + this.rTime;
307-
}
308-
else if ( (t - this.lastAttack) < (this.aTime + this.dTime + this.sTime) ) {
309-
var s = this.aTime + this.dTime + this.sTime - (now - this.lastAttack);
310-
this.control.linearRampToValueAtTime(this.sLevel, t + s);
311-
this.control.linearRampToValueAtTime(this.rLevel, t + s + this.rTime);
312-
relTime = t + this.rTime;
321+
else
322+
{
323+
this.control.linearRampToValueAtTime(valToSet, t);
313324
}
314-
else {
315-
this.control.linearRampToValueAtTime(this.sLevel, t);
316-
this.control.linearRampToValueAtTime(this.rLevel, t + this.rTime);
317-
relTime = t + this.dTime + this.sTime + this.rTime;
325+
326+
// release
327+
t += this.rTime;
328+
329+
if (this.isExponential == true)
330+
{
331+
this.control.exponentialRampToValueAtTime(this.checkExpInput(this.rLevel), t);
332+
valToSet = this.checkExpInput(this.control.getValueAtTime(t));
333+
this.control.cancelScheduledValues(t);
334+
this.control.exponentialRampToValueAtTime(valToSet, t);
318335
}
319-
320-
// clear osc / sources
321-
var clearTime = (t + this.aTime + this.dTime + this.sTime + this.rTime); // * 1000;
322-
323-
if (this.connection && this.connection.hasOwnProperty('oscillator')) {
324-
this.sourceToClear = this.connection.oscillator;
325-
this.sourceToClear.stop(clearTime + .01);
326-
} else if (this.connect && this.connection.hasOwnProperty('source')){
327-
this.sourceToClear = this.connection.source;
328-
this.sourceToClear.stop(clearTime + .01);
336+
else
337+
{
338+
this.control.linearRampToValueAtTime(this.rLevel, t);
339+
valToSet = this.control.getValueAtTime(t);
340+
this.control.cancelScheduledValues(t);
341+
this.control.linearRampToValueAtTime(valToSet, t);
329342
}
330343

331344
this.wasTriggered = false;
@@ -360,6 +373,8 @@ define(function (require) {
360373
this.output.disconnect();
361374
};
362375

376+
377+
363378
// Signal Math
364379

365380
/**
@@ -419,6 +434,10 @@ define(function (require) {
419434

420435
// get rid of the oscillator
421436
p5.Env.prototype.dispose = function() {
437+
// remove reference from soundArray
438+
var index = p5sound.soundArray.indexOf(this);
439+
p5sound.soundArray.splice(index, 1);
440+
422441
var now = p5sound.audiocontext.currentTime;
423442
this.disconnect();
424443
try{

0 commit comments

Comments
 (0)