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