comparison core/RTAudio.cpp @ 45:579c86316008 newapi

Major API overhaul. Moved to a single data structure for handling render functions. Functionally, generally similar except for scheduling within PRU loop function, which now uses interrupts from the PRU rather than polling. This requires an updated kernel.
author andrewm
date Thu, 28 May 2015 14:35:55 -0400
parents ad5cd8dd99b3
children 643cbee74eda
comparison
equal deleted inserted replaced
40:419ce4ebfc4c 45:579c86316008
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 #include "../include/client.h"
35
36 // ARM interrupt number for PRU event EVTOUT7
37 #define PRU_RTAUDIO_IRQ 21
35 38
36 using namespace std; 39 using namespace std;
37 40
38 // Data structure to keep track of auxiliary tasks we 41 // Data structure to keep track of auxiliary tasks we
39 // can schedule 42 // can schedule
43 char *name; 46 char *name;
44 int priority; 47 int priority;
45 } InternalAuxiliaryTask; 48 } InternalAuxiliaryTask;
46 49
47 const char gRTAudioThreadName[] = "beaglert-audio"; 50 const char gRTAudioThreadName[] = "beaglert-audio";
51 const char gRTAudioInterruptName[] = "beaglert-pru-irq";
48 52
49 // Real-time tasks and objects 53 // Real-time tasks and objects
50 RT_TASK gRTAudioThread; 54 RT_TASK gRTAudioThread;
55 RT_INTR gRTAudioInterrupt;
51 PRU *gPRU = 0; 56 PRU *gPRU = 0;
52 I2c_Codec *gAudioCodec = 0; 57 I2c_Codec *gAudioCodec = 0;
53 58
54 vector<InternalAuxiliaryTask*> gAuxTasks; 59 vector<InternalAuxiliaryTask*> gAuxTasks;
55 60
56 // Flag which tells the audio task to stop 61 // Flag which tells the audio task to stop
57 bool gShouldStop = false; 62 bool gShouldStop = false;
58 63
59 // general settings 64 // general settings
60 char *gPRUFilename;//[256] = "pru_rtaudio.bin"; // path to PRU binary file 65 char gPRUFilename[MAX_PRU_FILENAME_LENGTH]; // Path to PRU binary file (internal code if empty)_
61 int gRTAudioVerbose = 0; // Verbosity level for debugging 66 int gRTAudioVerbose = 0; // Verbosity level for debugging
62 int gAmplifierMutePin = -1; 67 int gAmplifierMutePin = -1;
63 int gAmplifierShouldBeginMuted = 0; 68 int gAmplifierShouldBeginMuted = 0;
64 69
65 // Number of audio and analog channels, globally accessible 70 // 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 71 BeagleRTContext gContext;
67 // by the AnalogRead() and AnalogWrite() and the digital macros without creating 72
68 // extra confusion in their use cases by passing this argument 73 // User data passed in from main()
69 int gNumAudioChannels = 0; 74 void *gUserData;
70 int gNumAnalogChannels = 0;
71 int gNumDigitalChannels = 0;
72 75
73 // initAudio() prepares the infrastructure for running PRU-based real-time 76 // initAudio() prepares the infrastructure for running PRU-based real-time
74 // audio, but does not actually start the calculations. 77 // audio, but does not actually start the calculations.
75 // periodSize indicates the number of _sensor_ frames per period: the audio period size 78 // periodSize indicates the number of _sensor_ frames per period: the audio period size
76 // is twice this value. In total, the audio latency in frames will be 4*periodSize, 79 // is twice this value. In total, the audio latency in frames will be 4*periodSize,
80 // userData is an opaque pointer which will be passed through to the initialise_render() 83 // userData is an opaque pointer which will be passed through to the initialise_render()
81 // function for application-specific use 84 // function for application-specific use
82 // 85 //
83 // Returns 0 on success. 86 // Returns 0 on success.
84 87
85 88 int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData)
86 int BeagleRT_initAudio(RTAudioSettings *settings, void *userData)
87 { 89 {
88 rt_print_auto_init(1); 90 rt_print_auto_init(1);
89 setVerboseLevel(settings->verbose); 91
90 gPRUFilename=settings->pruFilename; 92 BeagleRT_setVerboseLevel(settings->verbose);
91 if(gRTAudioVerbose == 1) 93 strncpy(gPRUFilename, settings->pruFilename, MAX_PRU_FILENAME_LENGTH);
92 rt_printf("Running with Xenomai\n"); 94 gUserData = userData;
95
96 // Initialise context data structure
97 memset(&gContext, 0, sizeof(BeagleRTContext));
93 98
94 if(gRTAudioVerbose) { 99 if(gRTAudioVerbose) {
95 cout << "Starting with period size " << settings->periodSize << "; "; 100 cout << "Starting with period size " << settings->periodSize << "; ";
96 if(settings->useAnalog) 101 if(settings->useAnalog)
97 cout << "analog enabled\n"; 102 cout << "analog enabled\n";
140 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) { 145 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) {
141 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; 146 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
142 return 1; 147 return 1;
143 } 148 }
144 149
150 // Initialise the rendering environment: sample rates, frame counts, numbers of channels
151 gContext.audioSampleRate = 44100.0;
152 gContext.audioChannels = 2;
153
154 if(settings->useAnalog) {
155 gContext.audioFrames = settings->periodSize * settings->numAnalogChannels / 4;
156
157 gContext.analogFrames = settings->periodSize;
158 gContext.analogChannels = settings->numAnalogChannels;
159 gContext.analogSampleRate = gContext.audioSampleRate * 4.0 / (float)settings->numAnalogChannels;
160 }
161 else {
162 gContext.audioFrames = settings->periodSize * 2;
163
164 gContext.analogFrames = 0;
165 gContext.analogChannels = 0;
166 gContext.analogSampleRate = 0;
167 }
168
169 // For now, digital frame rate is equal to audio frame rate
170 if(settings->useDigital) {
171 gContext.digitalFrames = gContext.audioFrames;
172 gContext.digitalSampleRate = gContext.audioSampleRate;
173 gContext.digitalChannels = settings->numDigitalChannels;
174 }
175 else {
176 gContext.digitalFrames = 0;
177 gContext.digitalSampleRate = 0;
178 gContext.digitalChannels = 0;
179 }
180
181 // Set flags based on init settings
182 if(settings->interleave)
183 gContext.flags |= BEAGLERT_FLAG_INTERLEAVED;
184 if(settings->analogOutputsPersist)
185 gContext.flags |= BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST;
186
145 // Use PRU for audio 187 // Use PRU for audio
146 gPRU = new PRU(); 188 gPRU = new PRU(&gContext);
147 gAudioCodec = new I2c_Codec(); 189 gAudioCodec = new I2c_Codec();
148 190
149 gNumDigitalChannels = settings->useDigital ? settings->numDigitalChannels : 0; //this is called here to make sure prepareGPIO initializes the appropriate GPIO pins 191 // Initialise the GPIO pins, including possibly the digital pins in the render routines
150 if(gPRU->prepareGPIO(settings->useAnalog, settings->useDigital, 1, 1)) { 192 if(gPRU->prepareGPIO(1, 1)) {
151 cout << "Error: unable to prepare GPIO for PRU audio\n"; 193 cout << "Error: unable to prepare GPIO for PRU audio\n";
152 return 1; 194 return 1;
153 } 195 }
196
197 // Get the PRU memory buffers ready to go
154 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) { 198 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) {
155 cout << "Error: unable to initialise PRU\n"; 199 cout << "Error: unable to initialise PRU\n";
156 return 1; 200 return 1;
157 } 201 }
202
203 // Prepare the audio codec, which clocks the whole system
158 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) { 204 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) {
159 cout << "Unable to open codec I2C\n"; 205 cout << "Unable to open codec I2C\n";
160 return 1; 206 return 1;
161 } 207 }
162 if(gAudioCodec->initCodec()) { 208 if(gAudioCodec->initCodec()) {
167 // Set default volume levels 213 // Set default volume levels
168 BeagleRT_setDACLevel(settings->dacLevel); 214 BeagleRT_setDACLevel(settings->dacLevel);
169 BeagleRT_setADCLevel(settings->adcLevel); 215 BeagleRT_setADCLevel(settings->adcLevel);
170 BeagleRT_setHeadphoneLevel(settings->headphoneLevel); 216 BeagleRT_setHeadphoneLevel(settings->headphoneLevel);
171 217
172 // Initialise the rendering environment: pass the number of audio and analog 218 // Call the user-defined initialisation function
173 // channels, the period size for analog and audio, and the sample rates 219 if(!initialise_render(&gContext, userData)) {
174
175 int audioPeriodSize = settings->periodSize * 2;
176 float audioSampleRate = 44100.0;
177 float analogSampleRate = 22050.0;
178 if(settings->useAnalog) {
179 audioPeriodSize = settings->periodSize * settings->numAnalogChannels / 4;
180 analogSampleRate = audioSampleRate * 4.0 / (float)settings->numAnalogChannels;
181 }
182
183 gNumAudioChannels = 2;
184 gNumAnalogChannels = settings->useAnalog ? settings->numAnalogChannels : 0;
185 if(!initialise_render(gNumAnalogChannels, gNumDigitalChannels, gNumAudioChannels,
186 settings->useAnalog ? settings->periodSize : 0, /* analog period size */
187 audioPeriodSize,
188 analogSampleRate, audioSampleRate,
189 userData, settings)) {
190 cout << "Couldn't initialise audio rendering\n"; 220 cout << "Couldn't initialise audio rendering\n";
191 return 1; 221 return 1;
192 } 222 }
193 223
194 return 0; 224 return 0;
225 if(gRTAudioVerbose) 255 if(gRTAudioVerbose)
226 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); 256 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n");
227 } 257 }
228 } 258 }
229 259
230 gPRU->loop(); 260 gPRU->loop(&gRTAudioInterrupt, gUserData);
231 261
232 // Now clean up 262 // Now clean up
233 // gPRU->waitForFinish(); 263 // gPRU->waitForFinish();
234 gPRU->disable(); 264 gPRU->disable();
235 gAudioCodec->stopAudio(); 265 gAudioCodec->stopAudio();
240 if(gRTAudioVerbose == 1) 270 if(gRTAudioVerbose == 1)
241 rt_printf("audio thread ended\n"); 271 rt_printf("audio thread ended\n");
242 } 272 }
243 273
244 // Create a calculation loop which can run independently of the audio, at a different 274 // Create a calculation loop which can run independently of the audio, at a different
245 // (equal or lower) priority. Audio priority is 99; priority should be generally be less than this. 275 // (equal or lower) priority. Audio priority is defined in BEAGLERT_AUDIO_PRIORITY;
276 // priority should be generally be less than this.
246 // Returns an (opaque) pointer to the created task on success; 0 on failure 277 // Returns an (opaque) pointer to the created task on success; 0 on failure
247 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name) 278 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name)
248 { 279 {
249 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); 280 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask));
250 281
302 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 333 // It launches the real-time Xenomai task which runs the audio loop. Returns 0
303 // on success. 334 // on success.
304 335
305 int BeagleRT_startAudio() 336 int BeagleRT_startAudio()
306 { 337 {
307 // Create audio thread with the highest priority 338 // Create audio thread with high Xenomai priority
308 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) { 339 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, BEAGLERT_AUDIO_PRIORITY, T_JOINABLE | T_FPU)) {
309 cout << "Error: unable to create Xenomai audio thread" << endl; 340 cout << "Error: unable to create Xenomai audio thread" << endl;
310 return -1; 341 return -1;
342 }
343
344 // Create an interrupt which the audio thread receives from the PRU
345 int result = 0;
346 if((result = rt_intr_create(&gRTAudioInterrupt, gRTAudioInterruptName, PRU_RTAUDIO_IRQ, I_NOAUTOENA)) != 0) {
347 cout << "Error: unable to create Xenomai interrupt for PRU (error " << result << ")" << endl;
348 return -1;
311 } 349 }
312 350
313 // Start all RT threads 351 // Start all RT threads
314 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { 352 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) {
315 cout << "Error: unable to start Xenomai audio thread" << endl; 353 cout << "Error: unable to start Xenomai audio thread" << endl;
356 } 394 }
357 395
358 // Free any resources associated with PRU real-time audio 396 // Free any resources associated with PRU real-time audio
359 void BeagleRT_cleanupAudio() 397 void BeagleRT_cleanupAudio()
360 { 398 {
361 cleanup_render(); 399 cleanup_render(&gContext, gUserData);
362 400
363 // Clean up the auxiliary tasks 401 // Clean up the auxiliary tasks
364 vector<InternalAuxiliaryTask*>::iterator it; 402 vector<InternalAuxiliaryTask*>::iterator it;
365 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { 403 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
366 InternalAuxiliaryTask *taskStruct = *it; 404 InternalAuxiliaryTask *taskStruct = *it;
367 405
406 // Delete the task
407 rt_task_delete(&taskStruct->task);
408
368 // Free the name string and the struct itself 409 // Free the name string and the struct itself
369 free(taskStruct->name); 410 free(taskStruct->name);
370 free(taskStruct); 411 free(taskStruct);
371 } 412 }
372 gAuxTasks.clear(); 413 gAuxTasks.clear();
414
415 // Delete the audio task and its interrupt
416 rt_intr_delete(&gRTAudioInterrupt);
417 rt_task_delete(&gRTAudioThread);
373 418
374 if(gPRU != 0) 419 if(gPRU != 0)
375 delete gPRU; 420 delete gPRU;
376 if(gAudioCodec != 0) 421 if(gAudioCodec != 0)
377 delete gAudioCodec; 422 delete gAudioCodec;
422 467
423 return gpio_set_value(gAmplifierMutePin, pinValue); 468 return gpio_set_value(gAmplifierMutePin, pinValue);
424 } 469 }
425 470
426 // Set the verbosity level 471 // Set the verbosity level
427 void setVerboseLevel(int level) 472 void BeagleRT_setVerboseLevel(int level)
428 { 473 {
429 gRTAudioVerbose = level; 474 gRTAudioVerbose = level;
430 } 475 }