comparison projects/oscillator_bank/render.cpp @ 0:8a575ba3ab52

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