annotate projects/oscillator_bank/render.cpp @ 45:579c86316008 newapi

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