maxi_emscr.h 15.2 KB
Newer Older
Dr-Dan's avatar
Dr-Dan committed
1
2
3
4
5
6
7
8
/*
 *  maximilian.h
 *  platform independent synthesis library using portaudio or rtaudio
 *
 *  Created by Mick Grierson on 29/12/2009.
 *  Copyright 2009 Mick Grierson & Strangeloop Limited. All rights reserved.
 *	Thanks to the Goldsmiths Creative Computing Team.
 *	Special thanks to Arturo Castro for the PortAudio implementation.
Dr-Dan's avatar
Dr-Dan committed
9
 *
Dr-Dan's avatar
Dr-Dan committed
10
11
12
13
14
15
16
17
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
Dr-Dan's avatar
Dr-Dan committed
18
 *
Dr-Dan's avatar
Dr-Dan committed
19
20
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions o/Users/Dan/Downloads/Maximilian-master/stb_vorbis.c
Dr-Dan's avatar
Dr-Dan committed
21
22
23
24
25
 /Users/Dan/Downloads/Maximilian-master/stb_vorbis.h
 /Users/Dan/Downloads/Maximilian-master/player.h
 /Users/Dan/Downloads/Maximilian-master/player.cpp
 /Users/Dan/Downloads/Maximilian-master/maximilian.cpp
 /Users/Dan/Downloads/Maximilian-master/maximilian.hf the Software.
Dr-Dan's avatar
Dr-Dan committed
26
 *
Dr-Dan's avatar
Dr-Dan committed
27
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Dr-Dan's avatar
Dr-Dan committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#ifndef _max_emscr_h
#define _max_emscr_h

//#define MAXIMILIAN_PORTAUDIO
#define MAXIMILIAN_RT_AUDIO


#include <iostream>
#include <fstream>
#include <string.h>
#include <cstdlib>
#include <vector>
#include "math.h"

using namespace std;
#ifndef PI
#define PI  3.1415926535897932384626433832795
#endif
#define TWOPI 6.283185307179586476925286766559

class maxiSettings {
public:
	static int sampleRate;
	static int channels;
	static int bufferSize;
	static void setup(int initSampleRate, int initChannels, int initBufferSize) {
		maxiSettings::sampleRate = initSampleRate;
		maxiSettings::channels = initChannels;
		maxiSettings::bufferSize = initBufferSize;
	}
};


class maxiOsc {
	
	double frequency;
	double phase;
	double startphase;
	double endphase;
	double output;
	double tri;
	
	
public:
	maxiOsc();
	double sinewave(double frequency);
	double coswave(double frequency);
	double phasor(double frequency);
	double phasor(double frequency, double startphase, double endphase);
	double saw(double frequency);
	double triangle(double frequency);
	double square(double frequency);
	double pulse(double frequency, double duty);
	double noise();
	double sinebuf(double frequency);
	double sinebuf4(double frequency);
Dr-Dan's avatar
Dr-Dan committed
94
95
	double sawn(double frequency);
	double rect(double frequency, double duty=0.5);
Dr-Dan's avatar
Dr-Dan committed
96
97
98
99
100
101
102
103
104
105
106
107
108
	void phaseReset(double phaseIn);
	
};


class maxiEnvelope {
	
	double period;
	double output;
	double startval;
	double currentval;
	double nextval;
	int isPlaying;
Dr-Dan's avatar
Dr-Dan committed
109
	
Dr-Dan's avatar
Dr-Dan committed
110
public:
Dr-Dan's avatar
Dr-Dan committed
111
	//	maxiEnvelope(){}
Dr-Dan's avatar
Dr-Dan committed
112
	double line(int numberofsegments, std::vector<double>& segments);
Dr-Dan's avatar
Dr-Dan committed
113
114
	//	double line(int numberofsegments , double segments[1000]);
	
Dr-Dan's avatar
Dr-Dan committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
	void trigger(int index,double amp);
	int valindex;
	double amplitude;
	
	double GetAmplitude() const{
		return amplitude;
	}
	
	void SetAmplitude(double ampIn){
		amplitude = ampIn;
	}
	
};

129

Dr-Dan's avatar
Dr-Dan committed
130
131
132
133
134
135
136
137
138
139
140
141
142
class maxiDelayline {
	double frequency;
	int phase;
	double startphase;
	double endphase;
	double output;
	double memory[88200]; // vector??? will memset cause emscr problems???
	
public:
	maxiDelayline();
	double dl(double input, int size, double feedback);
	double dl(double input, int size, double feedback, int position);
};
143

Dr-Dan's avatar
Dr-Dan committed
144
145

// NEXT
Dr-Dan's avatar
Dr-Dan committed
146
class maxiFilter {
Dr-Dan's avatar
Dr-Dan committed
147
148
149
150
151
152
153
154
155
156
	double gain;
	double input;
	double output;
	double inputs[10];
	double outputs[10];
	double cutoff1;
	double x;//speed
	double y;//pos
	double z;//pole
	double c;//filter coefficient
Dr-Dan's avatar
Dr-Dan committed
157
	
Dr-Dan's avatar
Dr-Dan committed
158
159
160
161
162
163
164
165
166
167
168
169
public:
	maxiFilter():x(0.0), y(0.0), z(0.0), c(0.0){};
	double cutoff;
	double resonance;
	double lores(double input,double cutoff1, double resonance);
	double hires(double input,double cutoff1, double resonance);
	double bandpass(double input,double cutoff1, double resonance);
	double lopass(double input,double cutoff);
	double hipass(double input,double cutoff);
	
};

170
171
172

// most variables in this class seem to have no use ??
// all recieved as args ...
Dr-Dan's avatar
Dr-Dan committed
173
class maxiMix  {
Dr-Dan's avatar
Dr-Dan committed
174
175
176
177
178
179
	
	// these are never used???
	//	double input;
	//	double two[2];
	//	double four[4];
	//	double eight[8];
Dr-Dan's avatar
Dr-Dan committed
180

Dr-Dan's avatar
Dr-Dan committed
181
public:
Dr-Dan's avatar
Dr-Dan committed
182
183
184
	//	double x;
	//	double y;
	//	double z;
185
186
187
	/*
	 currently using non-pointer version for embind, need to sort out pointer stuff though
	 embind has no support for pointers to primitives!
Dr-Dan's avatar
Dr-Dan committed
188
189
190
191
	 */
	//	double *stereo(double input,double two[2],double x);
	//	double *quad(double input,double four[4], double x,double y);
	//	double *ambisonic(double input,double eight[8],double x,double y, double z);
