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