annotate examples/d-box/main.cpp @ 379:24c3a0663d54 prerelease

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