192
	
193
	// as no variables are used, perhaps these should be static also?
194
	void stereo(double input,vector<double>& two,double x);
Dr-Dan's avatar
Dr-Dan committed
195
196
	void quad(double input,vector<double>& four, double x,double y);
	void ambisonic(double input,vector<double>& eight, double x,double y, double z);
Dr-Dan's avatar
Dr-Dan committed
197
};
198
199


Dr-Dan's avatar
Dr-Dan committed
200
201
202
203
204
 //lagging with an exponential moving average
 //a lower alpha value gives a slower lag
 template <class T>
 class maxiLagExp {
 public:
Dr-Dan's avatar
Dr-Dan committed
205
206
207
208
	T alpha, alphaReciprocal;
	T val;
	
	maxiLagExp() {
Dr-Dan's avatar
Dr-Dan committed
209
 init(0.5, 0.0);
Dr-Dan's avatar
Dr-Dan committed
210
	};
211
212
213
214
215

	 // haven't worked out how to do this (override) with smart_ptr_constructor?!
//	maxiLagExp(T initAlpha, T initVal) {
// init(initAlpha, initVal);
//	}
Dr-Dan's avatar
Dr-Dan committed
216
217
	
	void init(T initAlpha, T initVal) {
Dr-Dan's avatar
Dr-Dan committed
218
219
220
 alpha = initAlpha;
 alphaReciprocal = 1.0 - alpha;
 val = initVal;
Dr-Dan's avatar
Dr-Dan committed
221
	}
222
223
	 // no need for inline if declared/defined in .h file
	void addSample(T newVal) {
Dr-Dan's avatar
Dr-Dan committed
224
 val = (alpha * newVal) + (alphaReciprocal * val);
Dr-Dan's avatar
Dr-Dan committed
225
226
	}
	
227
	T value() {
Dr-Dan's avatar
Dr-Dan committed
228
 return val;
Dr-Dan's avatar
Dr-Dan committed
229
	}
Dr-Dan's avatar
Dr-Dan committed
230
 };
