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 
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:
71  Oscil(const int8_t * TABLE_NAME):table(TABLE_NAME)
72  {}
73 
74 
82  {}
83 
84 
88  inline
89  int8_t next()
90  {
91  incrementPhase();
92  return readTable();
93  }
94 
95 
99  void setTable(const int8_t * TABLE_NAME)
100  {
101  table = TABLE_NAME;
102  }
103 
104 
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 
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 
132  {
133  return phase_fractional;
134  }
135 
136 
137 
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
149  int8_t phMod(Q15n16 phmod_proportion)
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 
162  template <int8_t NI, int8_t NF, uint8_t RANGE>
163  inline
164  int8_t phMod(SFix<NI,NF,RANGE> phmod_proportion)
165  {
166  return phMod(SFix<15,16>(phmod_proportion).asRaw());
167  }
168 
169 
170 
177  inline
178  int8_t phMod(SFix<15,16> phmod_proportion)
179  {
180  return phMod(phmod_proportion.asRaw());
181  }
182 
183 
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 
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 
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 
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 
259  template <uint64_t RANGE>
260  inline
261  void setFreq(UFix<24,8,RANGE> frequency)
262  {
263  setFreq_Q24n8(frequency.asRaw());
264  }
265 
266 
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 
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 
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 */
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 
349  inline
350  uint32_t phaseIncFromFreq(int frequency)
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 
362  inline
363  void setPhaseInc(uint32_t phaseinc_fractional)
364  {
365  phase_increment_fractional = phaseinc_fractional;
366  }
367 
368 
369 
370 private:
371 
372 
375 static const uint8_t ADJUST_FOR_NUM_TABLE_CELLS = (NUM_TABLE_CELLS<2048) ? 8 : 0;
376 
377 
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 
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 
414 #endif /* OSCIL_H_ */
This file provides declarations of the Mozzi Core Functions Mozzi functions, but no implementation.
Oscil plays a wavetable, cycling through the table to generate an audio or control signal.
Definition: Oscil.h:62
void setFreq(UFix< NI, NF, RANGE > frequency)
Set the frequency using UFix<NI,NF> fixed-point number format.
Definition: Oscil.h:221
void setTable(const int8_t *TABLE_NAME)
Change the sound table which will be played by the Oscil.
Definition: Oscil.h:99
void setFreq(int frequency)
Set the oscillator frequency with an unsigned int.
Definition: Oscil.h:192
void setFreq(SFix< NI, NF, RANGE > frequency)
Set the frequency using SFix<NI,NF> fixed-point number format.
Definition: Oscil.h:316
uint32_t phaseIncFromFreq(int frequency)
phaseIncFromFreq() and setPhaseInc() are for saving processor time when sliding between frequencies.
Definition: Oscil.h:350
int8_t phMod(Q15n16 phmod_proportion)
Returns the next sample given a phase modulation value.
Definition: Oscil.h:149
void setFreq(UFix< 16, 16, RANGE > frequency)
Set the frequency using UFix<16,16> fixed-point number format.
Definition: Oscil.h:301
int8_t next()
Updates the phase according to the current frequency and returns the sample at the new phase position...
Definition: Oscil.h:89
void setFreq_Q16n16(Q16n16 frequency)
Set the frequency using Q16n16 fixed-point number format.
Definition: Oscil.h:276
void setFreq(UFix< 24, 8, RANGE > frequency)
Set the frequency using UFix<24,8> fixed-point number format.
Definition: Oscil.h:261
int8_t phMod(SFix< NI, NF, RANGE > phmod_proportion)
Returns the next sample given a phase modulation value.
Definition: Oscil.h:164
int8_t atIndex(unsigned int index)
Returns the sample at the given table index.
Definition: Oscil.h:333
void setPhaseFractional(uint32_t phase)
Set the phase of the Oscil.
Definition: Oscil.h:122
void setFreq(float frequency)
Set the oscillator frequency with a float.
Definition: Oscil.h:207
void setPhase(unsigned int phase)
Set the phase of the Oscil.
Definition: Oscil.h:111
Oscil()
Constructor.
Definition: Oscil.h:81
void setFreq_Q24n8(Q24n8 frequency)
Set the frequency using Q24n8 fixed-point number format.
Definition: Oscil.h:236
void setPhaseInc(uint32_t phaseinc_fractional)
Set a specific phase increment.
Definition: Oscil.h:363
int8_t phMod(SFix< 15, 16 > phmod_proportion)
Returns the next sample given a phase modulation value.
Definition: Oscil.h:178
uint32_t getPhaseFractional()
Get the phase of the Oscil in fractional format.
Definition: Oscil.h:131
Oscil(const int8_t *TABLE_NAME)
Constructor.
Definition: Oscil.h:71
int32_t Q15n16
signed fractional number using 15 integer bits and 16 fractional bits, represents -32767....
Definition: mozzi_fixmath.h:46
uint32_t Q24n8
unsigned fractional number using 24 integer bits and 8 fractional bits, represents 0 to 16777215
Definition: mozzi_fixmath.h:51
uint32_t Q16n16
unsigned fractional number using 16 integer bits and 16 fractional bits, represents 0 to 65535....
Definition: mozzi_fixmath.h:52
uint32_t xorshift96()
Random number generator.
Definition: mozzi_rand.h:30