Mercurial > hg > beaglert
comparison core/RTAudio.cpp @ 108:3068421c0737 ultra-staging
Merged default into ultra-staging
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Tue, 18 Aug 2015 00:35:15 +0100 |
parents | 4255ecbb9bec f944d0b60fa8 |
children |
comparison
equal
deleted
inserted
replaced
54:d3f869b98147 | 108:3068421c0737 |
---|---|
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" | 34 |
35 // ARM interrupt number for PRU event EVTOUT7 | |
36 #define PRU_RTAUDIO_IRQ 21 | |
35 | 37 |
36 using namespace std; | 38 using namespace std; |
37 | 39 |
38 // Data structure to keep track of auxiliary tasks we | 40 // Data structure to keep track of auxiliary tasks we |
39 // can schedule | 41 // can schedule |
43 char *name; | 45 char *name; |
44 int priority; | 46 int priority; |
45 } InternalAuxiliaryTask; | 47 } InternalAuxiliaryTask; |
46 | 48 |
47 const char gRTAudioThreadName[] = "beaglert-audio"; | 49 const char gRTAudioThreadName[] = "beaglert-audio"; |
50 const char gRTAudioInterruptName[] = "beaglert-pru-irq"; | |
48 | 51 |
49 // Real-time tasks and objects | 52 // Real-time tasks and objects |
50 RT_TASK gRTAudioThread; | 53 RT_TASK gRTAudioThread; |
54 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
55 RT_INTR gRTAudioInterrupt; | |
56 #endif | |
51 PRU *gPRU = 0; | 57 PRU *gPRU = 0; |
52 I2c_Codec *gAudioCodec = 0; | 58 I2c_Codec *gAudioCodec = 0; |
53 | 59 |
54 vector<InternalAuxiliaryTask*> gAuxTasks; | 60 vector<InternalAuxiliaryTask*> gAuxTasks; |
55 | 61 |
56 // Flag which tells the audio task to stop | 62 // Flag which tells the audio task to stop |
57 bool gShouldStop = false; | 63 bool gShouldStop = false; |
58 | 64 |
59 // general settings | 65 // general settings |
60 char *gPRUFilename;//[256] = "pru_rtaudio.bin"; // path to PRU binary file | 66 char gPRUFilename[MAX_PRU_FILENAME_LENGTH]; // Path to PRU binary file (internal code if empty)_ |
61 int gRTAudioVerbose = 0; // Verbosity level for debugging | 67 int gRTAudioVerbose = 0; // Verbosity level for debugging |
62 int gAmplifierMutePin = -1; | 68 int gAmplifierMutePin = -1; |
63 int gAmplifierShouldBeginMuted = 0; | 69 int gAmplifierShouldBeginMuted = 0; |
64 | 70 |
65 // Number of audio and analog channels, globally accessible | 71 // Context which holds all the audio/sensor data passed to the render routines |
66 // At least gNumAnalogChannels and gNumDigitalChannels need to be global to be used | 72 BeagleRTContext gContext; |
67 // by the AnalogRead() and AnalogWrite() and the digital macros without creating | 73 |
68 // extra confusion in their use cases by passing this argument | 74 // User data passed in from main() |
69 int gNumAudioChannels = 0; | 75 void *gUserData; |
70 int gNumAnalogChannels = 0; | |
71 int gNumDigitalChannels = 0; | |
72 | |
73 | |
74 void printIntervals(){ | |
75 rt_printf("\n"); | |
76 gPRU->renderTimer.print(); | |
77 gPRU->sleepTimer.print(); | |
78 } | |
79 | 76 |
80 // initAudio() prepares the infrastructure for running PRU-based real-time | 77 // initAudio() prepares the infrastructure for running PRU-based real-time |
81 // audio, but does not actually start the calculations. | 78 // audio, but does not actually start the calculations. |
82 // periodSize indicates the number of _sensor_ frames per period: the audio period size | 79 // periodSize indicates the number of _sensor_ frames per period: the audio period size |
83 // is twice this value. In total, the audio latency in frames will be 4*periodSize, | 80 // is twice this value. In total, the audio latency in frames will be 4*periodSize, |
84 // plus any latency inherent in the ADCs and DACs themselves. | 81 // plus any latency inherent in the ADCs and DACs themselves. |
85 // useAnalog indicates whether to enable the ADC and DAC or just use the audio codec. | 82 // useAnalog indicates whether to enable the ADC and DAC or just use the audio codec. |
86 // numAnalogChannels indicates how many ADC and DAC channels to use. | 83 // numAnalogChannels indicates how many ADC and DAC channels to use. |
87 // userData is an opaque pointer which will be passed through to the initialise_render() | 84 // userData is an opaque pointer which will be passed through to the setup() |
88 // function for application-specific use | 85 // function for application-specific use |
89 // | 86 // |
90 // Returns 0 on success. | 87 // Returns 0 on success. |
91 | 88 |
92 | 89 int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData) |
93 int BeagleRT_initAudio(RTAudioSettings *settings, void *userData) | |
94 { | 90 { |
95 rt_print_auto_init(1); | 91 rt_print_auto_init(1); |
96 setVerboseLevel(settings->verbose); | 92 |
97 gPRUFilename=settings->pruFilename; | 93 BeagleRT_setVerboseLevel(settings->verbose); |
98 if(gRTAudioVerbose == 1) | 94 strncpy(gPRUFilename, settings->pruFilename, MAX_PRU_FILENAME_LENGTH); |
99 rt_printf("Running with Xenomai\n"); | 95 gUserData = userData; |
96 | |
97 // Initialise context data structure | |
98 memset(&gContext, 0, sizeof(BeagleRTContext)); | |
100 | 99 |
101 if(gRTAudioVerbose) { | 100 if(gRTAudioVerbose) { |
102 cout << "Starting with period size " << settings->periodSize << "; "; | 101 cout << "Starting with period size " << settings->periodSize << "; "; |
103 if(settings->useAnalog) | 102 if(settings->useAnalog) |
104 cout << "analog enabled\n"; | 103 cout << "analog enabled\n"; |
147 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) { | 146 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) { |
148 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; | 147 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; |
149 return 1; | 148 return 1; |
150 } | 149 } |
151 | 150 |
151 // Initialise the rendering environment: sample rates, frame counts, numbers of channels | |
152 gContext.audioSampleRate = 44100.0; | |
153 gContext.audioChannels = 2; | |
154 | |
155 if(settings->useAnalog) { | |
156 gContext.audioFrames = settings->periodSize * settings->numAnalogChannels / 4; | |
157 | |
158 gContext.analogFrames = settings->periodSize; | |
159 gContext.analogChannels = settings->numAnalogChannels; | |
160 gContext.analogSampleRate = gContext.audioSampleRate * 4.0 / (float)settings->numAnalogChannels; | |
161 } | |
162 else { | |
163 gContext.audioFrames = settings->periodSize * 2; | |
164 | |
165 gContext.analogFrames = 0; | |
166 gContext.analogChannels = 0; | |
167 gContext.analogSampleRate = 0; | |
168 } | |
169 | |
170 // For now, digital frame rate is equal to audio frame rate | |
171 if(settings->useDigital) { | |
172 gContext.digitalFrames = gContext.audioFrames; | |
173 gContext.digitalSampleRate = gContext.audioSampleRate; | |
174 gContext.digitalChannels = settings->numDigitalChannels; | |
175 } | |
176 else { | |
177 gContext.digitalFrames = 0; | |
178 gContext.digitalSampleRate = 0; | |
179 gContext.digitalChannels = 0; | |
180 } | |
181 | |
182 // Set flags based on init settings | |
183 if(settings->interleave) | |
184 gContext.flags |= BEAGLERT_FLAG_INTERLEAVED; | |
185 if(settings->analogOutputsPersist) | |
186 gContext.flags |= BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST; | |
187 | |
152 // Use PRU for audio | 188 // Use PRU for audio |
153 gPRU = new PRU(); | 189 gPRU = new PRU(&gContext); |
154 gAudioCodec = new I2c_Codec(); | 190 gAudioCodec = new I2c_Codec(); |
155 | 191 |
156 gNumDigitalChannels = settings->useDigital ? settings->numDigitalChannels : 0; //this is called here to make sure prepareGPIO initializes the appropriate GPIO pins | 192 // Initialise the GPIO pins, including possibly the digital pins in the render routines |
157 if(gPRU->prepareGPIO(settings->useAnalog, settings->useDigital, 1, 1)) { | 193 if(gPRU->prepareGPIO(1, 1)) { |
158 cout << "Error: unable to prepare GPIO for PRU audio\n"; | 194 cout << "Error: unable to prepare GPIO for PRU audio\n"; |
159 return 1; | 195 return 1; |
160 } | 196 } |
197 | |
198 // Get the PRU memory buffers ready to go | |
161 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) { | 199 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) { |
162 cout << "Error: unable to initialise PRU\n"; | 200 cout << "Error: unable to initialise PRU\n"; |
163 return 1; | 201 return 1; |
164 } | 202 } |
203 | |
204 // Prepare the audio codec, which clocks the whole system | |
165 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) { | 205 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) { |
166 cout << "Unable to open codec I2C\n"; | 206 cout << "Unable to open codec I2C\n"; |
167 return 1; | 207 return 1; |
168 } | 208 } |
169 if(gAudioCodec->initCodec()) { | 209 if(gAudioCodec->initCodec()) { |
174 // Set default volume levels | 214 // Set default volume levels |
175 BeagleRT_setDACLevel(settings->dacLevel); | 215 BeagleRT_setDACLevel(settings->dacLevel); |
176 BeagleRT_setADCLevel(settings->adcLevel); | 216 BeagleRT_setADCLevel(settings->adcLevel); |
177 BeagleRT_setHeadphoneLevel(settings->headphoneLevel); | 217 BeagleRT_setHeadphoneLevel(settings->headphoneLevel); |
178 | 218 |
179 // Initialise the rendering environment: pass the number of audio and analog | 219 // Call the user-defined initialisation function |
180 // channels, the period size for analog and audio, and the sample rates | 220 if(!setup(&gContext, userData)) { |
181 | |
182 int audioPeriodSize = settings->periodSize * 2; | |
183 float audioSampleRate = 44100.0; | |
184 float analogSampleRate = 22050.0; | |
185 if(settings->useAnalog) { | |
186 audioPeriodSize = settings->periodSize * settings->numAnalogChannels / 4; | |
187 analogSampleRate = audioSampleRate * 4.0 / (float)settings->numAnalogChannels; | |
188 } | |
189 | |
190 gNumAudioChannels = 2; | |
191 gNumAnalogChannels = settings->useAnalog ? settings->numAnalogChannels : 0; | |
192 if(!initialise_render(gNumAnalogChannels, gNumDigitalChannels, gNumAudioChannels, | |
193 settings->useAnalog ? settings->periodSize : 0, /* analog period size */ | |
194 audioPeriodSize, | |
195 analogSampleRate, audioSampleRate, | |
196 userData, settings)) { | |
197 cout << "Couldn't initialise audio rendering\n"; | 221 cout << "Couldn't initialise audio rendering\n"; |
198 return 1; | 222 return 1; |
199 } | 223 } |
200 gPRU->printIntervalsTask=createAuxiliaryTaskLoop(*printIntervals, 10, "transmit-receive-data"); | 224 |
201 return 0; | 225 return 0; |
202 } | 226 } |
203 | 227 |
204 // audioLoop() is the main function which starts the PRU audio code | 228 // audioLoop() is the main function which starts the PRU audio code |
205 // and then transfers control to the PRU object. The PRU object in | 229 // and then transfers control to the PRU object. The PRU object in |
218 rt_printf("Error: unable to start I2C audio codec\n"); | 242 rt_printf("Error: unable to start I2C audio codec\n"); |
219 gShouldStop = 1; | 243 gShouldStop = 1; |
220 } | 244 } |
221 else { | 245 else { |
222 if(gPRU->start(gPRUFilename)) { | 246 if(gPRU->start(gPRUFilename)) { |
223 rt_printf("Error: unable to start PRU %s\n", gPRUFilename); | 247 rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename); |
224 gShouldStop = 1; | 248 gShouldStop = 1; |
225 } | 249 } |
226 else { | 250 else { |
227 // All systems go. Run the loop; it will end when gShouldStop is set to 1 | 251 // All systems go. Run the loop; it will end when gShouldStop is set to 1 |
228 | 252 |
232 if(gRTAudioVerbose) | 256 if(gRTAudioVerbose) |
233 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); | 257 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); |
234 } | 258 } |
235 } | 259 } |
236 | 260 |
237 gPRU->loop(); | 261 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS |
238 | 262 gPRU->loop(&gRTAudioInterrupt, gUserData); |
263 #else | |
264 gPRU->loop(0, gUserData); | |
265 #endif | |
239 // Now clean up | 266 // Now clean up |
240 // gPRU->waitForFinish(); | 267 // gPRU->waitForFinish(); |
241 gPRU->disable(); | 268 gPRU->disable(); |
242 gAudioCodec->stopAudio(); | 269 gAudioCodec->stopAudio(); |
243 gPRU->cleanupGPIO(); | 270 gPRU->cleanupGPIO(); |
247 if(gRTAudioVerbose == 1) | 274 if(gRTAudioVerbose == 1) |
248 rt_printf("audio thread ended\n"); | 275 rt_printf("audio thread ended\n"); |
249 } | 276 } |
250 | 277 |
251 // Create a calculation loop which can run independently of the audio, at a different | 278 // Create a calculation loop which can run independently of the audio, at a different |
252 // (equal or lower) priority. Audio priority is 99; priority should be generally be less than this. | 279 // (equal or lower) priority. Audio priority is defined in BEAGLERT_AUDIO_PRIORITY; |
280 // priority should be generally be less than this. | |
253 // Returns an (opaque) pointer to the created task on success; 0 on failure | 281 // Returns an (opaque) pointer to the created task on success; 0 on failure |
254 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name) | 282 AuxiliaryTask BeagleRT_createAuxiliaryTask(void (*functionToCall)(void), int priority, const char *name) |
255 { | 283 { |
256 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); | 284 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); |
257 | 285 |
258 // Attempt to create the task | 286 // Attempt to create the task |
259 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) { | 287 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) { |
272 return (AuxiliaryTask)newTask; | 300 return (AuxiliaryTask)newTask; |
273 } | 301 } |
274 | 302 |
275 // Schedule a previously created auxiliary task. It will run when the priority rules next | 303 // Schedule a previously created auxiliary task. It will run when the priority rules next |
276 // allow it to be scheduled. | 304 // allow it to be scheduled. |
277 void scheduleAuxiliaryTask(AuxiliaryTask task) | 305 void BeagleRT_scheduleAuxiliaryTask(AuxiliaryTask task) |
278 { | 306 { |
279 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task; | 307 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task; |
280 | 308 |
281 rt_task_resume(&taskToSchedule->task); | 309 rt_task_resume(&taskToSchedule->task); |
282 } | 310 } |
309 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 | 337 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 |
310 // on success. | 338 // on success. |
311 | 339 |
312 int BeagleRT_startAudio() | 340 int BeagleRT_startAudio() |
313 { | 341 { |
314 // Create audio thread with the highest priority | 342 // Create audio thread with high Xenomai priority |
315 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) { | 343 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, BEAGLERT_AUDIO_PRIORITY, T_JOINABLE | T_FPU)) { |
316 cout << "Error: unable to create Xenomai audio thread" << endl; | 344 cout << "Error: unable to create Xenomai audio thread" << endl; |
317 return -1; | 345 return -1; |
318 } | 346 } |
347 | |
348 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
349 // Create an interrupt which the audio thread receives from the PRU | |
350 int result = 0; | |
351 if((result = rt_intr_create(&gRTAudioInterrupt, gRTAudioInterruptName, PRU_RTAUDIO_IRQ, I_NOAUTOENA)) != 0) { | |
352 cout << "Error: unable to create Xenomai interrupt for PRU (error " << result << ")" << endl; | |
353 return -1; | |
354 } | |
355 #endif | |
319 | 356 |
320 // Start all RT threads | 357 // Start all RT threads |
321 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { | 358 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { |
322 cout << "Error: unable to start Xenomai audio thread" << endl; | 359 cout << "Error: unable to start Xenomai audio thread" << endl; |
323 return -1; | 360 return -1; |
363 } | 400 } |
364 | 401 |
365 // Free any resources associated with PRU real-time audio | 402 // Free any resources associated with PRU real-time audio |
366 void BeagleRT_cleanupAudio() | 403 void BeagleRT_cleanupAudio() |
367 { | 404 { |
368 cleanup_render(); | 405 cleanup(&gContext, gUserData); |
369 | 406 |
370 // Clean up the auxiliary tasks | 407 // Clean up the auxiliary tasks |
371 vector<InternalAuxiliaryTask*>::iterator it; | 408 vector<InternalAuxiliaryTask*>::iterator it; |
372 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { | 409 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { |
373 InternalAuxiliaryTask *taskStruct = *it; | 410 InternalAuxiliaryTask *taskStruct = *it; |
374 | 411 |
412 // Delete the task | |
413 rt_task_delete(&taskStruct->task); | |
414 | |
375 // Free the name string and the struct itself | 415 // Free the name string and the struct itself |
376 free(taskStruct->name); | 416 free(taskStruct->name); |
377 free(taskStruct); | 417 free(taskStruct); |
378 } | 418 } |
379 gAuxTasks.clear(); | 419 gAuxTasks.clear(); |
420 | |
421 // Delete the audio task and its interrupt | |
422 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS | |
423 rt_intr_delete(&gRTAudioInterrupt); | |
424 #endif | |
425 rt_task_delete(&gRTAudioThread); | |
380 | 426 |
381 if(gPRU != 0) | 427 if(gPRU != 0) |
382 delete gPRU; | 428 delete gPRU; |
383 if(gAudioCodec != 0) | 429 if(gAudioCodec != 0) |
384 delete gAudioCodec; | 430 delete gAudioCodec; |
429 | 475 |
430 return gpio_set_value(gAmplifierMutePin, pinValue); | 476 return gpio_set_value(gAmplifierMutePin, pinValue); |
431 } | 477 } |
432 | 478 |
433 // Set the verbosity level | 479 // Set the verbosity level |
434 void setVerboseLevel(int level) | 480 void BeagleRT_setVerboseLevel(int level) |
435 { | 481 { |
436 gRTAudioVerbose = level; | 482 gRTAudioVerbose = level; |
437 } | 483 } |