Mozzi  version v2.0
sound synthesis library for Arduino
MetaOscil.h
1 /*
2  * MetaOscil.h
3  *
4  * A wrap-up to swap between different oscillators seemlessly, allowing to produce non-aliased sounds by automatically switching between oscillators.
5  *
6  * This file is part of Mozzi.
7  *
8  * Copyright 2021-2024 T. Combriat and the Mozzi Team
9  *
10  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
11  *
12  */
13 
14 
15 #ifndef META_OSCIL_H
16 #define META_OSCIL_H
17 
18 
19 #include <Arduino.h>
20 
21 #include "Oscil.h"
22 #include "mozzi_fixmath.h"
23 
24 
25 /**
26  MetaOscil is a wrapper for several Oscil. Once constructed it will behave exactly as an Oscil except that it will automatically switch between Oscil depending on the asked frequency. This allows to produce non-aliased sounds by switching between tables with less and less harmonics as the frequency increases.
27 */
28 
29 
30 template<uint16_t NUM_TABLE_CELLS, uint16_t UPDATE_RATE, byte N_OSCIL>
31  class MetaOscil
32 
33 {
34  public:
35  /** Constructor
36  Declare a MetaOscil containing any number of Oscil pointers. Every Oscil should have the same TABLE_NUM_CELLS and UPDATE_RATE which are also passed in the MetaOscil constructor.
37  @param N_OSCIL is the number of Oscil contained in the MetaOscil. This cannot be changed after construction. */
38  template<class... T> MetaOscil(Oscil<NUM_TABLE_CELLS, UPDATE_RATE>* first, T*... elements):oscillators{first, elements...} {
39  current_osc=oscillators[0];};
40 
41  MetaOscil(){};
42 
43  /* Add one oscil to the MetaOscil.
44  @param osc is a pointer toward an Oscil
45  @param cutoff_freq is the cutoff frequency of this Oscil
46  void addOscil(Oscil<NUM_TABLE_CELLS, UPDATE_RATE>* osc, int cutoff_freq)
47  {
48  oscillators[current_rank] = osc;
49  cutoff_freqs[current_rank] = cutoff_freq;
50  if (current_rank == 0) current_osc=oscillators[0];
51  current_rank += 1;
52  }*/
53 
54 
55  /** Set all Oscil of a MetaOscil.
56  @param first... is a list of pointers towards several Oscil */
57  template<typename ... T > void setOscils(Oscil<NUM_TABLE_CELLS, UPDATE_RATE>* first,T... elements)
58  {
59  oscillators[current_rank]=first;
60  if (current_rank == 0) current_osc=oscillators[0];
61  current_rank+=1;
62  setOscils(elements...);
63  current_rank = 0;
64  }
65 
66  void setOscils(){};
67 
68 
69  /** Set all the cutoff frequencies for changing between Oscil. They have to be sorted in increasing values and contain at least N_OSCIL-1 values. Note that the last Oscil will be used by default for frequencies higher than the higher cutoff, hence the last value can be discarded.
70  @param first, elements... a set of int cutoff frequencies.*/
71  template<typename ... T > void setCutoffFreqs(int first,T... elements)
72  {
73  cutoff_freqs[current_rank]=first;
74  current_rank+=1;
75  setCutoffFreqs(elements...);
76  current_rank = 0;
77  }
78 
79  void setCutoffFreqs() {};
80 
81  /** Set or change the cutoff frequency of one Oscil.
82  @param rank is the rank of the Oscil.
83  @param freq is the cutoff frequency. */
84  void setCutoffFreq(int freq, byte rank)
85  {
86  cutoff_freqs[rank] = freq;
87  }
88 
89  /** Updates the phase according to the current frequency and returns the sample at the new phase position.
90  @return the next sample.
91  */
92  inline
93  int8_t next() {return current_osc->next();}
94 
95  /** Change the sound table which will be played by the Oscil of rank.
96  @param TABLE_NAME is the name of the array in the table ".h" file you're using.
97  @param rank is the Oscil.*/
98  void setTable(const int8_t * TABLE_NAME, byte rank) {oscillators[rank]->setTable(TABLE_NAME);}
99 
100 
101  /** Set the phase of the currently playing Oscil.
102  @param phase a position in the wavetable.*/
103  void setPhase(unsigned int phase) {current_osc->setPhase(phase);}
104 
105 
106  /** Set the phase of the currently playing Oscil in fractional format.
107  @param phase a position in the wavetable.*/
108  void setPhaseFractional(unsigned long phase) {current_osc->setPhaseFractional(phase);}
109 
110 
111  /** Get the phase of the currently playin Oscil in fractional format.
112  @return position in the wavetable, shifted left by OSCIL_F_BITS (which is 16 when this was written).
113  */
114  unsigned long getPhaseFractional() {return current_osc->getPhaseFractional();}
115 
116 
117 
118  /** Returns the next sample given a phase modulation value.
119  @param phmod_proportion a phase modulation value given as a proportion of the wave. The
120  phmod_proportion parameter is a Q15n16 fixed-point number where the fractional
121  n16 part represents almost -1 to almost 1, modulating the phase by one whole table length in
122  each direction.
123  @return a sample from the table.*/
124  inline
125  int8_t phMod(Q15n16 phmod_proportion) {return current_osc->phMod(phmod_proportion);}
126 
127 
128  /** Set the MetaOsc frequency with an unsigned int.
129  @param frequency to play the wave table.*/
130  inline
131  void setFreq(int frequency, bool apply = true)
132  {
133  if (frequency < cutoff_freqs[0]) //getting out the extreme cases
134  {
135  oscillators[0]->setPhaseFractional(current_osc->getPhaseFractional());
136  current_osc = oscillators[0];
137  current_osc->setFreq(frequency);
138  }
139 
140  else if (frequency > cutoff_freqs[N_OSCIL-1])
141  {
142  oscillators[N_OSCIL-1]->setPhaseFractional(current_osc->getPhaseFractional());
143  current_osc = oscillators[N_OSCIL-1];
144  current_osc->setFreq(frequency);
145  }
146  else // dichotomic search
147  {
148  byte low_point = 0, high_point = N_OSCIL-1, mid_point = (N_OSCIL-1)>>1;
149  while(low_point != high_point)
150  {
151  if (frequency > cutoff_freqs[mid_point]) low_point = mid_point+1;
152  else if (frequency < cutoff_freqs[mid_point]) high_point = mid_point;
153  else
154  {
155  break;
156  }
157  mid_point = (low_point + high_point)>>1;
158  }
159  oscillators[mid_point]->setPhaseFractional(current_osc->getPhaseFractional());
160  current_osc = oscillators[mid_point];
161  if (apply) current_osc->setFreq(frequency);
162  }
163 
164  }
165 
166 
167  /** Set the MetaOsc frequency with a float.
168  @param frequency to play the wave table.*/
169  inline
170  void setFreq(float frequency)
171  {
172  setFreq((int) frequency, false);
173  current_osc->setFreq(frequency);
174  }
175 
176 
177  /** Set the MetaOsc frequency with a Q24n8 fixed-point number format.
178  @param frequency to play the wave table.*/
179  inline
180  void setFreq_Q24n8(Q24n8 frequency)
181  {
182  setFreq((int) (frequency>>8), false);
183  current_osc->setFreq_Q24n8(frequency);
184  }
185 
186 
187  /** Set the MetaOsc frequency with a Q16n16 fixed-point number format.
188  @param frequency to play the wave table.*/
189  inline
190  void setFreq_Q16n16(Q16n16 frequency)
191  {
192  setFreq((int) (frequency>>16), false);
193  current_osc->setFreq_Q16n16(frequency);
194  }
195 
196 
197  /** Returns the sample at the given table index of the current Oscil.
198  @param index between 0 and the table size.The
199  index rolls back around to 0 if it's larger than the table size.
200  @return the sample at the given table index.
201  */
202  inline
203  int8_t atIndex(unsigned int index) {return current_osc->atIndex(index);}
204 
205 
206  /** phaseIncFromFreq() and setPhaseInc() are for saving processor time when sliding between frequencies.
207  @param frequency for which you want to calculate a phase increment value.
208  @return the phase increment value which will produce a given frequency.*/
209  inline
210  unsigned long phaseIncFromFreq(int frequency) {return current_osc->phaseIncFromFreq(frequency);}
211 
212  /** Set a specific phase increment.
213  @param phaseinc_fractional a phase increment value as calculated by phaseIncFromFreq().
214  */
215  inline
216  void setPhaseInc(unsigned long phaseinc_fractional) {current_osc->setPhaseInc(phaseinc_fractional);}
217 
218 
219 
220  private:
221  Oscil<NUM_TABLE_CELLS, UPDATE_RATE> * oscillators[N_OSCIL];
222  Oscil<NUM_TABLE_CELLS, UPDATE_RATE> * current_osc = NULL;
223  int cutoff_freqs[N_OSCIL];
224  byte current_rank = 0;
225 
226 };
227 
228 /**
229 @example 06.Synthesis/NonAlias_MetaOscil/NonAlias_MetaOscil.ino
230 This example demonstrates the Meta_Oscil class.
231 */
232 
233 #endif /* META_OSCIL_H */