Mercurial > hg > beaglert
comparison projects/d-box/main.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 <signal.h> // interrupt handler | |
21 #include <assert.h> | |
22 #include <vector> | |
23 #include <dirent.h> // to handle files in dirs | |
24 #include <mntent.h> // to check if device is mounted | |
25 #include <sys/mount.h> // mount() | |
26 #include <sys/time.h> // elapsed time | |
27 #include <NE10.h> // neon library | |
28 | |
29 // thread priority | |
30 #include <pthread.h> | |
31 #include <sched.h> | |
32 | |
33 // get_opt_long | |
34 #include <getopt.h> | |
35 | |
36 #include "../../include/RTAudio.h" | |
37 #include "config.h" | |
38 #include "sensors.h" | |
39 #include "OscillatorBank.h" | |
40 #include "StatusLED.h" | |
41 #include "logger.h" | |
42 | |
43 using namespace std; | |
44 | |
45 //---------------------------------------- | |
46 // main variables | |
47 //---------------------------------------- | |
48 vector<OscillatorBank*> gOscBanks; | |
49 int gCurrentOscBank = 0; | |
50 int gNextOscBank = 0; | |
51 int oscBnkOversampling = 1; // oscillator bank frame oversampling | |
52 | |
53 const int kStatusLEDPin = 30; // P9-11 controls status LED | |
54 StatusLED gStatusLED; | |
55 | |
56 pthread_t keyboardThread; | |
57 pthread_t logThread; | |
58 | |
59 // general settings | |
60 int gVerbose = 0; // verbose flag | |
61 bool forceKeyboard = true; // activate/deactivate keyboard control | |
62 bool forceSensors = false; // activate/deactivate sensor control | |
63 bool forceLog = true; // activate/deactivate log on boot partition | |
64 bool useSD = true; // activate/deactivate file loading from SD [as opposed to emmc] | |
65 bool useAudioTest = false; // activate/deactivate sensors and test audio only | |
66 | |
67 // audio settings | |
68 unsigned int gPeriodSize = 8; // period size for audio | |
69 char* gPartialFilename = 0; // name of the partials file to load | |
70 bool gAudioIn = false; // stereo audio in status | |
71 | |
72 int touchSensor0Address = 0x0C; // I2C addresses of touch sensors | |
73 int touchSensor1Address = 0x0B; | |
74 bool useNewSensors = false; | |
75 | |
76 char sdPath[256] = "/dev/mmcblk0p2"; // system path of the SD, partition 2 | |
77 char mountPath[256] = "/root/d-box/usersounds"; // mount point of SD partition 2 [where user files are] | |
78 char gUserDirName[256] = "usersounds"; // Directory in which user analysis files can be found [dir of mountPath] | |
79 char gDefaultDirName[256] = "sounds"; // Directory in which built in analysis files can be found | |
80 char *gDirName; | |
81 bool gIsLoading = false; | |
82 int fileCnt = 0; | |
83 std::vector <std::string> files; | |
84 | |
85 char gId = 'f'; // from 0 to 14, hexadecimal [0-d]! f means not set | |
86 char gGroup = '2'; // 0 is no info, 1 info. 2 is not set | |
87 | |
88 // audio in filter | |
89 extern ne10_float32_t *filterState[2]; | |
90 extern ne10_float32_t *filterIn[2]; | |
91 extern ne10_float32_t *filterOut[2]; | |
92 | |
93 struct arg_data | |
94 { | |
95 int argc; | |
96 char **argv; | |
97 }; | |
98 | |
99 arg_data args; | |
100 | |
101 | |
102 int readFiles() | |
103 { | |
104 if(useSD) | |
105 gDirName = gUserDirName; | |
106 else | |
107 gDirName = gDefaultDirName; | |
108 DIR *dir; | |
109 struct dirent *ent; | |
110 | |
111 // From http://stackoverflow.com/questions/612097/how-can-i-get-a-list-of-files-in-a-directory-using-c-or-c | |
112 if ((dir = opendir (gDirName)) != NULL) { | |
113 /* print all the files and directories within directory */ | |
114 while ((ent = readdir (dir)) != NULL) { | |
115 // Ignore dotfiles and . and .. paths | |
116 if(!strncmp(ent->d_name, ".", 1)) | |
117 continue; | |
118 | |
119 //dbox_printf("%s\n", ent->d_name); | |
120 | |
121 // take only .dbx and .txt files | |
122 string name = string(ent->d_name); | |
123 int len = name.length(); | |
124 | |
125 bool dboxFile = false; | |
126 | |
127 if( (name[len-4]=='.') && (name[len-3]=='d') && (name[len-2]=='b') && (name[len-1]=='x') ) | |
128 dboxFile = true; | |
129 if( (name[len-4]=='.') && (name[len-3]=='t') && (name[len-2]=='x') && (name[len-1]=='t') ) | |
130 dboxFile = true; | |
131 | |
132 if(dboxFile) | |
133 { | |
134 fileCnt++; | |
135 //dbox_printf("%s\n", ent->d_name); | |
136 files.push_back( std::string( ent->d_name ) ); | |
137 } | |
138 } | |
139 closedir (dir); | |
140 } else { | |
141 /* could not open directory */ | |
142 printf("Could not open directory %s\n", gDirName); | |
143 return 1; | |
144 } | |
145 | |
146 // order by name | |
147 std::sort( files.begin(), files.end() ); | |
148 | |
149 if(fileCnt==0) | |
150 { | |
151 printf("No .dbx or .txt files in %s!\n", gDirName); | |
152 return 1; | |
153 } | |
154 | |
155 return 0; | |
156 } | |
157 | |
158 /* Load sounds from the directory */ | |
159 void loadAudioFiles(bool loadFirstFile) | |
160 { | |
161 char fullFileName[256]; | |
162 | |
163 if(loadFirstFile) { | |
164 strcpy(fullFileName, gDirName); | |
165 strcat(fullFileName, "/"); | |
166 strncat(fullFileName, files[0].c_str(), 255 - strlen(gDirName)); | |
167 dbox_printf("Loading first file %s...\n", fullFileName); | |
168 OscillatorBank *bank = new OscillatorBank(fullFileName); | |
169 if(bank->initBank(oscBnkOversampling)) { | |
170 bank->setLoopHops(100, bank->getLastHop()); | |
171 gOscBanks.push_back(bank); | |
172 } | |
173 } | |
174 | |
175 else { | |
176 for(int i=1; i<fileCnt; i++){ | |
177 strcpy(fullFileName, gDirName); | |
178 strcat(fullFileName, "/"); | |
179 strncat(fullFileName, files[i].c_str(), 255 - strlen(gDirName)); | |
180 dbox_printf("Loading file %s...\n", fullFileName); | |
181 OscillatorBank *bank = new OscillatorBank(fullFileName); | |
182 if(bank->initBank(oscBnkOversampling)) { | |
183 bank->setLoopHops(100, bank->getLastHop()); | |
184 gOscBanks.push_back(bank); | |
185 } | |
186 } | |
187 } | |
188 } | |
189 | |
190 // adapted from http://program-nix.blogspot.co.uk/2008/08/c-language-check-filesystem-is-mounted.html | |
191 int checkIfMounted (char * dev_path) | |
192 { | |
193 FILE * mtab = NULL; | |
194 struct mntent * part = NULL; | |
195 int is_mounted = 0; | |
196 | |
197 if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL) | |
198 { | |
199 while ( ( part = getmntent ( mtab) ) != NULL) | |
200 { | |
201 if ( ( part->mnt_fsname != NULL ) && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 ) | |
202 is_mounted = 1; | |
203 } | |
204 endmntent(mtab); | |
205 } | |
206 return is_mounted; | |
207 } | |
208 | |
209 int mountSDuserPartition() | |
210 { | |
211 if(checkIfMounted(sdPath)) | |
212 { | |
213 printf("device %s already mounted, fair enough, let's move on\n", sdPath); | |
214 return 0; | |
215 } | |
216 // if mount rootfs from SD [rootfs eMMC not used] or from eMMC via properly formatted SD [SD rootfs used as storage volume] | |
217 // we always use rootfs on SD as storage volume ----> "/dev/mmcblk0p2" | |
218 int ret = mount(sdPath, "/root/d-box/usersounds", "vfat", 0, NULL); | |
219 if (ret!=0) | |
220 { | |
221 printf("Error in mount...%s\n", strerror(ret)); | |
222 return 1; | |
223 } | |
224 return 0; | |
225 } | |
226 | |
227 int initSoundFiles() | |
228 { | |
229 if(gVerbose==1) | |
230 cout << "---------------->Init Audio Thread" << endl; | |
231 | |
232 if(useSD) | |
233 { | |
234 // mount the SD partition where user sounds are located | |
235 // [this is p2, p1 is already mounted and we will log data there] | |
236 if(mountSDuserPartition()!=0) | |
237 return -1; | |
238 } | |
239 | |
240 gIsLoading = true; | |
241 | |
242 // read files from SD and order them alphabetically | |
243 if(readFiles()!=0) | |
244 return 1; | |
245 | |
246 // load first file into oscBank | |
247 loadAudioFiles(true); | |
248 | |
249 return 0; | |
250 } | |
251 | |
252 //--------------------------------------------------------------------------------------------------------- | |
253 | |
254 // Handle Ctrl-C | |
255 void interrupt_handler(int var) | |
256 { | |
257 // kill keyboard thread mercilessly | |
258 if(forceKeyboard) | |
259 pthread_cancel(keyboardThread); | |
260 | |
261 gShouldStop = true; | |
262 } | |
263 | |
264 | |
265 void parseArguments(arg_data args) | |
266 { | |
267 // Default filename; | |
268 gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt"); | |
269 | |
270 // TODO: complete this | |
271 struct option long_option[] = | |
272 { | |
273 {"help", 0, NULL, 'h'}, | |
274 {"period", 1, NULL, 'p'}, | |
275 {"verbose", 1, NULL, 'v'}, | |
276 {"audioin", 1, NULL, 'i'}, | |
277 {"file", 1, NULL, 'f'}, | |
278 {"keyboard", 1, NULL, 'k'}, | |
279 {"audio-test", 0, NULL, 'A'}, | |
280 {"new-sensors", 0, NULL, 'S'}, | |
281 {"sensor0", 1, NULL, 'Q'}, | |
282 {"sensor1", 1, NULL, 'R'}, | |
283 {"log", 1, NULL, 'l'}, | |
284 {"usesd", 1, NULL, 'u'}, | |
285 {"oversamp", 1, NULL, 'o'}, | |
286 {"boxnumber", 1, NULL, 'n'}, | |
287 {"group", 1, NULL, 'g'}, | |
288 {NULL, 0, NULL, 0}, | |
289 }; | |
290 int morehelp = 0; | |
291 int tmp = -1; | |
292 | |
293 while (1) | |
294 { | |
295 int c; | |
296 if ((c = getopt_long(args.argc, args.argv, "hp:vf:ki:sAQ:R:Sl:u:o:n:g:", long_option, NULL)) < 0) | |
297 break; | |
298 switch (c) | |
299 { | |
300 case 'h': | |
301 morehelp++; | |
302 break; | |
303 case 'p': | |
304 gPeriodSize = atoi(optarg); | |
305 break; | |
306 case 'v': | |
307 gVerbose = 1; | |
308 break; | |
309 case 'f': | |
310 free(gPartialFilename); | |
311 gPartialFilename = strdup(optarg); | |
312 break; | |
313 case 'k': | |
314 forceKeyboard = true; | |
315 break; | |
316 case 'i': | |
317 gAudioIn = (atoi(optarg)==0) ? false : true; | |
318 break; | |
319 case 's': | |
320 forceSensors = true; | |
321 break; | |
322 case 'A': | |
323 useAudioTest = true; | |
324 break; | |
325 case 'S': | |
326 useNewSensors = true; | |
327 break; | |
328 case 'Q': | |
329 touchSensor0Address = atoi(optarg); | |
330 break; | |
331 case 'R': | |
332 touchSensor1Address = atoi(optarg); | |
333 break; | |
334 case 'l': | |
335 tmp = atoi(optarg); | |
336 if(tmp==0) | |
337 forceLog = false; | |
338 else if(tmp>0) | |
339 forceLog = true; | |
340 break; | |
341 case 'u': | |
342 tmp = atoi(optarg); | |
343 if(tmp==0) | |
344 useSD = false; | |
345 else if(tmp>0) | |
346 useSD = true; | |
347 break; | |
348 case 'o': | |
349 oscBnkOversampling = atoi(optarg); | |
350 break; | |
351 case 'n': | |
352 gId = *optarg; | |
353 cout << "-set box number to: " << gId << endl; | |
354 break; | |
355 case 'g': | |
356 gGroup = *optarg; | |
357 cout << "-set group to: " << gId << endl; | |
358 break; | |
359 default: | |
360 break; | |
361 } | |
362 } | |
363 } | |
364 | |
365 int main(int argc, char *argv[]) | |
366 { | |
367 RT_TASK rtSensorThread; | |
368 const char rtSensorThreadName[] = "dbox-sensor"; | |
369 int oscBankHopSize; | |
370 | |
371 // Parse command-line arguments | |
372 args.argc = argc; | |
373 args.argv = argv; | |
374 parseArguments(args); | |
375 | |
376 setVerboseLevel(gVerbose); | |
377 if(gVerbose == 1 && useAudioTest) | |
378 cout << "main() : running in audio test mode" << endl; | |
379 | |
380 // Load sound files from directory | |
381 if(initSoundFiles() != 0) | |
382 return -1; | |
383 | |
384 oscBankHopSize = gOscBanks[gCurrentOscBank]->getHopSize()/gOscBanks[gCurrentOscBank]->getMinSpeed(); | |
385 | |
386 // Initialise the audio device | |
387 if(initAudio(gPeriodSize, 1, &oscBankHopSize) != 0) | |
388 return -1; | |
389 | |
390 // Initialise the status LED | |
391 if(!gStatusLED.init(kStatusLEDPin)) { | |
392 if(gVerbose) | |
393 cout << "Couldn't initialise status LED pin\n"; | |
394 } | |
395 | |
396 // Free file name string which is no longer needed | |
397 if(gPartialFilename != 0) | |
398 free(gPartialFilename); | |
399 | |
400 if(!useAudioTest) { | |
401 if(initSensorLoop(touchSensor0Address, touchSensor1Address, useNewSensors) != 0) | |
402 return -1; | |
403 } | |
404 | |
405 if(gVerbose == 1) | |
406 cout << "main() : creating audio thread" << endl; | |
407 | |
408 if(startAudio()) { | |
409 cout << "Error: unable to start real-time audio" << endl; | |
410 return -1; | |
411 } | |
412 | |
413 // LED on... | |
414 gStatusLED.on(); | |
415 | |
416 if(forceSensors && !useAudioTest) { | |
417 if(gVerbose==1) | |
418 cout << "main() : creating control thread" << endl; | |
419 | |
420 if(rt_task_create(&rtSensorThread, rtSensorThreadName, 0, 95, T_JOINABLE | T_FPU)) { | |
421 cout << "Error:unable to create Xenomai control thread" << endl; | |
422 return -1; | |
423 } | |
424 if(rt_task_start(&rtSensorThread, &sensorLoop, 0)) { | |
425 cout << "Error:unable to start Xenomai control thread" << endl; | |
426 return -1; | |
427 } | |
428 } | |
429 | |
430 if(forceKeyboard) { | |
431 if(gVerbose==1) | |
432 cout << "main() : creating keyboard thread" << endl; | |
433 | |
434 if ( pthread_create(&keyboardThread, NULL, keyboardLoop, NULL) ) { | |
435 cout << "Error:unable to create keyboard thread" << endl; | |
436 return -1; | |
437 } | |
438 } | |
439 | |
440 if(forceLog) { | |
441 if(gVerbose==1) | |
442 cout << "main() : creating log thread" << endl; | |
443 | |
444 if(initLogLoop()!=0) { | |
445 cout << "Error:unable to create log thread" << endl; | |
446 return -1; | |
447 } | |
448 | |
449 if ( pthread_create(&logThread, NULL, logLoop, NULL) ) { | |
450 cout << "Error:unable to create keyboard thread" << endl; | |
451 return -1; | |
452 } | |
453 } | |
454 | |
455 // Set up interrupt handler to catch Control-C | |
456 signal(SIGINT, interrupt_handler); | |
457 | |
458 // load all other files into oscBanks | |
459 loadAudioFiles(false); | |
460 cout << "Finished loading analysis files\n"; | |
461 gIsLoading = false; | |
462 | |
463 // Run until told to stop | |
464 while(!gShouldStop) { | |
465 usleep(100000); | |
466 } | |
467 | |
468 stopAudio(); | |
469 | |
470 if(!useAudioTest) | |
471 rt_task_join(&rtSensorThread); | |
472 | |
473 cleanupAudio(); | |
474 | |
475 pthread_join( keyboardThread, NULL); | |
476 pthread_join( logThread, NULL); | |
477 | |
478 for(unsigned int i = 0; i < gOscBanks.size(); i++) | |
479 delete gOscBanks[i]; | |
480 | |
481 NE10_FREE(filterState[0]); | |
482 NE10_FREE(filterState[1]); | |
483 NE10_FREE(filterIn[0]); | |
484 NE10_FREE(filterIn[1]); | |
485 NE10_FREE(filterOut[0]); | |
486 NE10_FREE(filterOut[1]); | |
487 | |
488 printf("Program ended\nBye bye\n"); | |
489 return 0; | |
490 } |