Mercurial > hg > beaglert
comparison core/RTAudio.cpp @ 67:472e892c6e41
Merge newapi into default
author | Andrew McPherson <a.mcpherson@qmul.ac.uk> |
---|---|
date | Fri, 17 Jul 2015 15:28:18 +0100 |
parents | 3c3a1357657d |
children | f944d0b60fa8 |
comparison
equal
deleted
inserted
replaced
21:0d80ff9e2227 | 67:472e892c6e41 |
---|---|
22 | 22 |
23 // Xenomai-specific includes | 23 // Xenomai-specific includes |
24 #include <sys/mman.h> | 24 #include <sys/mman.h> |
25 #include <native/task.h> | 25 #include <native/task.h> |
26 #include <native/timer.h> | 26 #include <native/timer.h> |
27 #include <native/intr.h> | |
27 #include <rtdk.h> | 28 #include <rtdk.h> |
28 | 29 |
29 #include "../include/RTAudio.h" | 30 #include "../include/BeagleRT.h" |
30 #include "../include/PRU.h" | 31 #include "../include/PRU.h" |
31 #include "../include/I2c_Codec.h" | 32 #include "../include/I2c_Codec.h" |
32 #include "../include/render.h" | |
33 #include "../include/GPIOcontrol.h" | 33 #include "../include/GPIOcontrol.h" |
34 #include "../include/client.h" | |
35 | |
36 // ARM interrupt number for PRU event EVTOUT7 | |
37 #define PRU_RTAUDIO_IRQ 21 | |
34 | 38 |
35 using namespace std; | 39 using namespace std; |
36 | 40 |
37 // Data structure to keep track of auxiliary tasks we | 41 // Data structure to keep track of auxiliary tasks we |
38 // can schedule | 42 // can schedule |
42 char *name; | 46 char *name; |
43 int priority; | 47 int priority; |
44 } InternalAuxiliaryTask; | 48 } InternalAuxiliaryTask; |
45 | 49 |
46 const char gRTAudioThreadName[] = "beaglert-audio"; | 50 const char gRTAudioThreadName[] = "beaglert-audio"; |
51 const char gRTAudioInterruptName[] = "beaglert-pru-irq"; | |
47 | 52 |
48 // Real-time tasks and objects | 53 // Real-time tasks and objects |
49 RT_TASK gRTAudioThread; | 54 RT_TASK gRTAudioThread; |
55 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
56 RT_INTR gRTAudioInterrupt; | |
57 #endif | |
50 PRU *gPRU = 0; | 58 PRU *gPRU = 0; |
51 I2c_Codec *gAudioCodec = 0; | 59 I2c_Codec *gAudioCodec = 0; |
52 | 60 |
53 vector<InternalAuxiliaryTask*> gAuxTasks; | 61 vector<InternalAuxiliaryTask*> gAuxTasks; |
54 | 62 |
55 // Flag which tells the audio task to stop | 63 // Flag which tells the audio task to stop |
56 bool gShouldStop = false; | 64 bool gShouldStop = false; |
57 | 65 |
58 // general settings | 66 // general settings |
67 char gPRUFilename[MAX_PRU_FILENAME_LENGTH]; // Path to PRU binary file (internal code if empty)_ | |
59 int gRTAudioVerbose = 0; // Verbosity level for debugging | 68 int gRTAudioVerbose = 0; // Verbosity level for debugging |
60 int gAmplifierMutePin = -1; | 69 int gAmplifierMutePin = -1; |
61 int gAmplifierShouldBeginMuted = 0; | 70 int gAmplifierShouldBeginMuted = 0; |
62 | 71 |
63 // Number of audio and matrix channels, globally accessible | 72 // Context which holds all the audio/sensor data passed to the render routines |
64 // At least gNumMatrixChannels needs to be global to be used | 73 BeagleRTContext gContext; |
65 // by the analogRead() and analogWrite() macros without creating | 74 |
66 // extra confusion in their use cases by passing this argument | 75 // User data passed in from main() |
67 int gNumAudioChannels = 0; | 76 void *gUserData; |
68 int gNumMatrixChannels = 0; | |
69 | 77 |
70 // initAudio() prepares the infrastructure for running PRU-based real-time | 78 // initAudio() prepares the infrastructure for running PRU-based real-time |
71 // audio, but does not actually start the calculations. | 79 // audio, but does not actually start the calculations. |
72 // periodSize indicates the number of _sensor_ frames per period: the audio period size | 80 // periodSize indicates the number of _sensor_ frames per period: the audio period size |
73 // is twice this value. In total, the audio latency in frames will be 4*periodSize, | 81 // is twice this value. In total, the audio latency in frames will be 4*periodSize, |
74 // plus any latency inherent in the ADCs and DACs themselves. | 82 // plus any latency inherent in the ADCs and DACs themselves. |
75 // useMatrix indicates whether to enable the ADC and DAC or just use the audio codec. | 83 // useAnalog indicates whether to enable the ADC and DAC or just use the audio codec. |
76 // numMatrixChannels indicates how many ADC and DAC channels to use. | 84 // numAnalogChannels indicates how many ADC and DAC channels to use. |
77 // userData is an opaque pointer which will be passed through to the initialise_render() | 85 // userData is an opaque pointer which will be passed through to the setup() |
78 // function for application-specific use | 86 // function for application-specific use |
79 // | 87 // |
80 // Returns 0 on success. | 88 // Returns 0 on success. |
81 | 89 |
82 int BeagleRT_initAudio(RTAudioSettings *settings, void *userData) | 90 int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData) |
83 { | 91 { |
84 rt_print_auto_init(1); | 92 rt_print_auto_init(1); |
85 setVerboseLevel(settings->verbose); | 93 |
86 | 94 BeagleRT_setVerboseLevel(settings->verbose); |
87 if(gRTAudioVerbose == 1) | 95 strncpy(gPRUFilename, settings->pruFilename, MAX_PRU_FILENAME_LENGTH); |
88 rt_printf("Running with Xenomai\n"); | 96 gUserData = userData; |
97 | |
98 // Initialise context data structure | |
99 memset(&gContext, 0, sizeof(BeagleRTContext)); | |
89 | 100 |
90 if(gRTAudioVerbose) { | 101 if(gRTAudioVerbose) { |
91 cout << "Starting with period size " << settings->periodSize << "; "; | 102 cout << "Starting with period size " << settings->periodSize << "; "; |
92 if(settings->useMatrix) | 103 if(settings->useAnalog) |
93 cout << "matrix enabled\n"; | 104 cout << "analog enabled\n"; |
94 else | 105 else |
95 cout << "matrix disabled\n"; | 106 cout << "analog disabled\n"; |
96 cout << "DAC level " << settings->dacLevel << "dB; ADC level " << settings->adcLevel; | 107 cout << "DAC level " << settings->dacLevel << "dB; ADC level " << settings->adcLevel; |
97 cout << "dB; headphone level " << settings->headphoneLevel << "dB\n"; | 108 cout << "dB; headphone level " << settings->headphoneLevel << "dB\n"; |
98 if(settings->beginMuted) | 109 if(settings->beginMuted) |
99 cout << "Beginning with speaker muted\n"; | 110 cout << "Beginning with speaker muted\n"; |
100 } | 111 } |
104 gAmplifierMutePin = settings->ampMutePin; | 115 gAmplifierMutePin = settings->ampMutePin; |
105 gAmplifierShouldBeginMuted = settings->beginMuted; | 116 gAmplifierShouldBeginMuted = settings->beginMuted; |
106 | 117 |
107 if(gpio_export(settings->ampMutePin)) { | 118 if(gpio_export(settings->ampMutePin)) { |
108 if(gRTAudioVerbose) | 119 if(gRTAudioVerbose) |
109 cout << "Warning: couldn't export amplifier mute pin\n"; | 120 cout << "Warning: couldn't export amplifier mute pin " << settings-> ampMutePin << "\n"; |
110 } | 121 } |
111 if(gpio_set_dir(settings->ampMutePin, OUTPUT_PIN)) { | 122 if(gpio_set_dir(settings->ampMutePin, OUTPUT_PIN)) { |
112 if(gRTAudioVerbose) | 123 if(gRTAudioVerbose) |
113 cout << "Couldn't set direction on amplifier mute pin\n"; | 124 cout << "Couldn't set direction on amplifier mute pin\n"; |
114 return -1; | 125 return -1; |
118 cout << "Couldn't set value on amplifier mute pin\n"; | 129 cout << "Couldn't set value on amplifier mute pin\n"; |
119 return -1; | 130 return -1; |
120 } | 131 } |
121 } | 132 } |
122 | 133 |
123 // Limit the matrix channels to sane values | 134 // Limit the analog channels to sane values |
124 if(settings->numMatrixChannels >= 8) | 135 if(settings->numAnalogChannels >= 8) |
125 settings->numMatrixChannels = 8; | 136 settings->numAnalogChannels = 8; |
126 else if(settings->numMatrixChannels >= 4) | 137 else if(settings->numAnalogChannels >= 4) |
127 settings->numMatrixChannels = 4; | 138 settings->numAnalogChannels = 4; |
128 else | 139 else |
129 settings->numMatrixChannels = 2; | 140 settings->numAnalogChannels = 2; |
130 | 141 |
131 // Sanity check the combination of channels and period size | 142 // Sanity check the combination of channels and period size |
132 if(settings->numMatrixChannels <= 4 && settings->periodSize < 2) { | 143 if(settings->numAnalogChannels <= 4 && settings->periodSize < 2) { |
133 cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; | 144 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; |
134 return 1; | 145 return 1; |
135 } | 146 } |
136 if(settings->numMatrixChannels <= 2 && settings->periodSize < 4) { | 147 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) { |
137 cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; | 148 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; |
138 return 1; | 149 return 1; |
139 } | 150 } |
151 | |
152 // Initialise the rendering environment: sample rates, frame counts, numbers of channels | |
153 gContext.audioSampleRate = 44100.0; | |
154 gContext.audioChannels = 2; | |
155 | |
156 if(settings->useAnalog) { | |
157 gContext.audioFrames = settings->periodSize * settings->numAnalogChannels / 4; | |
158 | |
159 gContext.analogFrames = settings->periodSize; | |
160 gContext.analogChannels = settings->numAnalogChannels; | |
161 gContext.analogSampleRate = gContext.audioSampleRate * 4.0 / (float)settings->numAnalogChannels; | |
162 } | |
163 else { | |
164 gContext.audioFrames = settings->periodSize * 2; | |
165 | |
166 gContext.analogFrames = 0; | |
167 gContext.analogChannels = 0; | |
168 gContext.analogSampleRate = 0; | |
169 } | |
170 | |
171 // For now, digital frame rate is equal to audio frame rate | |
172 if(settings->useDigital) { | |
173 gContext.digitalFrames = gContext.audioFrames; | |
174 gContext.digitalSampleRate = gContext.audioSampleRate; | |
175 gContext.digitalChannels = settings->numDigitalChannels; | |
176 } | |
177 else { | |
178 gContext.digitalFrames = 0; | |
179 gContext.digitalSampleRate = 0; | |
180 gContext.digitalChannels = 0; | |
181 } | |
182 | |
183 // Set flags based on init settings | |
184 if(settings->interleave) | |
185 gContext.flags |= BEAGLERT_FLAG_INTERLEAVED; | |
186 if(settings->analogOutputsPersist) | |
187 gContext.flags |= BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST; | |
140 | 188 |
141 // Use PRU for audio | 189 // Use PRU for audio |
142 gPRU = new PRU(); | 190 gPRU = new PRU(&gContext); |
143 gAudioCodec = new I2c_Codec(); | 191 gAudioCodec = new I2c_Codec(); |
144 | 192 |
145 if(gPRU->prepareGPIO(settings->useMatrix, 1, 1)) { | 193 // Initialise the GPIO pins, including possibly the digital pins in the render routines |
194 if(gPRU->prepareGPIO(1, 1)) { | |
146 cout << "Error: unable to prepare GPIO for PRU audio\n"; | 195 cout << "Error: unable to prepare GPIO for PRU audio\n"; |
147 return 1; | 196 return 1; |
148 } | 197 } |
149 if(gPRU->initialise(0, settings->periodSize, settings->numMatrixChannels, true)) { | 198 |
199 // Get the PRU memory buffers ready to go | |
200 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) { | |
150 cout << "Error: unable to initialise PRU\n"; | 201 cout << "Error: unable to initialise PRU\n"; |
151 return 1; | 202 return 1; |
152 } | 203 } |
204 | |
205 // Prepare the audio codec, which clocks the whole system | |
153 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) { | 206 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) { |
154 cout << "Unable to open codec I2C\n"; | 207 cout << "Unable to open codec I2C\n"; |
155 return 1; | 208 return 1; |
156 } | 209 } |
157 if(gAudioCodec->initCodec()) { | 210 if(gAudioCodec->initCodec()) { |
162 // Set default volume levels | 215 // Set default volume levels |
163 BeagleRT_setDACLevel(settings->dacLevel); | 216 BeagleRT_setDACLevel(settings->dacLevel); |
164 BeagleRT_setADCLevel(settings->adcLevel); | 217 BeagleRT_setADCLevel(settings->adcLevel); |
165 BeagleRT_setHeadphoneLevel(settings->headphoneLevel); | 218 BeagleRT_setHeadphoneLevel(settings->headphoneLevel); |
166 | 219 |
167 // Initialise the rendering environment: pass the number of audio and matrix | 220 // Call the user-defined initialisation function |
168 // channels, the period size for matrix and audio, and the sample rates | 221 if(!setup(&gContext, userData)) { |
169 | |
170 int audioPeriodSize = settings->periodSize * 2; | |
171 float audioSampleRate = 44100.0; | |
172 float matrixSampleRate = 22050.0; | |
173 if(settings->useMatrix) { | |
174 audioPeriodSize = settings->periodSize * settings->numMatrixChannels / 4; | |
175 matrixSampleRate = audioSampleRate * 4.0 / (float)settings->numMatrixChannels; | |
176 } | |
177 | |
178 gNumAudioChannels = 2; | |
179 gNumMatrixChannels = settings->useMatrix ? settings->numMatrixChannels : 0; | |
180 | |
181 if(!initialise_render(gNumMatrixChannels, gNumAudioChannels, | |
182 settings->useMatrix ? settings->periodSize : 0, /* matrix period size */ | |
183 audioPeriodSize, | |
184 matrixSampleRate, audioSampleRate, | |
185 userData)) { | |
186 cout << "Couldn't initialise audio rendering\n"; | 222 cout << "Couldn't initialise audio rendering\n"; |
187 return 1; | 223 return 1; |
188 } | 224 } |
189 | 225 |
190 return 0; | 226 return 0; |
206 if(gAudioCodec->startAudio(0)) { | 242 if(gAudioCodec->startAudio(0)) { |
207 rt_printf("Error: unable to start I2C audio codec\n"); | 243 rt_printf("Error: unable to start I2C audio codec\n"); |
208 gShouldStop = 1; | 244 gShouldStop = 1; |
209 } | 245 } |
210 else { | 246 else { |
211 if(gPRU->start()) { | 247 if(gPRU->start(gPRUFilename)) { |
212 rt_printf("Error: unable to start PRU\n"); | 248 rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename); |
213 gShouldStop = 1; | 249 gShouldStop = 1; |
214 } | 250 } |
215 else { | 251 else { |
216 // All systems go. Run the loop; it will end when gShouldStop is set to 1 | 252 // All systems go. Run the loop; it will end when gShouldStop is set to 1 |
217 | 253 |
221 if(gRTAudioVerbose) | 257 if(gRTAudioVerbose) |
222 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); | 258 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); |
223 } | 259 } |
224 } | 260 } |
225 | 261 |
226 gPRU->loop(); | 262 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS |
227 | 263 gPRU->loop(&gRTAudioInterrupt, gUserData); |
264 #else | |
265 gPRU->loop(0, gUserData); | |
266 #endif | |
228 // Now clean up | 267 // Now clean up |
229 // gPRU->waitForFinish(); | 268 // gPRU->waitForFinish(); |
230 gPRU->disable(); | 269 gPRU->disable(); |
231 gAudioCodec->stopAudio(); | 270 gAudioCodec->stopAudio(); |
232 gPRU->cleanupGPIO(); | 271 gPRU->cleanupGPIO(); |
236 if(gRTAudioVerbose == 1) | 275 if(gRTAudioVerbose == 1) |
237 rt_printf("audio thread ended\n"); | 276 rt_printf("audio thread ended\n"); |
238 } | 277 } |
239 | 278 |
240 // Create a calculation loop which can run independently of the audio, at a different | 279 // Create a calculation loop which can run independently of the audio, at a different |
241 // (equal or lower) priority. Audio priority is 99; priority should be generally be less than this. | 280 // (equal or lower) priority. Audio priority is defined in BEAGLERT_AUDIO_PRIORITY; |
281 // priority should be generally be less than this. | |
242 // Returns an (opaque) pointer to the created task on success; 0 on failure | 282 // Returns an (opaque) pointer to the created task on success; 0 on failure |
243 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name) | 283 AuxiliaryTask BeagleRT_createAuxiliaryTask(void (*functionToCall)(void), int priority, const char *name) |
244 { | 284 { |
245 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); | 285 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); |
246 | 286 |
247 // Attempt to create the task | 287 // Attempt to create the task |
248 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) { | 288 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) { |
261 return (AuxiliaryTask)newTask; | 301 return (AuxiliaryTask)newTask; |
262 } | 302 } |
263 | 303 |
264 // Schedule a previously created auxiliary task. It will run when the priority rules next | 304 // Schedule a previously created auxiliary task. It will run when the priority rules next |
265 // allow it to be scheduled. | 305 // allow it to be scheduled. |
266 void scheduleAuxiliaryTask(AuxiliaryTask task) | 306 void BeagleRT_scheduleAuxiliaryTask(AuxiliaryTask task) |
267 { | 307 { |
268 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task; | 308 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task; |
269 | 309 |
270 rt_task_resume(&taskToSchedule->task); | 310 rt_task_resume(&taskToSchedule->task); |
271 } | 311 } |
298 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 | 338 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 |
299 // on success. | 339 // on success. |
300 | 340 |
301 int BeagleRT_startAudio() | 341 int BeagleRT_startAudio() |
302 { | 342 { |
303 // Create audio thread with the highest priority | 343 // Create audio thread with high Xenomai priority |
304 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) { | 344 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, BEAGLERT_AUDIO_PRIORITY, T_JOINABLE | T_FPU)) { |
305 cout << "Error: unable to create Xenomai audio thread" << endl; | 345 cout << "Error: unable to create Xenomai audio thread" << endl; |
306 return -1; | 346 return -1; |
307 } | 347 } |
348 | |
349 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
350 // Create an interrupt which the audio thread receives from the PRU | |
351 int result = 0; | |
352 if((result = rt_intr_create(&gRTAudioInterrupt, gRTAudioInterruptName, PRU_RTAUDIO_IRQ, I_NOAUTOENA)) != 0) { | |
353 cout << "Error: unable to create Xenomai interrupt for PRU (error " << result << ")" << endl; | |
354 return -1; | |
355 } | |
356 #endif | |
308 | 357 |
309 // Start all RT threads | 358 // Start all RT threads |
310 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { | 359 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { |
311 cout << "Error: unable to start Xenomai audio thread" << endl; | 360 cout << "Error: unable to start Xenomai audio thread" << endl; |
312 return -1; | 361 return -1; |
352 } | 401 } |
353 | 402 |
354 // Free any resources associated with PRU real-time audio | 403 // Free any resources associated with PRU real-time audio |
355 void BeagleRT_cleanupAudio() | 404 void BeagleRT_cleanupAudio() |
356 { | 405 { |
357 cleanup_render(); | 406 cleanup(&gContext, gUserData); |
358 | 407 |
359 // Clean up the auxiliary tasks | 408 // Clean up the auxiliary tasks |
360 vector<InternalAuxiliaryTask*>::iterator it; | 409 vector<InternalAuxiliaryTask*>::iterator it; |
361 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { | 410 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { |
362 InternalAuxiliaryTask *taskStruct = *it; | 411 InternalAuxiliaryTask *taskStruct = *it; |
363 | 412 |
413 // Delete the task | |
414 rt_task_delete(&taskStruct->task); | |
415 | |
364 // Free the name string and the struct itself | 416 // Free the name string and the struct itself |
365 free(taskStruct->name); | 417 free(taskStruct->name); |
366 free(taskStruct); | 418 free(taskStruct); |
367 } | 419 } |
368 gAuxTasks.clear(); | 420 gAuxTasks.clear(); |
421 | |
422 // Delete the audio task and its interrupt | |
423 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
424 rt_intr_delete(&gRTAudioInterrupt); | |
425 #endif | |
426 rt_task_delete(&gRTAudioThread); | |
369 | 427 |
370 if(gPRU != 0) | 428 if(gPRU != 0) |
371 delete gPRU; | 429 delete gPRU; |
372 if(gAudioCodec != 0) | 430 if(gAudioCodec != 0) |
373 delete gAudioCodec; | 431 delete gAudioCodec; |
418 | 476 |
419 return gpio_set_value(gAmplifierMutePin, pinValue); | 477 return gpio_set_value(gAmplifierMutePin, pinValue); |
420 } | 478 } |
421 | 479 |
422 // Set the verbosity level | 480 // Set the verbosity level |
423 void setVerboseLevel(int level) | 481 void BeagleRT_setVerboseLevel(int level) |
424 { | 482 { |
425 gRTAudioVerbose = level; | 483 gRTAudioVerbose = level; |
426 } | 484 } |