Mozzi  version v2.0
sound synthesis library for Arduino
Ead.h
1 /*
2  * Ead.h
3  *
4  * Adapted from ead~.c puredata external (creb library)
5  *
6  * This file is part of Mozzi.
7  *
8  * Copyright (c) 2000-2003 by Tom Schouten
9  * Copyright 2012 Tim Barrass
10  * Copyright 2012-2024 Tim Barrass and the Mozzi Team
11  *
12  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
13  *
14  */
15 
16 #ifndef EAD_H_
17 #define EAD_H_
18 
19 #include "math.h"
20 #include "mozzi_fixmath.h"
21 
22 
23 /** Exponential attack decay envelope. This produces a natural sounding
24 envelope. It calculates a new value each time next() is called, which can be
25 mapped to other parameters to change the amplitude or timbre of a sound.
26 @note Currently doesn't work at audio rate... may need larger number
27 types for Q8n8attack and Q8n8decay ?
28 */
29 
30 class Ead
31 {
32 
33 public:
34 
35  /** Constructor
36  @param update_rate
37  Usually this will be MOZZI_CONTROL_RATE or MOZZI_AUDIO_RATE, unless you
38  design another scheme for updating. One such alternative scheme could take turns
39  for various control changes in a rotating schedule to spread out calculations
40  made in successive updateControl() routines.
41  */
42  Ead(unsigned int update_rate) : UPDATE_RATE(update_rate)
43  {
44  ;
45  }
46 
47  /** Set the attack time in milliseconds.
48  @param attack_ms The time taken for values returned by successive calls of
49  the next() method to change from 0 to 255.
50  */
51  inline
52  void setAttack(unsigned int attack_ms)
53  {
54  Q8n8attack = float_to_Q8n8(millisToOneMinusRealPole(attack_ms));
55  }
56 
57 
58  /** Set the decay time in milliseconds.
59  @param decay_ms The time taken for values returned by successive calls of
60  the next() method to change from 255 to 0.
61  */
62  inline
63  void setDecay(unsigned int decay_ms)
64  {
65  Q8n8decay = float_to_Q8n8(millisToOneMinusRealPole(decay_ms));
66  }
67 
68 
69  /** Set attack and decay times in milliseconds.
70  @param attack_ms The time taken for values returned by successive calls of
71  the next() method to change from 0 to 255.
72  @param decay_ms The time taken for values returned by successive calls of
73  the next() method to change from 255 to 0.
74  */
75  inline
76  void set(unsigned int attack_ms, unsigned int decay_ms)
77  {
78  setAttack(attack_ms);
79  setDecay(decay_ms);
80  }
81 
82 
83  /** Start the envelope from the beginning.
84  This can be used at any time, even if the previous envelope is not finished.
85  */
86  inline
87  void start()
88  {
89  Q8n24state = 0;
90  attack_phase = true;
91  }
92 
93 
94  /** Set attack and decay times in milliseconds, and start the envelope from the beginning.
95  This can be used at any time, even if the previous envelope is not finished.
96  @param attack_ms The time taken for values returned by successive calls of
97  the next() method to change from 0 to 255.
98  @param decay_ms The time taken for values returned by successive calls of
99  the next() method to change from 255 to 0.
100  */
101  inline
102  void start(unsigned int attack_ms, unsigned int decay_ms)
103  {
104  set(attack_ms, decay_ms);
105  //Q8n24state = 0; // don't restart from 0, just go from whatever the current level is, to avoid glitches
106  attack_phase = true;
107  }
108 
109 
110  /** Calculate and return the next envelope value, in the range -128 to 127
111  @note Timing: 5us
112  */
113 
114  inline
116  {
117  if(attack_phase)
118  {
119  // multiply A(a1,b1) * A(a2,b2) = A(a1+a2, b1+b2)
120  Q8n24state += (((Q8n24)(Q8n24_FIX1 - Q8n24state) * Q8n8attack)) >> 8; // Q8n24, shifts all back into n24
121  if (Q8n24state >= Q8n24_FIX1-256)
122  {
123  Q8n24state = Q8n24_FIX1-256;
124  attack_phase = false;
125  }
126  }else{ /* decay phase */
127  Q8n24state -= (Q8n24state * Q8n8decay)>>8;
128  }
129  return Q8n24_to_Q0n8(Q8n24state);
130  }
131 
132 
133 private:
134 
135  Q8n8 Q8n8attack;
136  Q8n8 Q8n8decay;
137  Q8n24 Q8n24state;
138  bool attack_phase;
139  const unsigned int UPDATE_RATE;
140 
141 
142  /* convert milliseconds to 1-p, with p a real pole */
143  inline
144  float millisToOneMinusRealPole(unsigned int milliseconds)
145  {
146  static const float NUMERATOR = 1000.0f * log(0.001f);
147  return -expm1(NUMERATOR / ((float)UPDATE_RATE * milliseconds));
148  }
149 
150 
151  // Compute exp(x) - 1 without loss of precision for small values of x.
152  inline
153  float expm1(float x)
154  {
155  if (fabs(x) < 1e-5)
156  {
157  return x + 0.5*x*x;
158  }
159  else
160  {
161  return exp(x) - 1.0;
162  }
163  }
164 
165 };
166 
167 /**
168 @example 07.Envelopes/Ead_Envelope/Ead_Envelope.ino
169 This is an example of how to use the Ead class.
170 */
171 
172 #endif /* EAD_H_ */