| Mozzi version v1.1.0 sound synthesis library for Arduino |

Sample.h
1 /*
2 * Sample.h
3 *
4 * Copyright 2012 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 SAMPLE_H_
13 #define SAMPLE_H_
14
15 #include "MozziGuts.h"
16 #include "mozzi_fixmath.h"
17 #include "mozzi_pgmspace.h"
18
19 // fractional bits for sample index precision
20 #define SAMPLE_F_BITS 16
21 #define SAMPLE_F_BITS_AS_MULTIPLIER 65536
22
23 // phmod_proportion is an 1n15 fixed-point number only using
24 // the fractional part and the sign bit
25 #define SAMPLE_PHMOD_BITS 16
26
27 enum interpolation {INTERP_NONE, INTERP_LINEAR};
28
29 /** Sample is like Oscil, it plays a wavetable. However, Sample can be
30 set to play once through only, with variable start and end points,
31 or can loop, also with variable start and end points.
32 It defaults to playing once through the whole sound table, from start to finish.
33 @tparam NUM_TABLE_CELLS This is defined in the table ".h" file the Sample will be
34 using. The sound table can be arbitrary length for Sample.
35 It's important that NUM_TABLE_CELLS is either a literal number (eg. "8192") or a
36 defined macro, rather than a const or int, for the Sample to run fast enough.
37 @tparam UPDATE_RATE This will be AUDIO_RATE if the Sample is updated in
38 updateAudio(), or CONTROL_RATE if it's updated each time updateControl() is
39 called. It could also be a fraction of CONTROL_RATE if you are doing some kind
40 of cyclic updating in updateControl(), for example, to spread out the processor load.
41 @section int8_t2mozzi
42 Converting soundfiles for Mozzi.
43 There is a python script called int8_t2mozzi.py in the Mozzi/python folder.
44 The script converts raw sound data saved from a program like Audacity.
45 Instructions are in the int8_t2mozzi.py file.
46 */
47 template <unsigned int NUM_TABLE_CELLS, unsigned int UPDATE_RATE, uint8_t INTERP=INTERP_NONE>
48 class Sample
49 {
50
51 public:
52
53 /** Constructor.
54 @param TABLE_NAME the name of the array the Sample will be using. This
55 can be found in the table ".h" file if you are using a table made for
56 Mozzi by the int8_t2mozzi.py python script in Mozzi's python
57 folder. Sound tables can be of arbitrary lengths for Sample().
58 */
59 Sample(const int8_t * TABLE_NAME):table(TABLE_NAME),endpos_fractional((unsigned long) NUM_TABLE_CELLS << SAMPLE_F_BITS) // so isPlaying() will work
60 {
61 setLoopingOff();
62 //rangeWholeSample();
63 }
64
65
66
67 /** Constructor.
68 Declare a Sample with template TABLE_NUM_CELLS and UPDATE_RATE parameters, without specifying a particular wave table for it to play.
69 The table can be set or changed on the fly with setTable().
70 */
71 Sample():endpos_fractional(4294967295UL) // biggest UL number so isPlaying() will work
72 {
73 setLoopingOff();
74 //rangeWholeSample();
75 }
76
77
78 /** Change the sound table which will be played by the Sample.
79 @param TABLE_NAME is the name of the array in the table ".h" file you're using.
80 */
81 inline
82 void setTable(const int8_t * TABLE_NAME)
83 {
84 table = TABLE_NAME;
85 }
86
87
88 /** Sets the starting position in samples.
89 @param startpos offset position in samples.
90 */
91 inline
92 void setStart(unsigned int startpos)
93 {
94 startpos_fractional = (unsigned long) startpos << SAMPLE_F_BITS;
95 }
96
97
98 /** Resets the phase (the playhead) to the start position, which will be 0 unless set to another value with setStart();
99 */
100 inline
101 void start()
102 {
103 phase_fractional = startpos_fractional;
104 }
105
106
107 /** Sets a new start position plays the sample from that position.
108 @param startpos position in samples from the beginning of the sound.
109 */
110 inline
111 void start(unsigned int startpos)
112 {
113 setStart(startpos);
114 start();
115 }
116
117
118 /** Sets the end position in samples from the beginning of the sound.
119 @param end position in samples.
120 */
121 inline
122 void setEnd(unsigned int end)
123 {
124 endpos_fractional = (unsigned long) end << SAMPLE_F_BITS;
125 }
126
127
128 /** Sets the start and end points to include the range of the whole sound table.
129 */
130 inline
131 void rangeWholeSample()
132 {
133 startpos_fractional = 0;
134 endpos_fractional = (unsigned long) NUM_TABLE_CELLS << SAMPLE_F_BITS;
135 }
136
137
138 /** Turns looping on.
139 */
140 inline
141 void setLoopingOn()
142 {
143 looping=true;
144 }
145
146
147 /** Turns looping off.
148 */
149 inline
150 void setLoopingOff()
151 {
152 looping=false;
153 }
154
155
156
157 /**
158 Returns the sample at the current phase position, or 0 if looping is off
159 and the phase overshoots the end of the sample. Updates the phase
160 according to the current frequency.
161 @return the next sample value from the table, or 0 if it's finished playing.
162 @todo in next(), incrementPhase() happens in a different position than for Oscil - check if it can be standardised
163 */
164 inline
165 int8_t next() { // 4us
166
167 if (phase_fractional>endpos_fractional){
168 if (looping) {
169 phase_fractional = startpos_fractional + (phase_fractional - endpos_fractional);
170 }else{
171 return 0;
172 }
173 }
174 int8_t out;
175 if(INTERP==INTERP_LINEAR){
176 // WARNNG this is hard coded for when SAMPLE_F_BITS is 16
177 unsigned int index = phase_fractional >> SAMPLE_F_BITS;
178 out = FLASH_OR_RAM_READ<const int8_t>(table + index);
179 int8_t difference = FLASH_OR_RAM_READ<const int8_t>((table + 1) + index) - out;
180 int8_t diff_fraction = (int8_t)(((((unsigned int) phase_fractional)>>8)*difference)>>8); // (unsigned int) phase_fractional keeps low word, then>> for only 8 bit precision
181 out += diff_fraction;
182 }else{
183 out = FLASH_OR_RAM_READ<const int8_t>(table + (phase_fractional >> SAMPLE_F_BITS));
184 }
185 incrementPhase();
186 return out;
187 }
188
189
190 /** Checks if the sample is playing by seeing if the phase is within the limits of its end position.
191 @return true if the sample is playing
192 */
193 inline
194 boolean isPlaying(){
195 return phase_fractional<endpos_fractional;
196 }
197
198
199 // Not readjusted for arbitrary table length yet
200 //
201 // Returns the next sample given a phase modulation value.
202 // @param phmod_proportion phase modulation value given as a proportion of the wave. The
203 // phmod_proportion parameter is a Q15n16 fixed-point number where to fractional
204 // n16 part represents -1 to 1, modulating the phase by one whole table length in
205 // each direction.
206 // @return a sample from the table.
207 //
208 // inline
209 // int8_t phMod(long phmod_proportion)
210 // {
211 // incrementPhase();
212 // return FLASH_OR_RAM_READ<const int8_t>(table + (((phase_fractional+(phmod_proportion * NUM_TABLE_CELLS))>>SAMPLE_SAMPLE_F_BITS) & (NUM_TABLE_CELLS - 1)));
213 // }
214
215
216
217 /** Set the oscillator frequency with an unsigned int.
218 This is faster than using a float, so it's useful when processor time is tight,
219 but it can be tricky with low and high frequencies, depending on the size of the
220 wavetable being used. If you're not getting the results you expect, try
221 explicitly using a float, or try setFreq_Q24n8.
222 @param frequency to play the wave table.
223 */
224 inline
225 void setFreq (int frequency) {
226 phase_increment_fractional = ((((unsigned long)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)*frequency)/UPDATE_RATE) << (SAMPLE_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS);
227 }
228
229
230 /** Set the sample frequency with a float. Using a float is the most reliable
231 way to set frequencies, -Might- be slower than using an int but you need either
232 this or setFreq_Q24n8 for fractional frequencies.
233 @param frequency to play the wave table.
234 */
235 inline
236 void setFreq(float frequency)
237 { // 1 us - using float doesn't seem to incur measurable overhead with the oscilloscope
238 phase_increment_fractional = (unsigned long)((((float)NUM_TABLE_CELLS * frequency)/UPDATE_RATE) * SAMPLE_F_BITS_AS_MULTIPLIER);
239 }
240
241
242 /** Set the frequency using Q24n8 fixed-point number format.
243 This might be faster than the float version for setting low frequencies
244 such as 1.5 Hz, or other values which may not work well with your table
245 size. Note: use with caution because it's prone to overflow with higher
246 frequencies and larger table sizes. An Q24n8 representation of 1.5 is 384
247 (ie. 1.5 * 256).
248 @param frequency in Q24n8 fixed-point number format.
249 */
250 inline
251 void setFreq_Q24n8(Q24n8 frequency)
252 {
253 //phase_increment_fractional = (frequency* (NUM_TABLE_CELLS>>3)/(UPDATE_RATE>>6)) << (F_BITS-(8-3+6));
254 phase_increment_fractional = (((((unsigned long)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)>>3)*frequency)/(UPDATE_RATE>>6))
255 << (SAMPLE_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - (8-3+6));
256 }
257
258
259 /** Returns the sample at the given table index.
260 @param index between 0 and the table size.
261 @return the sample at the given table index.
262 */
263 inline
264 int8_t atIndex(unsigned int index)
265 {
266 return FLASH_OR_RAM_READ<const int8_t>(table + index);
267 }
268
269
270 /** phaseIncFromFreq() and setPhaseInc() are for saving processor time when sliding between frequencies.
271 Instead of recalculating the phase increment for each
272 frequency in between, you can just calculate the phase increment for each end
273 frequency with phaseIncFromFreq(), then use a Line to interpolate on the fly and
274 use setPhaseInc() to set the phase increment at each step. (Note: I should
275 really profile this with the oscilloscope to see if it's worth the extra
276 confusion!)
277 @param frequency for which you want to calculate a phase increment value.
278 @return the phase increment value which will produce a given frequency.
279 */
280 inline
281 unsigned long phaseIncFromFreq(unsigned int frequency)
282 {
283 return (((unsigned long)frequency * NUM_TABLE_CELLS)/UPDATE_RATE) << SAMPLE_F_BITS;
284 }
285
286
287 /** Set a specific phase increment. See phaseIncFromFreq().
288 @param phaseinc_fractional a phase increment value as calculated by phaseIncFromFreq().
289 */
290 inline
291 void setPhaseInc(unsigned long phaseinc_fractional)
292 {
293 phase_increment_fractional = phaseinc_fractional;
294 }
295
296
297 private:
298
299
300 /** Used for shift arithmetic in setFreq() and its variations.
301 */
302 static const uint8_t ADJUST_FOR_NUM_TABLE_CELLS = (NUM_TABLE_CELLS<2048) ? 8 : 0;
303
304
305 /** Increments the phase of the oscillator without returning a sample.
306 */
307 inline
308 void incrementPhase()
309 {
310 phase_fractional += phase_increment_fractional;
311 }
312
313
314 volatile unsigned long phase_fractional;
315 volatile unsigned long phase_increment_fractional;
316 const int8_t * table;
317 bool looping;
318 unsigned long startpos_fractional, endpos_fractional;
319 };
320
321
322 /**
323 @example 08.Samples/Sample/Sample.ino
324 This example demonstrates the Sample class.
325 */
326
327 #endif /* SAMPLE_H_ */
Sample::setFreq_Q24n8
void setFreq_Q24n8(Q24n8 frequency)
Set the frequency using Q24n8 fixed-point number format.
Definition: Sample.h:251
Sample::setLoopingOn
void setLoopingOn()
Turns looping on.
Definition: Sample.h:141
Sample::setFreq
void setFreq(int frequency)
Set the oscillator frequency with an unsigned int.
Definition: Sample.h:225
Sample::setEnd
void setEnd(unsigned int end)
Sets the end position in samples from the beginning of the sound.
Definition: Sample.h:122
Sample::Sample
Sample(const int8_t *TABLE_NAME)
Constructor.
Definition: Sample.h:59
Sample::rangeWholeSample
void rangeWholeSample()
Sets the start and end points to include the range of the whole sound table.
Definition: Sample.h:131
Sample::phaseIncFromFreq
unsigned long phaseIncFromFreq(unsigned int frequency)
phaseIncFromFreq() and setPhaseInc() are for saving processor time when sliding between frequencies...
Definition: Sample.h:281
Sample::atIndex
int8_t atIndex(unsigned int index)
Returns the sample at the given table index.
Definition: Sample.h:264
Sample::setPhaseInc
void setPhaseInc(unsigned long phaseinc_fractional)
Set a specific phase increment.
Definition: Sample.h:291
Sample::setStart
void setStart(unsigned int startpos)
Sets the starting position in samples.
Definition: Sample.h:92
Sample::start
void start(unsigned int startpos)
Sets a new start position plays the sample from that position.
Definition: Sample.h:111
Sample::setTable
void setTable(const int8_t *TABLE_NAME)
Change the sound table which will be played by the Sample.
Definition: Sample.h:82
SAMPLE_F_BITS
#define SAMPLE_F_BITS
Definition: Sample.h:20
Sample::next
int8_t next()
Returns the sample at the current phase position, or 0 if looping is off and the phase overshoots th...
Definition: Sample.h:165
Q24n8
uint32_t Q24n8
unsigned fractional number using 24 integer bits and 8 fractional bits, represents 0 to 16777215 ...
Definition: mozzi_fixmath.h:45
Sample::isPlaying
boolean isPlaying()
Checks if the sample is playing by seeing if the phase is within the limits of its end position...
Definition: Sample.h:194
Sample::start
void start()
Resets the phase (the playhead) to the start position, which will be 0 unless set to another value wi...
Definition: Sample.h:101
Sample::setFreq
void setFreq(float frequency)
Set the sample frequency with a float.
Definition: Sample.h:236
Sample
Sample is like Oscil, it plays a wavetable.
Definition: Sample.h:48
SAMPLE_F_BITS_AS_MULTIPLIER
#define SAMPLE_F_BITS_AS_MULTIPLIER
Definition: Sample.h:21
Sample::Sample
Sample()
Constructor.
Definition: Sample.h:71
Sample::setLoopingOff
void setLoopingOff()
Turns looping off.
Definition: Sample.h:150