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