Mercurial > hg > beaglert
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 } |