summaryrefslogtreecommitdiffstats
path: root/CheapSynth_FM4osc2Poly_v0_5_6/ADSRslow.h
blob: 760d136e2768c07c2b7e24d28a3782856c3b3836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
 * ADSR.h
 *
 * Copyright 2012 Tim Barrass.
 *
 * This file is part of Mozzi.
 *
 * Mozzi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mozzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Mozzi.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef ADSR_H_
#define ADSR_H_

#include "Arduino.h"
//#include <util/atomic.h>
#include "Line.h"
#include "mozzi_fixmath.h"

/** A simple ADSR envelope generator.
@todo Test whether using the template parameter makes any difference to speed, 
and rationalise which units which do and don't need them.  
Template objects are messy when you try to use pointers to them, 
you have to include the whole template shebang in the pointer handling.
*/
template <unsigned int CONTROL_UPDATE_RATE>
class ADSR
{
private:

	const unsigned int AUDIO_TICKS_PER_CONTROL;

	unsigned int phase_control_step_counter;
	unsigned int phase_num_control_steps;

	enum {ATTACK,DECAY,SUSTAIN,RELEASE,IDLE};
	
	
	struct phase{
		byte phase_type;
		unsigned int control_steps;
		unsigned long audio_steps;
		Q8n0 level;
	}attack,decay,sustain,release,idle;
	
	phase * current_phase;
	
	// Linear audio rate transitions for envelope
	Line <unsigned long> transition;

	inline
	unsigned int convertMsecToControlSteps(unsigned int msec){
		return (uint) (((ulong)msec*CONTROL_UPDATE_RATE)>>10); // approximate /1000 with shift
	}

	inline
	void setPhase(phase * next_phase) {
		phase_control_step_counter = 0;
		phase_num_control_steps = next_phase->control_steps;
		transition.set(Q8n0_to_Q16n16(next_phase->level),next_phase-> control_steps);
		current_phase = next_phase;
	}
	
	
	
	inline
	void checkForAndSetNextPhase(phase * next_phase) {
		if (++phase_control_step_counter >= phase_num_control_steps){
			setPhase(next_phase);
		}
	}
				
	
	inline
	void checkForAndSetIdle() {
		if (++phase_control_step_counter >= phase_num_control_steps){
			transition.set(0,0,1);
			current_phase = &idle;
		}
	}
	
	
	
inline
	void setTime(phase * p, unsigned int msec)
	{
		p->control_steps=convertMsecToControlSteps(msec);
		p->audio_steps = (ulong) p->control_steps * AUDIO_TICKS_PER_CONTROL;
	}
	
	
public:

	/** Constructor.
	 */
	ADSR():AUDIO_TICKS_PER_CONTROL(AUDIO_RATE/CONTROL_UPDATE_RATE)
	{
		attack.phase_type = ATTACK;
		decay.phase_type = DECAY;
		sustain.phase_type = SUSTAIN;
		release.phase_type = RELEASE;
		idle.phase_type = IDLE;
		release.level = 0;
	}


/** Updates the internal controls of the ADSR.
	Call this in updateControl().
	*/
	void update(){ // control rate
		
		switch(current_phase->phase_type) {
			
			case ATTACK:
				checkForAndSetNextPhase(&decay);
				break;

			case DECAY:
					checkForAndSetNextPhase(&sustain);
				break;
					
			case SUSTAIN:
					checkForAndSetNextPhase(&release);										
				break;
					
			case RELEASE:
					checkForAndSetIdle();							
				break;

		}	
	}

	/** Advances one audio step along the ADSR and returns the level.
	Call this in updateAudio().
	@return the next value, as an unsigned int.
	 */
	inline
	unsigned int next()
	{
		return Q16n16_to_Q16n0(transition.next());
	}



	/** Start the attack phase of the ADSR.  THis will restart the ADSR no matter what phase it is up to.
	*/
	inline
	void noteOn(){
		setPhase(&attack);
	}



	/** Start the release phase of the ADSR.
	@todo fix release for rate rather than steps (time), so it releases at the same rate whatever the current level.
	*/
	inline
	void noteOff(){   		
		setPhase(&release);
	}





	/** Set the attack level of the ADSR.
	@param value the attack level.
	 */
	inline
	void setAttackLevel(byte value)
	{
		attack.level=value;
	}



	/** Set the decay level of the ADSR.
	@param value the decay level.
	*/
	inline
	void setDecayLevel(byte value)
	{
		decay.level=value;
	}


	/** Set the sustain level of the ADSR.
	@param value the sustain level.  Usually the same as the decay level, 
	for a steady sustained note.
	*/
	inline
	void setSustainLevel(byte value)
	{
		sustain.level=value;
	}
	
	/** Set the release level of the ADSR.  Normally you'd make this 0, 
	but you have the option of some other value.
	@param value the release level (normally 0).
	*/
	inline
	void setReleaseLevel(byte value)
	{
		release.level=value;
	}
	
	
	
	/** Set the attack and decay levels of the ADSR.  This assumes a conventional 
	ADSR where the sustain continues at the same level as the decay, till the release ramps to 0.
	@param attack the new attack level.
	@param value the new sustain level.
	*/
	inline
	void setADLevels(byte attack, byte decay)
	{
		setAttackLevel(attack);
		setDecayLevel(decay);
		setSustainLevel(decay);
		setReleaseLevel(0);
	}
	


	
	
	
	
	/** Set the attack time of the ADSR in milliseconds.
	The actual time taken will be resolved within the resolution of CONTROL_RATE.
	@param value the unsigned int attack time in milliseconds.
	 */
	inline
	void setAttackTime(unsigned int msec)
	{
		setTime(&attack, msec);
	}


	/** Set the decay time of the ADSR in milliseconds.
	The actual time taken will be resolved within the resolution of CONTROL_RATE.
	@param value the unsigned int decay time in milliseconds.
	*/
	inline
	void setDecayTime(unsigned int msec)
	{
		setTime(&decay, msec);
	}

	
	/** Set the sustain time of the ADSR in milliseconds.
	The actual time taken will be resolved within the resolution of CONTROL_RATE.
	The sustain phase will finish if the ADSR recieves a noteOff().
	@param value the unsigned int sustain time in milliseconds.
	*/
	inline
	void setSustainTime(unsigned int msec)
	{
		setTime(&sustain, msec);
	}
	
	

	/** Set the release time of the ADSR in milliseconds.
	The actual time taken will be resolved within the resolution of CONTROL_RATE.
	@param value the unsigned int release time in milliseconds.
	*/
	inline
	void setReleaseTime(unsigned int msec)
	{
		setTime(&release, msec);
	}
	


	/** Set the attack, decay and release times of the ADSR in milliseconds.
	The actual times will be resolved within the resolution of CONTROL_RATE.
	@param attack_ms the new attack time in milliseconds.
	@param decay_ms the new decay time in milliseconds.
	@param decay_ms the new sustain time in milliseconds.
	@param release_ms the new release time in milliseconds.
	*/
	inline
	void setTimes(unsigned int attack_ms, unsigned int decay_ms, unsigned int sustain_ms, unsigned int release_ms)
	{
		setAttackTime(attack_ms);
		setDecayTime(decay_ms);
		setSustainTime(sustain_ms);
		setReleaseTime(release_ms);
	}


};

#endif /* ADSR_H_ */