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