Dr-Dan's avatar
Dr-Dan committed
231
232


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//lagging with an exponential moving average
//a lower alpha value gives a slower lag
//template <class T>
//class maxiLagExp{
//public:
//	double alpha, alphaReciprocal;
//	double val;
//	
//	maxiLagExp() {
//		init(0.5, 0.0);
//	};
//	
//	
//		maxiLagExp(double initAlpha, double initVal) {
//	 init(initAlpha, initVal);
//		}
//	
//	void init(double initAlpha, double initVal) {
//		alpha = initAlpha;
//		alphaReciprocal = 1.0 - alpha;
//		val = initVal;
//	}
//	// no need for inline if declared/defined in .h file
//	void addSample(double newVal) {
//		val = (alpha * newVal) + (alphaReciprocal * val);
//	}
//	
//	double value() {
//		return val;
//	}
//};
Dr-Dan's avatar
Dr-Dan committed
264
265
266
267
268
269
270


class maxiSample  {
	
private:
	string 	myPath;
	int 	myChunkSize;
271
	int 	mySubChunk1Size;
Dr-Dan's avatar
Dr-Dan committed
272
273
274
275
276
277
278
279
	int		readChannel;
	short 	myFormat;
	int   	myByteRate;
	short 	myBlockAlign;
	short 	myBitsPerSample;
	double position, recordPosition;
	double speed;
	double output;
Dr-Dan's avatar
Dr-Dan committed
280
	//    maxiLagExp<double> loopRecordLag;
Dr-Dan's avatar
Dr-Dan committed
281
282
283
284
285
286
	
public:
	int	 myDataSize;
	short 	myChannels;
	int   	mySampleRate;
	long length;
Dr-Dan's avatar
Dr-Dan committed
287
288
	//	void getLength(); // ???
	long getLength();
Dr-Dan's avatar
Dr-Dan committed
289
	void setLength(unsigned long numSamples);
Dr-Dan's avatar
Dr-Dan committed
290
291
292
	
	
	char* 	myData;
Dr-Dan's avatar
Dr-Dan committed
293
	short* temp;
Dr-Dan's avatar
Dr-Dan committed
294
295
	
	// different vars for use with js
Dr-Dan's avatar
Dr-Dan committed
296
	vector<double> tempVec;
Dr-Dan's avatar
Dr-Dan committed
297
298
299
300
	
	~maxiSample()
	{
		if (myData) free(myData);
Dr-Dan's avatar
Dr-Dan committed
301
302
303
		if (temp) free(temp);
		printf("freeing SampleData");
		
Dr-Dan's avatar
Dr-Dan committed
304
305
306
307
308
309
	}
	
	maxiSample():myData(NULL),temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {};
	
	bool load(string fileName, int channel=0);
	
Dr-Dan's avatar
Dr-Dan committed
310
311
	bool isReady();
	
Dr-Dan's avatar
Dr-Dan committed
312
313
	//    bool loadOgg(string filename,int channel=0);
	//
Dr-Dan's avatar
Dr-Dan committed
314
315
316
317
318
	void trigger();
	
	// read a wav file into this class
	bool read();
	
Dr-Dan's avatar
Dr-Dan committed
319
	
Dr-Dan's avatar
Dr-Dan committed
320
	//read an ogg file into this class using stb_vorbis
Dr-Dan's avatar
Dr-Dan committed
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
	//    bool readOgg();
	//
	//    void loopRecord(double newSample, const bool recordEnabled, const double recordMix) {
	//        loopRecordLag.addSample(recordEnabled);
	//        if(recordEnabled) {
	//            double currentSample = ((short*)myData)[(unsigned long)recordPosition] / 32767.0;
	//            newSample = (recordMix * currentSample) + ((1.0 - recordMix) * newSample);
	//            newSample *= loopRecordLag.value();
	//            ((short*)myData)[(unsigned long)recordPosition] = newSample * 32767;
	//        }
	//        ++recordPosition;
	//        if (recordPosition == length)
	//            recordPosition=0;
	//    }
	//
	//    void clear();
	//
	//    void reset();
	
	//	double play();
	
	
Dr-Dan's avatar
Dr-Dan committed
343
344
345
346
	
	void setSample(vector<double>& temp);
	
	double play();
347
	void clear(){tempVec.clear();}
Dr-Dan's avatar
Dr-Dan committed
348
	
Dr-Dan's avatar
Dr-Dan committed
349
		double playOnce();
Dr-Dan's avatar
Dr-Dan committed
350
	//
351
	double playOnce(double speed);
Dr-Dan's avatar
Dr-Dan committed
352
	//
Dr-Dan's avatar
Dr-Dan committed
353
	double play(double speed);
354
355
	
