annotate examples/10-Instruments/d-box/main.cpp @ 481:def3b8cf9749 prerelease

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