Mozzi  version v1.1.0
sound synthesis library for Arduino
MultiLine2.h
1 /*
2  * MultiLine.h
3  *
4  * Copyright 2012-2015 Tim Barrass.
5  *
6  * This file is part of Mozzi.
7  *
8  * Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
9  *
10  */
11 
12 #ifndef MultiLine_H_
13 #define MultiLine_H_
14 
15 #if ARDUINO >= 100
16  #include "Arduino.h"
17 #else
18  #include "WProgram.h"
19 #endif
20 #include "Line.h"
21 #include "mozzi_fixmath.h"
22 
23 
24 /** A simple MultiLine envelope generator. This implementation has separate update() and next()
25 methods, where next() interpolates values between each update().
26 The "normal" way to use this would be with update() in updateControl(), where it calculates a new internal state each control step,
27 and then next() is in updateAudio(), called much more often, where it interpolates between the control values.
28 This also allows the MultiLine updates to be made even more sparsely if desired, eg. every 3rd control update.
29 @tparam CONTROL_UPDATE_RATE The frequency of control updates.
30 Ordinarily this will be CONTROL_RATE, but an alternative (amongst others) is
31 to set this as well as the LERP_RATE parameter to AUDIO_RATE, and call both update() and next() in updateAudio().
32 Such a use would allow accurate envelopes with finer resolution of the control points than CONTROL_RATE.
33 @tparam LERP_RATE Sets how often next() will be called, to interpolate between updates set by CONTROL_UPDATE_RATE.
34 This will produce the smoothest results if it's set to AUDIO_RATE, but if you need to save processor time and your
35 envelope changes slowly or controls something like a filter where there may not be problems with glitchy or clicking transitions,
36 LERP_RATE could be set to CONTROL_RATE (for instance). Then update() and next() could both be called in updateControl(),
37 greatly reducing the amount of processing required compared to calling next() in updateAudio().
38 @todo Test whether using the template parameters makes any difference to speed,
39 and rationalise which units do and don't need them.
40 Template objects are messy when you try to use pointers to them,
41 you have to include the whole template shebang in the pointer handling.
42 */
43 
44 template <unsigned int NUM_PHASES, unsigned int CONTROL_UPDATE_RATE, unsigned int LERP_RATE>
45 class MultiLine
46 {
47 private:
48 
49  const unsigned int LERPS_PER_CONTROL;
50 
51  unsigned int update_step_counter;
52  unsigned int num_update_steps;
53 
54  //enum {LINE1,LINE2,LINE3,LINE4,IDLE};
55 
56 
57  struct phase{
58  byte phase_type;
59  unsigned int update_steps;
60  long lerp_steps; // signed, to match params to transition (line) type Q15n16, below
61  Q15n16 level;
62  };//target1,target2,target3,target4,idle;
63 
64  phase phases[NUM_PHASES];
65  unsigned int current_phase_num;
66 
67  phase * current_phase;
68 
69  Line <Q15n16> transition;
70 
71 
72  inline
73  unsigned int convertMsecToControlUpdateSteps(unsigned int msec){
74  return (uint16_t) (((uint32_t)msec*CONTROL_UPDATE_RATE)>>10); // approximate /1000 with shift
75  }
76 
77 
78  inline
79  void setPhase(phase * next_phase) {
80  update_step_counter = 0;
81  num_update_steps = next_phase->update_steps;
82  transition.set(next_phase->level, next_phase->lerp_steps);
83  current_phase = next_phase;
84  }
85 
86 
87 
88  inline
89  void checkForAndSetNextPhase() {
90  if (++update_step_counter >= num_update_steps){
91  if(current_phase_num < NUM_PHASES) current_phase_num++;
92  setPhase(&(phases[current_phase_num]));
93  }
94  }
95 
96 
97 
98  inline
99  void setTime(phase * p, unsigned int msec)
100  {
101  p->update_steps = convertMsecToControlUpdateSteps(msec);
102  p->lerp_steps = (long) p->update_steps * LERPS_PER_CONTROL;
103  }
104 
105 
106  inline
107  void setUpdateSteps(phase * p, unsigned int steps)
108  {
109  p->update_steps = steps;
110  p->lerp_steps = (long) steps * LERPS_PER_CONTROL;
111  }
112 
113 
114 
115 public:
116 
117  /** Constructor.
118  */
119  MultiLine():LERPS_PER_CONTROL(LERP_RATE/CONTROL_UPDATE_RATE)
120  {
121  //for(int i=0;i++;i<NUM_PHASES-1) phases[i].phase_type =
122  // target1.phase_type = LINE1;
123  // target2.phase_type = LINE2;
124  // target3.phase_type = LINE3;
125  // target4.phase_type = LINE4;
126  // idle.phase_type = IDLE;
127  // target4.level = 0;
128  current_phase_num = NUM_PHASES-1;
129  current_phase = &phases[current_phase_num];
130  }
131 
132 
133 
134  /** Updates the internal controls of the MultiLine.
135  Call this in updateControl().
136  */
137  void update(){ // control rate
138  checkForAndSetNextPhase();
139 }
140 
141 
142 
143  /** Advances one step along the MultiLine and returns the level.
144  Call this in updateAudio().
145  @return the next value, as a Q15n16 fixed-point number.
146  */
147  inline
149  {
150  return transition.next();
151  }
152 
153 
154 
155  /** Start the target1 phase of the MultiLine. THis will restart the MultiLine no matter what phase it is up to.
156  */
157  inline
158  void start(){
159  current_phase_num = 0;
160  setPhase(&(phases[current_phase_num]));
161  }
162 
163 
164 
165  /** Jump to to the final phase in the sequence
166  */
167  inline
168  void stop(){
169  setPhase(&(phases[NUM_PHASES-1]));
170  }
171 
172 
173 
174  /** Set the target level of one of the phases.
175  @param phase_num which phase to edit.
176  @param value the target level of the phase being edited.
177  */
178  inline
179  void setTargetLevel(uint8_t phase_num, Q15n16 value)
180  {
181  phases[phase_num].level=value;
182  }
183 
184 
185  /** Set the time taken to reach the target level of a phase, in milliseconds.
186  The actual time taken will be resolved within the resolution of CONTROL_RATE.
187  @param phase_num which phase to edit.
188  @param msec the target time in milliseconds.
189  @note Beware of low time values in case internal step size gets calculated as 0, which would mean nothing happens.
190  */
191  inline
192  void setTargetTime(uint8_t phase_num, unsigned int msec)
193  {
194  setTime(&(phases[phase_num]), msec);
195  }
196 
197 
198  /** Set the target1 time of the MultiLine, expressed as the number of update steps (not MultiLine::next() interpolation steps) in the target1 phase.
199  @param steps the number of times MultiLine::update() will be called in the target1 phase.
200  */
201  inline
202  void setTargetUpdateSteps(uint8_t phase_num, unsigned int steps)
203  {
204  setUpdateSteps(&(phases[phase_num]), steps);
205  }
206 
207 
208  /** Tells if the MultiLine is currently playing.
209  @return true if playing, false if in the final state
210  */
211  inline
212  bool playing()
213  {
214  return current_phase_num != NUM_PHASES-1;
215  }
216 
217 
218 };
219 
220 
221 /** @example 07.Envelopes/MultiLine_Envelope/MultiLine_Envelope.ino
222 This is an example of how to use the MultiLine class.
223 */
224 
225 #endif /* MultiLine_H_ */
void setTargetLevel(uint8_t phase_num, Q15n16 value)
Set the target level of one of the phases.
Definition: MultiLine2.h:179
int32_t Q15n16
signed fractional number using 15 integer bits and 16 fractional bits, represents -32767...
Definition: mozzi_fixmath.h:40
void stop()
Jump to to the final phase in the sequence.
Definition: MultiLine2.h:168
bool playing()
Tells if the MultiLine is currently playing.
Definition: MultiLine2.h:212
MultiLine()
Constructor.
Definition: MultiLine2.h:119
void setTargetTime(uint8_t phase_num, unsigned int msec)
Set the time taken to reach the target level of a phase, in milliseconds.
Definition: MultiLine2.h:192
void update()
Updates the internal controls of the MultiLine.
Definition: MultiLine2.h:137
void setTargetUpdateSteps(uint8_t phase_num, unsigned int steps)
Set the target1 time of the MultiLine, expressed as the number of update steps (not MultiLine::next()...
Definition: MultiLine2.h:202
For linear changes with a minimum of calculation at each step.
Definition: Line.h:37
Q15n16 next()
Advances one step along the MultiLine and returns the level.
Definition: MultiLine2.h:148
void start()
Start the target1 phase of the MultiLine.
Definition: MultiLine2.h:158