	double play(double frequency, double start, double end);
Dr-Dan's avatar
Dr-Dan committed
356
357
358
	
	// why this no work with embind? :
	//	non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'
359
	double play(double frequency, double start, double end, double& pos);
Dr-Dan's avatar
Dr-Dan committed
360
361
	//
	//
362
	double play4(double frequency, double start, double end);
Dr-Dan's avatar
Dr-Dan committed
363
	//
Dr-Dan's avatar
Dr-Dan committed
364
		double bufferPlay(unsigned char &bufferin,long length);
Dr-Dan's avatar
Dr-Dan committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
	//
	//	double bufferPlay(unsigned char &bufferin,double speed,long length);
	//
	//	double bufferPlay(unsigned char &bufferin,double frequency, double start, double end);
	//
	//	double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end);
	//    bool save() {
	//        save(myPath);
	//    }
	
	//	bool save(string filename)
	//	{
	//		fstream myFile (filename.c_str(), ios::out | ios::binary);
	//
	//		// write the wav file per the wav file format
	//		myFile.seekp (0, ios::beg);
	//		myFile.write ("RIFF", 4);
	//		myFile.write ((char*) &myChunkSize, 4);
	//		myFile.write ("WAVE", 4);
	//		myFile.write ("fmt ", 4);
	//		myFile.write ((char*) &mySubChunk1Size, 4);
	//		myFile.write ((char*) &myFormat, 2);
	//		myFile.write ((char*) &myChannels, 2);
	//		myFile.write ((char*) &mySampleRate, 4);
	//		myFile.write ((char*) &myByteRate, 4);
	//		myFile.write ((char*) &myBlockAlign, 2);
	//		myFile.write ((char*) &myBitsPerSample, 2);
	//		myFile.write ("data", 4);
	//		myFile.write ((char*) &myDataSize, 4);
	//		myFile.write (myData, myDataSize);
	//
	//		return true;
	//	}
Dr-Dan's avatar
Dr-Dan committed
398
399
	
	// return a printable summary of the wav file
Dr-Dan's avatar
Dr-Dan committed
400
401
402
403
404
405
406
	//	char *getSummary()
	//	{
	//		char *summary = new char[250];
	//		sprintf(summary, " Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize);
	//		std::cout << myDataSize;
	//		return summary;
	//	}
Dr-Dan's avatar
Dr-Dan committed
407
408
409
410
411
412
413
414
415
416
	
	string getSummary()
	{
		char summary[100];
		snprintf(summary, 100," Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize);
		std::cout << myDataSize;
		return string(summary);
	}
};

417

Dr-Dan's avatar
Dr-Dan committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

class maxiMap {
public:
	static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) {
		return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin;
	}
	
	static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) {
		//clipping
		val = max(min(val, inMax), inMin);
		return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin;
	}
	
	static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) {
		//clipping
		val = max(min(val, inMax), inMin);
		return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin;
	}
	
	static int inline clamp(int v, const int low, const int high) {
		v = min(high, v);
		v = max(low, v);
		return v;
	}
	
};


class maxiDyn {
	
	
public:
	double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995);
	double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995);
	double input;
	double ratio;
	double currentRatio;
	double threshold;
	double output;
	double attack;
	double release;
	double amplitude;
	long holdtime;
	long holdcount;
	int attackphase,holdphase,releasephase;
};

465

Dr-Dan's avatar
Dr-Dan committed
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
class maxiEnv {
	
	
public:
	double ar(double input, double attack=1, double release=0.9, long holdtime=1, int trigger=0);
	double adsr(double input, double attack=1, double decay=0.99, double sustain=0.125, double release=0.9, long holdtime=1, int trigger=0);
	double input;
	double output;
	double attack;
	double decay;
	double sustain;
	double release;
	double amplitude;
	int trigger;
	long holdtime;
	long holdcount;
	int attackphase,decayphase,sustainphase,holdphase,releasephase;
};

class convert {
public:
	double mtof(int midinote);
488
	//	static double mtof(int midinote); // should be static really
Dr-Dan's avatar
Dr-Dan committed
489
490
491
492
493
494
};



