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 }