Mercurial > hg > beaglert
comparison examples/10-Instruments/oscillator_bank/render.cpp @ 464:8fcfbfb32aa0 prerelease
Examples reorder with subdirectories. Added header to each project. Moved Doxygen to bottom of render.cpp.
author | Robert Jack <robert.h.jack@gmail.com> |
---|---|
date | Mon, 20 Jun 2016 16:20:38 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
463:c47709e8b5c9 | 464:8fcfbfb32aa0 |
---|---|
1 /* | |
2 ____ _____ _ _ | |
3 | __ )| ____| | / \ | |
4 | _ \| _| | | / _ \ | |
5 | |_) | |___| |___ / ___ \ | |
6 |____/|_____|_____/_/ \_\.io | |
7 | |
8 */ | |
9 | |
10 /* | |
11 * render.cpp | |
12 * | |
13 * Created on: Oct 24, 2014 | |
14 * Author: parallels | |
15 */ | |
16 | |
17 /** | |
18 \example 4_oscillator_bank | |
19 | |
20 Oscillator Bank | |
21 ---------------------- | |
22 | |
23 These files demonstrate an oscillator bank implemented in assembly code | |
24 that is used as part of the d-box project. | |
25 */ | |
26 | |
27 #include <Bela.h> | |
28 #include <rtdk.h> | |
29 #include <cstdlib> | |
30 #include <cmath> | |
31 #include <cstring> | |
32 #include <time.h> | |
33 | |
34 const float kMinimumFrequency = 20.0f; | |
35 const float kMaximumFrequency = 8000.0f; | |
36 | |
37 float *gWavetable; // Buffer holding the precalculated sine lookup table | |
38 float *gPhases; // Buffer holding the phase of each oscillator | |
39 float *gFrequencies; // Buffer holding the frequencies of each oscillator | |
40 float *gAmplitudes; // Buffer holding the amplitudes of each oscillator | |
41 float *gDFrequencies; // Buffer holding the derivatives of frequency | |
42 float *gDAmplitudes; // Buffer holding the derivatives of amplitude | |
43 | |
44 float gAudioSampleRate; | |
45 int gSampleCount; // Sample counter for indicating when to update frequencies | |
46 float gNewMinFrequency; | |
47 float gNewMaxFrequency; | |
48 | |
49 // Task for handling the update of the frequencies using the matrix | |
50 AuxiliaryTask gFrequencyUpdateTask; | |
51 | |
52 // These settings are carried over from main.cpp | |
53 // Setting global variables is an alternative approach | |
54 // to passing a structure to userData in setup() | |
55 | |
56 extern int gNumOscillators; | |
57 extern int gWavetableLength; | |
58 | |
59 void recalculate_frequencies(); | |
60 | |
61 extern "C" { | |
62 // Function prototype for ARM assembly implementation of oscillator bank | |
63 void oscillator_bank_neon(int numAudioFrames, float *audioOut, | |
64 int activePartialNum, int lookupTableSize, | |
65 float *phases, float *frequencies, float *amplitudes, | |
66 float *freqDerivatives, float *ampDerivatives, | |
67 float *lookupTable); | |
68 } | |
69 | |
70 // setup() is called once before the audio rendering starts. | |
71 // Use it to perform any initialisation and allocation which is dependent | |
72 // on the period size or sample rate. | |
73 // | |
74 // userData holds an opaque pointer to a data structure that was passed | |
75 // in from the call to initAudio(). | |
76 // | |
77 // Return true on success; returning false halts the program. | |
78 bool setup(BelaContext *context, void *userData) | |
79 { | |
80 srandom(time(NULL)); | |
81 | |
82 if(context->audioChannels != 2) { | |
83 rt_printf("Error: this example needs stereo audio enabled\n"); | |
84 return false; | |
85 } | |
86 | |
87 // Initialise the sine wavetable | |
88 if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { | |
89 rt_printf("Error allocating wavetable\n"); | |
90 return false; | |
91 } | |
92 for(int n = 0; n < gWavetableLength + 1; n++) | |
93 gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); | |
94 | |
95 // Allocate the other buffers | |
96 if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { | |
97 rt_printf("Error allocating phase buffer\n"); | |
98 return false; | |
99 } | |
100 if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { | |
101 rt_printf("Error allocating frequency buffer\n"); | |
102 return false; | |
103 } | |
104 if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { | |
105 rt_printf("Error allocating amplitude buffer\n"); | |
106 return false; | |
107 } | |
108 if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { | |
109 rt_printf("Error allocating frequency derivative buffer\n"); | |
110 return false; | |
111 } | |
112 if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { | |
113 rt_printf("Error allocating amplitude derivative buffer\n"); | |
114 return false; | |
115 } | |
116 | |
117 // Initialise buffer contents | |
118 | |
119 float freq = kMinimumFrequency; | |
120 float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; | |
121 | |
122 for(int n = 0; n < gNumOscillators; n++) { | |
123 gPhases[n] = 0.0; | |
124 | |
125 if(context->analogFrames == 0) { | |
126 // Random frequencies when used without matrix | |
127 gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); | |
128 } | |
129 else { | |
130 // Constant spread of frequencies when used with matrix | |
131 gFrequencies[n] = freq; | |
132 freq += increment; | |
133 } | |
134 | |
135 // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians | |
136 gFrequencies[n] *= (float)gWavetableLength / context->audioSampleRate; | |
137 gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; | |
138 gDFrequencies[n] = gDAmplitudes[n] = 0.0; | |
139 } | |
140 | |
141 increment = 0; | |
142 freq = 440.0; | |
143 | |
144 for(int n = 0; n < gNumOscillators; n++) { | |
145 // Update the frequencies to a regular spread, plus a small amount of randomness | |
146 // to avoid weird phase effects | |
147 float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; | |
148 float newFreq = freq * randScale; | |
149 | |
150 // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians | |
151 gFrequencies[n] = newFreq * (float)gWavetableLength / context->audioSampleRate; | |
152 | |
153 freq += increment; | |
154 } | |
155 | |
156 // Initialise auxiliary tasks | |
157 if((gFrequencyUpdateTask = Bela_createAuxiliaryTask(&recalculate_frequencies, 85, "bela-update-frequencies")) == 0) | |
158 return false; | |
159 | |
160 //for(int n = 0; n < gNumOscillators; n++) | |
161 // rt_printf("%f\n", gFrequencies[n]); | |
162 | |
163 gAudioSampleRate = context->audioSampleRate; | |
164 gSampleCount = 0; | |
165 | |
166 return true; | |
167 } | |
168 | |
169 // render() is called regularly at the highest priority by the audio engine. | |
170 // Input and output are given from the audio hardware and the other | |
171 // ADCs and DACs (if available). If only audio is available, numMatrixFrames | |
172 // will be 0. | |
173 | |
174 void render(BelaContext *context, void *userData) | |
175 { | |
176 // Initialise buffer to 0 | |
177 memset(context->audioOut, 0, 2 * context->audioFrames * sizeof(float)); | |
178 | |
179 // Render audio frames | |
180 oscillator_bank_neon(context->audioFrames, context->audioOut, | |
181 gNumOscillators, gWavetableLength, | |
182 gPhases, gFrequencies, gAmplitudes, | |
183 gDFrequencies, gDAmplitudes, | |
184 gWavetable); | |
185 | |
186 if(context->analogFrames != 0 && (gSampleCount += context->audioFrames) >= 128) { | |
187 gSampleCount = 0; | |
188 gNewMinFrequency = map(context->analogIn[0], 0, 1.0, 1000.0f, 8000.0f); | |
189 gNewMaxFrequency = map(context->analogIn[1], 0, 1.0, 1000.0f, 8000.0f); | |
190 | |
191 // Make sure max >= min | |
192 if(gNewMaxFrequency < gNewMinFrequency) { | |
193 float temp = gNewMaxFrequency; | |
194 gNewMaxFrequency = gNewMinFrequency; | |
195 gNewMinFrequency = temp; | |
196 } | |
197 | |
198 // Request that the lower-priority task run at next opportunity | |
199 //Bela_scheduleAuxiliaryTask(gFrequencyUpdateTask); | |
200 } | |
201 } | |
202 | |
203 // This is a lower-priority call to update the frequencies which will happen | |
204 // periodically when the matrix is enabled. By placing it at a lower priority, | |
205 // it has minimal effect on the audio performance but it will take longer to | |
206 // complete if the system is under heavy audio load. | |
207 | |
208 void recalculate_frequencies() | |
209 { | |
210 float freq = gNewMinFrequency; | |
211 float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; | |
212 | |
213 for(int n = 0; n < gNumOscillators; n++) { | |
214 // Update the frequencies to a regular spread, plus a small amount of randomness | |
215 // to avoid weird phase effects | |
216 float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; | |
217 float newFreq = freq * randScale; | |
218 | |
219 // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians | |
220 gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; | |
221 | |
222 freq += increment; | |
223 } | |
224 } | |
225 | |
226 | |
227 // cleanup() is called once at the end, after the audio has stopped. | |
228 // Release any resources that were allocated in setup(). | |
229 | |
230 void cleanup(BelaContext *context, void *userData) | |
231 { | |
232 free(gWavetable); | |
233 free(gPhases); | |
234 free(gFrequencies); | |
235 free(gAmplitudes); | |
236 free(gDFrequencies); | |
237 free(gDAmplitudes); | |
238 } |