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