forked from sensorium/Mozzi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Ead.h
172 lines (143 loc) · 4.14 KB
/
Ead.h
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
/*
* Ead.h
*
* Adapted from ead~.c puredata external (creb library)
*
* This file is part of Mozzi.
*
* Copyright (c) 2000-2003 by Tom Schouten
* Copyright 2012 Tim Barrass
* Copyright 2012-2024 Tim Barrass and the Mozzi Team
*
* Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
*
*/
#ifndef EAD_H_
#define EAD_H_
#include "math.h"
#include "mozzi_fixmath.h"
/** Exponential attack decay envelope. This produces a natural sounding
envelope. It calculates a new value each time next() is called, which can be
mapped to other parameters to change the amplitude or timbre of a sound.
@note Currently doesn't work at audio rate... may need larger number
types for Q8n8attack and Q8n8decay ?
*/
class Ead
{
public:
/** Constructor
@param update_rate
Usually this will be MOZZI_CONTROL_RATE or MOZZI_AUDIO_RATE, unless you
design another scheme for updating. One such alternative scheme could take turns
for various control changes in a rotating schedule to spread out calculations
made in successive updateControl() routines.
*/
Ead(unsigned int update_rate) : UPDATE_RATE(update_rate)
{
;
}
/** Set the attack time in milliseconds.
@param attack_ms The time taken for values returned by successive calls of
the next() method to change from 0 to 255.
*/
inline
void setAttack(unsigned int attack_ms)
{
Q8n8attack = float_to_Q8n8(millisToOneMinusRealPole(attack_ms));
}
/** Set the decay time in milliseconds.
@param decay_ms The time taken for values returned by successive calls of
the next() method to change from 255 to 0.
*/
inline
void setDecay(unsigned int decay_ms)
{
Q8n8decay = float_to_Q8n8(millisToOneMinusRealPole(decay_ms));
}
/** Set attack and decay times in milliseconds.
@param attack_ms The time taken for values returned by successive calls of
the next() method to change from 0 to 255.
@param decay_ms The time taken for values returned by successive calls of
the next() method to change from 255 to 0.
*/
inline
void set(unsigned int attack_ms, unsigned int decay_ms)
{
setAttack(attack_ms);
setDecay(decay_ms);
}
/** Start the envelope from the beginning.
This can be used at any time, even if the previous envelope is not finished.
*/
inline
void start()
{
Q8n24state = 0;
attack_phase = true;
}
/** Set attack and decay times in milliseconds, and start the envelope from the beginning.
This can be used at any time, even if the previous envelope is not finished.
@param attack_ms The time taken for values returned by successive calls of
the next() method to change from 0 to 255.
@param decay_ms The time taken for values returned by successive calls of
the next() method to change from 255 to 0.
*/
inline
void start(unsigned int attack_ms, unsigned int decay_ms)
{
set(attack_ms, decay_ms);
//Q8n24state = 0; // don't restart from 0, just go from whatever the current level is, to avoid glitches
attack_phase = true;
}
/** Calculate and return the next envelope value, in the range -128 to 127
@note Timing: 5us
*/
inline
uint8_t next()
{
if(attack_phase)
{
// multiply A(a1,b1) * A(a2,b2) = A(a1+a2, b1+b2)
Q8n24state += (((Q8n24)(Q8n24_FIX1 - Q8n24state) * Q8n8attack)) >> 8; // Q8n24, shifts all back into n24
if (Q8n24state >= Q8n24_FIX1-256)
{
Q8n24state = Q8n24_FIX1-256;
attack_phase = false;
}
}else{ /* decay phase */
Q8n24state -= (Q8n24state * Q8n8decay)>>8;
}
return Q8n24_to_Q0n8(Q8n24state);
}
private:
Q8n8 Q8n8attack;
Q8n8 Q8n8decay;
Q8n24 Q8n24state;
bool attack_phase;
const unsigned int UPDATE_RATE;
/* convert milliseconds to 1-p, with p a real pole */
inline
float millisToOneMinusRealPole(unsigned int milliseconds)
{
static const float NUMERATOR = 1000.0f * log(0.001f);
return -expm1(NUMERATOR / ((float)UPDATE_RATE * milliseconds));
}
// Compute exp(x) - 1 without loss of precision for small values of x.
inline
float expm1(float x)
{
if (fabs(x) < 1e-5)
{
return x + 0.5*x*x;
}
else
{
return exp(x) - 1.0;
}
}
};
/**
@example 07.Envelopes/Ead_Envelope/Ead_Envelope.ino
This is an example of how to use the Ead class.
*/
#endif /* EAD_H_ */