Mercurial > hg > beaglert
comparison core/RTAudio.cpp @ 0:8a575ba3ab52
Initial commit.
author | andrewm |
---|---|
date | Fri, 31 Oct 2014 19:10:17 +0100 |
parents | |
children | 09f03ac40fcc |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:8a575ba3ab52 |
---|---|
1 /* | |
2 * RTAudio.cpp | |
3 * | |
4 * Central control code for hard real-time audio on BeagleBone Black | |
5 * using PRU and Xenomai Linux extensions. This code began as part | |
6 * of the Hackable Instruments project (EPSRC) at Queen Mary University | |
7 * of London, 2013-14. | |
8 * | |
9 * (c) 2014 Victor Zappi and Andrew McPherson | |
10 * Queen Mary University of London | |
11 */ | |
12 | |
13 | |
14 #include <stdio.h> | |
15 #include <stdlib.h> | |
16 #include <string.h> | |
17 #include <strings.h> | |
18 #include <math.h> | |
19 #include <iostream> | |
20 #include <assert.h> | |
21 #include <vector> | |
22 | |
23 // Xenomai-specific includes | |
24 #include <sys/mman.h> | |
25 #include <native/task.h> | |
26 #include <native/timer.h> | |
27 #include <rtdk.h> | |
28 | |
29 #include "../include/RTAudio.h" | |
30 #include "../include/PRU.h" | |
31 #include "../include/I2c_Codec.h" | |
32 #include "../include/render.h" | |
33 #include "../include/GPIOcontrol.h" | |
34 | |
35 using namespace std; | |
36 | |
37 // Data structure to keep track of auxiliary tasks we | |
38 // can schedule | |
39 typedef struct { | |
40 RT_TASK task; | |
41 void (*function)(void); | |
42 char *name; | |
43 int priority; | |
44 } InternalAuxiliaryTask; | |
45 | |
46 const char gRTAudioThreadName[] = "beaglert-audio"; | |
47 const char gRTCalculationThreadNameMedium[] = "dbox-calculation-medium"; | |
48 const char gRTCalculationThreadNameLow[] = "dbox-calculation-low"; | |
49 | |
50 // Real-time tasks and objects | |
51 RT_TASK gRTAudioThread; | |
52 PRU *gPRU = 0; | |
53 I2c_Codec *gAudioCodec = 0; | |
54 | |
55 vector<InternalAuxiliaryTask*> gAuxTasks; | |
56 | |
57 // Flag which tells the audio task to stop | |
58 bool gShouldStop = false; | |
59 | |
60 // general settings | |
61 int gRTAudioVerbose = 0; // Verbosity level for debugging | |
62 char gPRUFilename[256] = "pru_rtaudio.bin"; // path to PRU binary file | |
63 int gAmplifierMutePin = -1; | |
64 | |
65 | |
66 // initAudio() prepares the infrastructure for running PRU-based real-time | |
67 // audio, but does not actually start the calculations. | |
68 // periodSize indicates the number of _sensor_ frames per period: the audio period size | |
69 // is twice this value. In total, the audio latency in frames will be 4*periodSize, | |
70 // plus any latency inherent in the ADCs and DACs themselves. | |
71 // useMatrix indicates whether to use the ADC and DAC or just the audio codec. | |
72 // userData is an opaque pointer which will be passed through to the initialise_render() | |
73 // function for application-specific use | |
74 // | |
75 // Returns 0 on success. | |
76 | |
77 int initAudio(int periodSize, int useMatrix, | |
78 void *userData, | |
79 int codecI2CAddress, int ampMutePin) | |
80 { | |
81 rt_print_auto_init(1); | |
82 if(gRTAudioVerbose == 1) | |
83 rt_printf("Running with Xenomai\n"); | |
84 | |
85 if(gRTAudioVerbose == 1) | |
86 cout << "---------------->Init Audio Thread" << endl; | |
87 | |
88 // Prepare GPIO pins for amplifier mute and status LED | |
89 if(ampMutePin >= 0) { | |
90 gAmplifierMutePin = ampMutePin; | |
91 | |
92 if(gpio_export(ampMutePin)) { | |
93 if(gRTAudioVerbose) | |
94 cout << "Warning: couldn't export amplifier mute pin\n"; | |
95 } | |
96 if(gpio_set_dir(ampMutePin, OUTPUT_PIN)) { | |
97 if(gRTAudioVerbose) | |
98 cout << "Couldn't set direction on amplifier mute pin\n"; | |
99 return -1; | |
100 } | |
101 if(gpio_set_value(ampMutePin, LOW)) { | |
102 if(gRTAudioVerbose) | |
103 cout << "Couldn't set value on amplifier mute pin\n"; | |
104 return -1; | |
105 } | |
106 } | |
107 | |
108 // Use PRU for audio | |
109 gPRU = new PRU(); | |
110 gAudioCodec = new I2c_Codec(); | |
111 | |
112 if(gPRU->prepareGPIO(useMatrix, 1, 1)) { | |
113 cout << "Error: unable to prepare GPIO for PRU audio\n"; | |
114 return 1; | |
115 } | |
116 if(gPRU->initialise(0, periodSize, true)) { | |
117 cout << "Error: unable to initialise PRU\n"; | |
118 return 1; | |
119 } | |
120 if(gAudioCodec->initI2C_RW(2, codecI2CAddress, -1)) { | |
121 cout << "Unable to open codec I2C\n"; | |
122 return 1; | |
123 } | |
124 if(gAudioCodec->initCodec()) { | |
125 cout << "Error: unable to initialise audio codec\n"; | |
126 return 1; | |
127 } | |
128 gAudioCodec->setDACVolume(0); // Set the DAC volume to full-scale | |
129 gAudioCodec->setHPVolume(-12); // Headphones 6dB down | |
130 gAudioCodec->setADCVolume(-12); // Set the ADC volume to 6dB down | |
131 | |
132 if(!initialise_render(2, useMatrix ? periodSize : 0, periodSize * 2, 22050.0, 44100.0, userData)) { | |
133 cout << "Couldn't initialise audio rendering\n"; | |
134 return 1; | |
135 } | |
136 | |
137 return 0; | |
138 } | |
139 | |
140 // audioLoop() is the main function which starts the PRU audio code | |
141 // and then transfers control to the PRU object. The PRU object in | |
142 // turn will call the audio render() callback function every time | |
143 // there is new data to process. | |
144 | |
145 void audioLoop(void *) | |
146 { | |
147 if(gRTAudioVerbose==1) | |
148 rt_printf("_________________Audio Thread!\n"); | |
149 | |
150 // PRU audio | |
151 assert(gAudioCodec != 0 && gPRU != 0); | |
152 | |
153 if(gAudioCodec->startAudio(0)) { | |
154 rt_printf("Error: unable to start I2C audio codec\n"); | |
155 gShouldStop = 1; | |
156 } | |
157 else { | |
158 if(gPRU->start(gPRUFilename)) { | |
159 rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename); | |
160 gShouldStop = 1; | |
161 } | |
162 else { | |
163 // All systems go. Run the loop; it will end when gShouldStop is set to 1 | |
164 // First unmute the amplifier | |
165 if(gpio_set_value(gAmplifierMutePin, HIGH)) { | |
166 if(gRTAudioVerbose) | |
167 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n"); | |
168 } | |
169 | |
170 gPRU->loop(); | |
171 | |
172 // Now clean up | |
173 // gPRU->waitForFinish(); | |
174 gPRU->disable(); | |
175 gAudioCodec->stopAudio(); | |
176 gPRU->cleanupGPIO(); | |
177 } | |
178 } | |
179 | |
180 if(gRTAudioVerbose == 1) | |
181 rt_printf("audio thread ended\n"); | |
182 } | |
183 | |
184 // Create a calculation loop which can run independently of the audio, at a different | |
185 // (equal or lower) priority. Audio priority is 99; priority should be generally be less than this. | |
186 // Returns an (opaque) pointer to the created task on success; 0 on failure | |
187 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name) | |
188 { | |
189 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask)); | |
190 | |
191 // Attempt to create the task | |
192 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) { | |
193 cout << "Error: unable to create auxiliary task " << name << endl; | |
194 free(newTask); | |
195 return 0; | |
196 } | |
197 | |
198 // Populate the rest of the data structure and store it in the vector | |
199 newTask->function = functionToCall; | |
200 newTask->name = strdup(name); | |
201 newTask->priority = priority; | |
202 | |
203 gAuxTasks.push_back(newTask); | |
204 | |
205 return (AuxiliaryTask)newTask; | |
206 } | |
207 | |
208 // Schedule a previously created auxiliary task. It will run when the priority rules next | |
209 // allow it to be scheduled. | |
210 void scheduleAuxiliaryTask(AuxiliaryTask task) | |
211 { | |
212 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task; | |
213 | |
214 rt_task_resume(&taskToSchedule->task); | |
215 } | |
216 | |
217 // Calculation loop that can be used for other tasks running at a lower | |
218 // priority than the audio thread. Simple wrapper for Xenomai calls. | |
219 // Treat the argument as containing the task structure | |
220 void auxiliaryTaskLoop(void *taskStruct) | |
221 { | |
222 // Get function to call from the argument | |
223 void (*auxiliary_function)(void) = ((InternalAuxiliaryTask *)taskStruct)->function; | |
224 const char *name = ((InternalAuxiliaryTask *)taskStruct)->name; | |
225 | |
226 // Wait for a notification | |
227 rt_task_suspend(NULL); | |
228 | |
229 while(!gShouldStop) { | |
230 // Then run the calculations | |
231 auxiliary_function(); | |
232 | |
233 // Wait for a notification | |
234 rt_task_suspend(NULL); | |
235 } | |
236 | |
237 if(gRTAudioVerbose == 1) | |
238 rt_printf("auxiliary task %s ended\n", name); | |
239 } | |
240 | |
241 // startAudio() should be called only after initAudio() successfully completes. | |
242 // It launches the real-time Xenomai task which runs the audio loop. Returns 0 | |
243 // on success. | |
244 | |
245 int startAudio() | |
246 { | |
247 // Create audio thread with the highest priority | |
248 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) { | |
249 cout << "Error: unable to create Xenomai audio thread" << endl; | |
250 return -1; | |
251 } | |
252 | |
253 // Start all RT threads | |
254 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) { | |
255 cout << "Error: unable to start Xenomai audio thread" << endl; | |
256 return -1; | |
257 } | |
258 | |
259 // The user may have created other tasks. Start those also. | |
260 vector<InternalAuxiliaryTask*>::iterator it; | |
261 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { | |
262 InternalAuxiliaryTask *taskStruct = *it; | |
263 | |
264 if(rt_task_start(&(taskStruct->task), &auxiliaryTaskLoop, taskStruct)) { | |
265 cerr << "Error: unable to start Xenomai task " << taskStruct->name << endl; | |
266 return -1; | |
267 } | |
268 } | |
269 | |
270 return 0; | |
271 } | |
272 | |
273 // Stop the PRU-based audio from running and wait | |
274 // for the tasks to complete before returning. | |
275 | |
276 void stopAudio() | |
277 { | |
278 // Tell audio thread to stop (if this hasn't been done already) | |
279 gShouldStop = true; | |
280 | |
281 // Now wait for threads to respond and actually stop... | |
282 rt_task_join(&gRTAudioThread); | |
283 | |
284 // Stop all the auxiliary threads too | |
285 vector<InternalAuxiliaryTask*>::iterator it; | |
286 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { | |
287 InternalAuxiliaryTask *taskStruct = *it; | |
288 | |
289 // Wake up each thread and join it | |
290 rt_task_resume(&(taskStruct->task)); | |
291 rt_task_join(&(taskStruct->task)); | |
292 } | |
293 } | |
294 | |
295 // Free any resources associated with PRU real-time audio | |
296 void cleanupAudio() | |
297 { | |
298 cleanup_render(); | |
299 | |
300 // Clean up the auxiliary tasks | |
301 vector<InternalAuxiliaryTask*>::iterator it; | |
302 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) { | |
303 InternalAuxiliaryTask *taskStruct = *it; | |
304 | |
305 // Free the name string and the struct itself | |
306 free(taskStruct->name); | |
307 free(taskStruct); | |
308 } | |
309 gAuxTasks.clear(); | |
310 | |
311 if(gPRU != 0) | |
312 delete gPRU; | |
313 if(gAudioCodec != 0) | |
314 delete gAudioCodec; | |
315 | |
316 if(gAmplifierMutePin >= 0) | |
317 gpio_unexport(gAmplifierMutePin); | |
318 gAmplifierMutePin = -1; | |
319 } | |
320 | |
321 // Set the verbosity level | |
322 void setVerboseLevel(int level) | |
323 { | |
324 gRTAudioVerbose = level; | |
325 } |