annotate examples/10-Instruments/oscillator-bank/render.cpp @ 556:ce391098f321 prerelease tip

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