class maxiDistortion {
public:
Dr-Dan's avatar
Dr-Dan committed
495
496
497
498
499
	//    atan distortion, see http://www.musicdsp.org/showArchiveComment.php?ArchiveID=104
	//    shape from 1 (soft clipping) to infinity (hard clipping)
	double atanDist(const double in, const double shape);
	double fastAtanDist(const double in, const double shape);
	double fastatan( double x );
Dr-Dan's avatar
Dr-Dan committed
500
501
502
};

inline double maxiDistortion::atanDist(const double in, const double shape) {
Dr-Dan's avatar
Dr-Dan committed
503
504
505
	double out;
	out = (1.0 / atan(shape)) * atan(in * shape);
	return out;
Dr-Dan's avatar
Dr-Dan committed
506
507
508
}

inline double maxiDistortion::fastAtanDist(const double in, const double shape) {
Dr-Dan's avatar
Dr-Dan committed
509
510
511
	double out;
	out = (1.0 / fastatan(shape)) * fastatan(in * shape);
	return out;
Dr-Dan's avatar
Dr-Dan committed
512
513
}

514
515
516
517
518
inline double maxiDistortion::fastatan(double x)
{
	return (x / (1.0 + 0.28 * (x * x)));
}

Dr-Dan's avatar
Dr-Dan committed
519
520
521

class maxiFlanger {
public:
Dr-Dan's avatar
Dr-Dan committed
522
523
524
525
526
527
528
529
	//delay = delay time - ~800 sounds good
	//feedback = 0 - 1
	//speed = lfo speed in Hz, 0.0001 - 10 sounds good
	//depth = 0 - 1
	double flange(const double input, const unsigned int delay, const double feedback, const double speed, const double depth);
	maxiDelayline dl;
	maxiOsc lfo;
	
Dr-Dan's avatar
Dr-Dan committed
530
531
};

532

Dr-Dan's avatar
Dr-Dan committed
533
534
inline double maxiFlanger::flange(const double input, const unsigned int delay, const double feedback, const double speed, const double depth)
{
Dr-Dan's avatar
Dr-Dan committed
535
536
537
538
539
540
541
	//todo: needs fixing
	double output;
	double lfoVal = lfo.triangle(speed);
	output = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ;
	double normalise = (1 - fabs(output));
	output *= normalise;
	return (output + input) / 2.0;
Dr-Dan's avatar
Dr-Dan committed
542
543
}

544

Dr-Dan's avatar
Dr-Dan committed
545
546
class maxiChorus {
public:
Dr-Dan's avatar
Dr-Dan committed
547
548
549
550
551
552
553
554
555
	//delay = delay time - ~800 sounds good
	//feedback = 0 - 1
	//speed = lfo speed in Hz, 0.0001 - 10 sounds good
	//depth = 0 - 1
	double chorus(const double input, const unsigned int delay, const double feedback, const double speed, const double depth);
	maxiDelayline dl, dl2;
	maxiOsc lfo;
	maxiFilter lopass;
	
Dr-Dan's avatar
Dr-Dan committed
556
557
558
559
};

inline double maxiChorus::chorus(const double input, const unsigned int delay, const double feedback, const double speed, const double depth)
{
Dr-Dan's avatar
Dr-Dan committed
560
561
562
563
564
565
566
567
568
	//this needs fixing
	double output1, output2;
	double lfoVal = lfo.noise();
	lfoVal = lopass.lores(lfoVal, speed, 1.0) * 2.0;
	output1 = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ;
	output2 = dl2.dl(input, (delay + (lfoVal * depth * delay * 1.02) + 1) * 0.98, feedback * 0.99) ;
	output1 *= (1.0 - fabs(output1));
	output2 *= (1.0 - fabs(output2));
	return (output1 + output2 + input) / 3.0;
Dr-Dan's avatar
Dr-Dan committed
569
570
}

571

Dr-Dan's avatar
Dr-Dan committed
572
573
class maxiEnvelopeFollower {
public:
Dr-Dan's avatar
Dr-Dan committed
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
	maxiEnvelopeFollower() {
		setAttack(100);
		setRelease(100);
		env = 0;
	}
	void setAttack(double attackMS);
	void setRelease(double releaseMS);
	inline double play(double input) {
		input = fabs(input);
		if (input>env)
			env = attack * (env - input) + input;
		else
			env = release * (env - input) + input;
		return env;
	}
Dr-Dan's avatar
Dr-Dan committed
589
590
	void reset() {env=0;}
private:
Dr-Dan's avatar
Dr-Dan committed
591
	double attack, release, env;
Dr-Dan's avatar
Dr-Dan committed
592
593
594
};

#endif