Mercurial > hg > beaglert
comparison examples/d-box/main.cpp @ 300:dbeed520b014 prerelease
Renamed projects to examples
| author | Giulio Moro <giuliomoro@yahoo.it> |
|---|---|
| date | Fri, 27 May 2016 13:58:20 +0100 |
| parents | projects/d-box/main.cpp@59edd5780fef |
| children | e4392164b458 |
comparison
equal
deleted
inserted
replaced
| 297:a3d83ebdf49b | 300:dbeed520b014 |
|---|---|
| 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 <BeagleRT.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 int sensorType = 0; | |
| 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] = "/root/d-box/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, BeagleRTInitSettings *settings) | |
| 266 { | |
| 267 // Default filename; | |
| 268 gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt"); | |
| 269 | |
| 270 const int kOptionAudioTest = 1000; | |
| 271 | |
| 272 // TODO: complete this | |
| 273 struct option long_option[] = | |
| 274 { | |
| 275 {"help", 0, NULL, 'h'}, | |
| 276 {"audioin", 1, NULL, 'i'}, | |
| 277 {"file", 1, NULL, 'f'}, | |
| 278 {"keyboard", 1, NULL, 'k'}, | |
| 279 {"audio-test", 0, NULL, kOptionAudioTest}, | |
| 280 {"sensor-type", 1, NULL, 't'}, | |
| 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 BeagleRT_defaultSettings(settings); | |
| 294 | |
| 295 while (1) | |
| 296 { | |
| 297 int c; | |
| 298 if ((c = BeagleRT_getopt_long(args.argc, args.argv, "hf:ki:sq:r:t:l:u:o:n:g:", long_option, settings)) < 0) | |
| 299 break; | |
| 300 switch (c) | |
| 301 { | |
| 302 case 'h': | |
| 303 morehelp++; | |
| 304 break; | |
| 305 case 'f': | |
| 306 free(gPartialFilename); | |
| 307 gPartialFilename = strdup(optarg); | |
| 308 break; | |
| 309 case 'k': | |
| 310 forceKeyboard = true; | |
| 311 break; | |
| 312 case 'i': | |
| 313 gAudioIn = (atoi(optarg)==0) ? false : true; | |
| 314 break; | |
| 315 case 's': | |
| 316 forceSensors = true; | |
| 317 break; | |
| 318 case kOptionAudioTest: | |
| 319 useAudioTest = true; | |
| 320 break; | |
| 321 case 't': | |
| 322 sensorType = atoi(optarg); | |
| 323 break; | |
| 324 case 'q': | |
| 325 touchSensor0Address = atoi(optarg); | |
| 326 break; | |
| 327 case 'r': | |
| 328 touchSensor1Address = atoi(optarg); | |
| 329 break; | |
| 330 case 'l': | |
| 331 tmp = atoi(optarg); | |
| 332 if(tmp==0) | |
| 333 forceLog = false; | |
| 334 else if(tmp>0) | |
| 335 forceLog = true; | |
| 336 break; | |
| 337 case 'u': | |
| 338 tmp = atoi(optarg); | |
| 339 if(tmp==0) | |
| 340 useSD = false; | |
| 341 else if(tmp>0) | |
| 342 useSD = true; | |
| 343 break; | |
| 344 case 'o': | |
| 345 oscBnkOversampling = atoi(optarg); | |
| 346 break; | |
| 347 case 'n': | |
| 348 gId = *optarg; | |
| 349 cout << "-set box number to: " << gId << endl; | |
| 350 break; | |
| 351 case 'g': | |
| 352 gGroup = *optarg; | |
| 353 cout << "-set group to: " << gId << endl; | |
| 354 break; | |
| 355 default: | |
| 356 break; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 gPeriodSize = settings->periodSize; | |
| 361 gVerbose = settings->verbose; | |
| 362 } | |
| 363 | |
| 364 int main(int argc, char *argv[]) | |
| 365 { | |
| 366 BeagleRTInitSettings settings; // Standard audio settings | |
| 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, &settings); | |
| 375 | |
| 376 BeagleRT_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(BeagleRT_initAudio(&settings, &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, sensorType) != 0) | |
| 402 return -1; | |
| 403 } | |
| 404 | |
| 405 if(gVerbose == 1) | |
| 406 cout << "main() : creating audio thread" << endl; | |
| 407 | |
| 408 if(BeagleRT_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, BEAGLERT_AUDIO_PRIORITY - 5, 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 and SIGTERM | |
| 456 signal(SIGINT, interrupt_handler); | |
| 457 signal(SIGTERM, interrupt_handler); | |
| 458 | |
| 459 // load all other files into oscBanks | |
| 460 loadAudioFiles(false); | |
| 461 cout << "Finished loading analysis files\n"; | |
| 462 gIsLoading = false; | |
| 463 | |
| 464 // Run until told to stop | |
| 465 while(!gShouldStop) { | |
| 466 usleep(100000); | |
| 467 } | |
| 468 | |
| 469 BeagleRT_stopAudio(); | |
| 470 | |
| 471 if(!useAudioTest) | |
| 472 rt_task_join(&rtSensorThread); | |
| 473 | |
| 474 BeagleRT_cleanupAudio(); | |
| 475 | |
| 476 pthread_join( keyboardThread, NULL); | |
| 477 pthread_join( logThread, NULL); | |
| 478 | |
| 479 for(unsigned int i = 0; i < gOscBanks.size(); i++) | |
| 480 delete gOscBanks[i]; | |
| 481 | |
| 482 NE10_FREE(filterState[0]); | |
| 483 NE10_FREE(filterState[1]); | |
| 484 NE10_FREE(filterIn[0]); | |
| 485 NE10_FREE(filterIn[1]); | |
| 486 NE10_FREE(filterOut[0]); | |
| 487 NE10_FREE(filterOut[1]); | |
| 488 | |
| 489 printf("Program ended\nBye bye\n"); | |
| 490 return 0; | |
| 491 } |
