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 }