Mozzi  version v2.0
sound synthesis library for Arduino
Oscil.h
1 /*
2  * Oscil.h
3  *
4  * Oscil.h owes much to AF_precision_synthesis.pde, 2009, Adrian Freed.
5  *
6  * This file is part of Mozzi.
7  *
8  * Copyright 20009 Arian Freed
9  * Copyright 2012-2024 Tim Barrass and the Mozzi Team
10  *
11  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
12  *
13  */
14 
15 
16 #ifndef OSCIL_H_
17 #define OSCIL_H_
18 
19 #include "Arduino.h"
20 #include "MozziHeadersOnly.h"
21 #include "mozzi_fixmath.h"
22 #include "FixMath.h"
23 #include "mozzi_pgmspace.h"
24 
25 #ifdef OSCIL_DITHER_PHASE
26 #include "mozzi_rand.h"
27 #endif
28 
29 // fractional bits for oscillator index precision
30 #define OSCIL_F_BITS 16
31 #define OSCIL_F_BITS_AS_MULTIPLIER 65536
32 
33 // phmod_proportion is an 15n16 fixed-point number
34 #define OSCIL_PHMOD_BITS 16
35 
36 /**
37 Oscil plays a wavetable, cycling through the table to generate an audio or
38 control signal. The frequency of the signal can be set or changed with
39 setFreq(), and the output of an Oscil can be produced with next() for a simple
40 cycling oscillator, or atIndex() for a particular sample in the table.
41 @tparam NUM_TABLE_CELLS This is defined in the table ".h" file the Oscil will be
42 using. It's important that it's a power of 2, and either a literal number (eg. "8192") or a
43 defined macro, rather than a const or int, for the Oscil to run fast enough.
44 @tparam UPDATE_RATE This will be MOZZI_AUDIO_RATE if the Oscil is updated in
45 updateAudio(), or MOZZI_CONTROL_RATE if it's updated each time updateControl() is
46 called. It could also be a fraction of MOZZI_CONTROL_RATE if you are doing some kind
47 of cyclic updating in updateControl(), for example, to spread out the processor load.
48 @todo Use conditional compilation to optimise setFreq() variations for different table
49 sizes.
50 @note If you #define OSCIL_DITHER_PHASE before you #include <Oscil.h>,
51 the phase increments will be dithered, which reduces spurious frequency spurs
52 in the audio output, at the cost of some extra processing and memory.
53 @section int8_t2mozzi
54 Converting soundfiles for Mozzi
55 There is a python script called char2mozzi.py in the Mozzi/python folder.
56 The usage is:
57 char2mozzi.py infilename outfilename tablename samplerate
58 */
59 //template <unsigned int NUM_TABLE_CELLS, unsigned int UPDATE_RATE, bool DITHER_PHASE=false>
60 template <uint16_t NUM_TABLE_CELLS, uint16_t UPDATE_RATE>
61 class Oscil
62 {
63 
64 
65 public:
66  /** Constructor.
67  @param TABLE_NAME the name of the array the Oscil will be using. This
68  can be found in the table ".h" file if you are using a table made for
69  Mozzi by the int8_t2mozzi.py python script in Mozzi's python
70  folder.*/
71  Oscil(const int8_t * TABLE_NAME):table(TABLE_NAME)
72  {}
73 
74 
75  /** Constructor.
76  Declare an Oscil with template TABLE_NUM_CELLS and UPDATE_RATE
77  parameters, without specifying a particular wave table for it to play.
78  The table can be set or changed on the fly with setTable(). Any tables
79  used by the Oscil must be the same size.
80  */
82  {}
83 
84 
85  /** Updates the phase according to the current frequency and returns the sample at the new phase position.
86  @return the next sample.
87  */
88  inline
90  {
91  incrementPhase();
92  return readTable();
93  }
94 
95 
96  /** Change the sound table which will be played by the Oscil.
97  @param TABLE_NAME is the name of the array in the table ".h" file you're using.
98  */
99  void setTable(const int8_t * TABLE_NAME)
100  {
101  table = TABLE_NAME;
102  }
103 
104 
105  /** Set the phase of the Oscil. This does the same thing as Sample::start(offset). Just different ways of thinking about oscillators and samples.
106  @param phase a position in the wavetable.
107  */
108  // This could be called in the control interrupt, so phase_fractional should really be volatile,
109  // but that could limit optimisation. Since phase_fractional gets changed often in updateAudio()
110  // (in loop()), it's probably worth keeping it nonvolatile until it causes problems
111  void setPhase(unsigned int phase)
112  {
113  phase_fractional = (uint32_t)phase << OSCIL_F_BITS;
114  }
115 
116  /** Set the phase of the Oscil. Might be useful with getPhaseFractional().
117  @param phase a position in the wavetable.
118  */
119  // This could be called in the control interrupt, so phase_fractional should really be volatile,
120  // but that could limit optimisation. Since phase_fractional gets changed often in updateAudio()
121  // (in loop()), it's probably worth keeping it nonvolatile until it causes problems
122  void setPhaseFractional(uint32_t phase)
123  {
124  phase_fractional = phase;
125  }
126 
127 
128  /** Get the phase of the Oscil in fractional format.
129  @return position in the wavetable, shifted left by OSCIL_F_BITS (which is 16 when this was written).
130  */
132  {
133  return phase_fractional;
134  }
135 
136 
137 
138  /** Returns the next sample given a phase modulation value.
139  @param phmod_proportion a phase modulation value given as a proportion of the wave. The
140  phmod_proportion parameter is a Q15n16 fixed-point number where the fractional
141  n16 part represents almost -1 to almost 1, modulating the phase by one whole table length in
142  each direction.
143  @return a sample from the table.
144  */
145  // PM: cos((angle += incr) + change)
146  // FM: cos(angle += (incr + change))
147  // The ratio of deviation to modulation frequency is called the "index of modulation". ( I = d / Fm )
148  inline
150  {
151  incrementPhase();
152  return FLASH_OR_RAM_READ<const int8_t>(table + (((phase_fractional+(phmod_proportion * NUM_TABLE_CELLS))>>OSCIL_F_BITS) & (NUM_TABLE_CELLS - 1)));
153  }
154 
155 
156  /** Returns the next sample given a phase modulation value.
157  @param phmod_proportion a phase modulation value given as a proportion of the wave. The
158  phmod_proportion parameter is a SFix<NI,NF> fixed-point number where the fractional part represents almost -1 to almost 1, modulating the phase by one whole table length in
159  each direction. This fixed point math number is interpreted as a SFix<15,16> internally.
160  @return a sample from the table.
161  */
162  template <int8_t NI, int8_t NF, uint8_t RANGE>
163  inline
165  {
166  return phMod(SFix<15,16>(phmod_proportion).asRaw());
167  }
168 
169 
170 
171  /** Returns the next sample given a phase modulation value.
172  @param phmod_proportion a phase modulation value given as a proportion of the wave. The
173  phmod_proportion parameter is a SFix<15,16> fixed-point number where the fractional part represents almost -1 to almost 1, modulating the phase by one whole table length in
174  each direction.
175  @return a sample from the table.
176  */
177  inline
179  {
180  return phMod(phmod_proportion.asRaw());
181  }
182 
183 
184  /** Set the oscillator frequency with an unsigned int. This is faster than using a
185  float, so it's useful when processor time is tight, but it can be tricky with
186  low and high frequencies, depending on the size of the wavetable being used. If
187  you're not getting the results you expect, try explicitly using a float, or try
188  setFreq_Q24n8() or or setFreq_Q16n16().
189  @param frequency to play the wave table.
190  */
191  inline
192  void setFreq (int frequency) {
193  // TB2014-8-20 change this following Austin Grossman's suggestion on user list
194  // https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mozzi-users/u4D5NMzVnQs/pCmiWInFvrkJ
195  //phase_increment_fractional = ((((uint32_t)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)*frequency)/UPDATE_RATE) << (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS);
196  // to this:
197  phase_increment_fractional = ((uint32_t)frequency) * ((OSCIL_F_BITS_AS_MULTIPLIER*NUM_TABLE_CELLS)/UPDATE_RATE);
198  }
199 
200 
201  /** Set the oscillator frequency with a float. Using a float is the most reliable
202  way to set frequencies, -Might- be slower than using an int but you need either
203  this, setFreq_Q24n8() or setFreq_Q16n16() for fractional frequencies.
204  @param frequency to play the wave table.
205  */
206  inline
207  void setFreq(float frequency)
208  { // 1 us - using float doesn't seem to incur measurable overhead with the oscilloscope
209  phase_increment_fractional = (uint32_t)((((float)NUM_TABLE_CELLS * frequency)/UPDATE_RATE) * OSCIL_F_BITS_AS_MULTIPLIER);
210  }
211 
212 
213  /** Set the frequency using UFix<NI,NF> fixed-point number format. This falls back to using UFix<16,16> internally and is provided as a fallout for other UFix types. If possible try to use directly UFix<16,16> or UFix<24,8> for well defined (and well tested) behaviors.
214  @note This should work OK with tables 2048 cells or smaller and
215  frequencies up to 4096 Hz. Can't be used with UPDATE_RATE less than 64 Hz.
216  @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance.
217  @param frequency in UFix<NI,NF> fixed-point number format.
218  */
219  template <int8_t NI, int8_t NF, uint64_t RANGE>
220  inline
221  void setFreq(UFix<NI,NF,RANGE> frequency)
222  {
223  setFreq_Q16n16(UFix<16,16>(frequency).asRaw());
224  }
225 
226 
227 
228  /** Set the frequency using Q24n8 fixed-point number format.
229  This might be faster than the float version for setting low frequencies such as
230  1.5 Hz, or other values which may not work well with your table size. A Q24n8
231  representation of 1.5 is 384 (ie. 1.5 * 256). Can't be used with UPDATE_RATE
232  less than 64 Hz.
233  @param frequency in Q24n8 fixed-point number format.
234  */
235  inline
236  void setFreq_Q24n8(Q24n8 frequency)
237  {
238  //phase_increment_fractional = (frequency* (NUM_TABLE_CELLS>>3)/(UPDATE_RATE>>6)) << (F_BITS-(8-3+6));
239 // TB2016-10-2 line below might have been left in accidentally while making the 2014 change below, remove for now
240 // phase_increment_fractional = (((((uint32_t)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)>>3)*frequency)/(UPDATE_RATE>>6))
241 // << (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - (8-3+6));
242 
243  // TB2014-8-20 change this following Austin Grossman's suggestion on user list
244  // https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mozzi-users/u4D5NMzVnQs/pCmiWInFvrkJ
245  if ((256UL*NUM_TABLE_CELLS) >= UPDATE_RATE) {
246  phase_increment_fractional = ((uint32_t)frequency) * ((256UL*NUM_TABLE_CELLS)/UPDATE_RATE);
247  } else {
248  phase_increment_fractional = ((uint32_t)frequency) / (UPDATE_RATE/(256UL*NUM_TABLE_CELLS));
249  }
250  }
251 
252  /** Set the frequency using UFix<24,8> fixed-point number format.
253  This might be faster than the float version for setting low frequencies such as
254  1.5 Hz, or other values which may not work well with your table size. A UFix<24,8>
255  representation of 1.5 is 384 (ie. 1.5 * 256). Can't be used with UPDATE_RATE
256  less than 64 Hz.
257  @param frequency in UFix<24,8> fixed-point number format.
258  */
259  template <uint64_t RANGE>
260  inline
261  void setFreq(UFix<24,8,RANGE> frequency)
262  {
263  setFreq_Q24n8(frequency.asRaw());
264  }
265 
266 
267  /** Set the frequency using Q16n16 fixed-point number format. This is useful in
268  combination with Q16n16_mtof(), a fast alternative to mtof(), using Q16n16
269  fixed-point format instead of floats.
270  @note This should work OK with tables 2048 cells or smaller and
271  frequencies up to 4096 Hz. Can't be used with UPDATE_RATE less than 64 Hz.
272  @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance.
273  @param frequency in Q16n16 fixed-point number format.
274  */
275  inline
276  void setFreq_Q16n16(Q16n16 frequency)
277  {
278  //phase_increment_fractional = ((frequency * (NUM_TABLE_CELLS>>7))/(UPDATE_RATE>>6)) << (F_BITS-16+1);
279  // TB2014-8-20 change this following Austin Grossman's suggestion on user list
280  // https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mozzi-users/u4D5NMzVnQs/pCmiWInFvrkJ
281  //phase_increment_fractional = (((((uint32_t)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)>>7)*frequency)/(UPDATE_RATE>>6))
282  // << (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - 16 + 1);
283  if (NUM_TABLE_CELLS >= UPDATE_RATE) {
284  phase_increment_fractional = ((uint32_t)frequency) * (NUM_TABLE_CELLS/UPDATE_RATE);
285  } else {
286  phase_increment_fractional = ((uint32_t)frequency) / (UPDATE_RATE/NUM_TABLE_CELLS);
287  }
288  }
289 
290 
291  /** Set the frequency using UFix<16,16> fixed-point number format. This is useful in
292  combination with Q16n16_mtof(), a fast alternative to mtof(), using UFix<16,16>
293  fixed-point format instead of fractional numbers.
294  @note This should work OK with tables 2048 cells or smaller and
295  frequencies up to 4096 Hz. Can't be used with UPDATE_RATE less than 64 Hz.
296  @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance.
297  @param frequency in UFix<16,16> fixed-point number format.
298  */
299  template <uint64_t RANGE>
300  inline
301  void setFreq(UFix<16,16,RANGE> frequency)
302  {
303  setFreq_Q16n16(frequency.asRaw());
304  }
305 
306 
307 
308  /** Set the frequency using SFix<NI,NF> fixed-point number format. This falls back to using UFix<16,16> internally and is provided as a fallout for other UFix types. If possible try to use directly UFix<16,16> or UFix<24,8> for well defined (and well tested) behaviors.
309  @note This should work OK with tables 2048 cells or smaller and
310  frequencies up to 4096 Hz. Can't be used with UPDATE_RATE less than 64 Hz.
311  @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance.
312  @param frequency in SFix<16,16> fixed-point number format.
313  */
314  template <int8_t NI, int8_t NF, uint64_t RANGE>
315  inline
316  void setFreq(SFix<NI,NF,RANGE> frequency)
317  {
318  setFreq_Q16n16(UFix<16,16>(frequency).asRaw());
319  }
320 
321 /*
322  inline
323  void setFreqMidi(int8_t note_num) {
324  setFreq_Q16n16(mtof(note_num));
325  }
326 */
327  /** Returns the sample at the given table index.
328  @param index between 0 and the table size.The
329  index rolls back around to 0 if it's larger than the table size.
330  @return the sample at the given table index.
331  */
332  inline
333  int8_t atIndex(unsigned int index)
334  {
335  return FLASH_OR_RAM_READ<const int8_t>(table + (index & (NUM_TABLE_CELLS - 1)));
336  }
337 
338 
339  /** phaseIncFromFreq() and setPhaseInc() are for saving processor time when sliding between frequencies.
340  Instead of recalculating the phase increment for each
341  frequency in between, you can just calculate the phase increment for each end
342  frequency with phaseIncFromFreq(), then use a Line to interpolate on the fly and
343  use setPhaseInc() to set the phase increment at each step. (Note: I should
344  really profile this with the oscilloscope to see if it's worth the extra
345  confusion!)
346  @param frequency for which you want to calculate a phase increment value.
347  @return the phase increment value which will produce a given frequency.
348  */
349  inline
351  {
352  // TB2014-8-20 change this following Austin Grossman's suggestion on user list
353  // https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mozzi-users/u4D5NMzVnQs/pCmiWInFvrkJ
354  //return (((uint32_t)frequency * NUM_TABLE_CELLS)/UPDATE_RATE) << OSCIL_F_BITS;
355  return ((uint32_t)frequency) * ((OSCIL_F_BITS_AS_MULTIPLIER*NUM_TABLE_CELLS)/UPDATE_RATE);
356  }
357 
358 
359  /** Set a specific phase increment. See phaseIncFromFreq().
360  @param phaseinc_fractional a phase increment value as calculated by phaseIncFromFreq().
361  */
362  inline
363  void setPhaseInc(uint32_t phaseinc_fractional)
364  {
365  phase_increment_fractional = phaseinc_fractional;
366  }
367 
368 
369 
370 private:
371 
372 
373  /** Used for shift arithmetic in setFreq() and its variations.
374  */
375 static const uint8_t ADJUST_FOR_NUM_TABLE_CELLS = (NUM_TABLE_CELLS<2048) ? 8 : 0;
376 
377 
378  /** Increments the phase of the oscillator without returning a sample.
379  */
380  inline
381  void incrementPhase()
382  {
383  //phase_fractional += (phase_increment_fractional | 1); // odd phase incr, attempt to reduce frequency spurs in output
384  phase_fractional += phase_increment_fractional;
385  }
386 
387 
388  /** Returns the current sample.
389  */
390  inline
391  int8_t readTable()
392  {
393 #ifdef OSCIL_DITHER_PHASE
394  return FLASH_OR_RAM_READ<const int8_t>(table + (((phase_fractional + ((int)(xorshift96()>>16))) >> OSCIL_F_BITS) & (NUM_TABLE_CELLS - 1)));
395 #else
396  return FLASH_OR_RAM_READ<const int8_t>(table + ((phase_fractional >> OSCIL_F_BITS) & (NUM_TABLE_CELLS - 1)));
397  //return FLASH_OR_RAM_READ<int8_t>(table + (((phase_fractional >> OSCIL_F_BITS) | 1 ) & (NUM_TABLE_CELLS - 1))); odd phase, attempt to reduce frequency spurs in output
398 #endif
399  }
400 
401 
402  uint32_t phase_fractional;
403  uint32_t phase_increment_fractional;
404  const int8_t * table;
405 
406 };
407 
408 
409 /**
410 @example 01.Basics/Vibrato/Vibrato.ino
411 This is an example using Oscil::phMod to produce vibrato using phase modulation.
412 */
413 
414 #endif /* OSCIL_H_ */