annotate projects/d-box/main.cpp @ 5:09f03ac40fcc

API improvements and cleanups. Now all common audio command-line options can be parsed automatically.
author andrewm
date Sat, 08 Nov 2014 16:16:55 +0100
parents 8a575ba3ab52
children 901d205d1a3c
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@0 27 #include <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
andrewm@0 36 #include "../../include/RTAudio.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@0 74 bool useNewSensors = false;
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@0 79 char gDefaultDirName[256] = "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
andrewm@5 265 void parseArguments(arg_data args, RTAudioSettings *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@0 270 // TODO: complete this
andrewm@0 271 struct option long_option[] =
andrewm@0 272 {
andrewm@0 273 {"help", 0, NULL, 'h'},
andrewm@0 274 {"audioin", 1, NULL, 'i'},
andrewm@0 275 {"file", 1, NULL, 'f'},
andrewm@0 276 {"keyboard", 1, NULL, 'k'},
andrewm@5 277 {"audio-test", 0, NULL, 'T'},
andrewm@0 278 {"new-sensors", 0, NULL, 'S'},
andrewm@0 279 {"sensor0", 1, NULL, 'Q'},
andrewm@0 280 {"sensor1", 1, NULL, 'R'},
andrewm@0 281 {"log", 1, NULL, 'l'},
andrewm@0 282 {"usesd", 1, NULL, 'u'},
andrewm@0 283 {"oversamp", 1, NULL, 'o'},
andrewm@0 284 {"boxnumber", 1, NULL, 'n'},
andrewm@0 285 {"group", 1, NULL, 'g'},
andrewm@0 286 {NULL, 0, NULL, 0},
andrewm@0 287 };
andrewm@0 288 int morehelp = 0;
andrewm@0 289 int tmp = -1;
andrewm@0 290
andrewm@5 291 BeagleRT_defaultSettings(settings);
andrewm@5 292
andrewm@0 293 while (1)
andrewm@0 294 {
andrewm@0 295 int c;
andrewm@5 296 if ((c = BeagleRT_getopt_long(args.argc, args.argv, "hf:ki:sTQ:R:Sl:u:o:n:g:", long_option, settings)) < 0)
andrewm@0 297 break;
andrewm@0 298 switch (c)
andrewm@0 299 {
andrewm@0 300 case 'h':
andrewm@0 301 morehelp++;
andrewm@0 302 break;
andrewm@0 303 case 'f':
andrewm@0 304 free(gPartialFilename);
andrewm@0 305 gPartialFilename = strdup(optarg);
andrewm@0 306 break;
andrewm@0 307 case 'k':
andrewm@0 308 forceKeyboard = true;
andrewm@0 309 break;
andrewm@0 310 case 'i':
andrewm@0 311 gAudioIn = (atoi(optarg)==0) ? false : true;
andrewm@0 312 break;
andrewm@0 313 case 's':
andrewm@0 314 forceSensors = true;
andrewm@0 315 break;
andrewm@5 316 case 'T':
andrewm@0 317 useAudioTest = true;
andrewm@0 318 break;
andrewm@0 319 case 'S':
andrewm@0 320 useNewSensors = true;
andrewm@0 321 break;
andrewm@0 322 case 'Q':
andrewm@0 323 touchSensor0Address = atoi(optarg);
andrewm@0 324 break;
andrewm@0 325 case 'R':
andrewm@0 326 touchSensor1Address = atoi(optarg);
andrewm@0 327 break;
andrewm@0 328 case 'l':
andrewm@0 329 tmp = atoi(optarg);
andrewm@0 330 if(tmp==0)
andrewm@0 331 forceLog = false;
andrewm@0 332 else if(tmp>0)
andrewm@0 333 forceLog = true;
andrewm@0 334 break;
andrewm@0 335 case 'u':
andrewm@0 336 tmp = atoi(optarg);
andrewm@0 337 if(tmp==0)
andrewm@0 338 useSD = false;
andrewm@0 339 else if(tmp>0)
andrewm@0 340 useSD = true;
andrewm@0 341 break;
andrewm@0 342 case 'o':
andrewm@0 343 oscBnkOversampling = atoi(optarg);
andrewm@0 344 break;
andrewm@0 345 case 'n':
andrewm@0 346 gId = *optarg;
andrewm@0 347 cout << "-set box number to: " << gId << endl;
andrewm@0 348 break;
andrewm@0 349 case 'g':
andrewm@0 350 gGroup = *optarg;
andrewm@0 351 cout << "-set group to: " << gId << endl;
andrewm@0 352 break;
andrewm@0 353 default:
andrewm@0 354 break;
andrewm@0 355 }
andrewm@0 356 }
andrewm@5 357
andrewm@5 358 gPeriodSize = settings->periodSize;
andrewm@5 359 gVerbose = settings->verbose;
andrewm@0 360 }
andrewm@0 361
andrewm@0 362 int main(int argc, char *argv[])
andrewm@0 363 {
andrewm@5 364 RTAudioSettings settings; // Standard audio settings
andrewm@0 365 RT_TASK rtSensorThread;
andrewm@0 366 const char rtSensorThreadName[] = "dbox-sensor";
andrewm@0 367 int oscBankHopSize;
andrewm@0 368
andrewm@0 369 // Parse command-line arguments
andrewm@0 370 args.argc = argc;
andrewm@0 371 args.argv = argv;
andrewm@5 372 parseArguments(args, &settings);
andrewm@0 373
andrewm@0 374 setVerboseLevel(gVerbose);
andrewm@0 375 if(gVerbose == 1 && useAudioTest)
andrewm@0 376 cout << "main() : running in audio test mode" << endl;
andrewm@0 377
andrewm@0 378 // Load sound files from directory
andrewm@0 379 if(initSoundFiles() != 0)
andrewm@0 380 return -1;
andrewm@0 381
andrewm@0 382 oscBankHopSize = gOscBanks[gCurrentOscBank]->getHopSize()/gOscBanks[gCurrentOscBank]->getMinSpeed();
andrewm@0 383
andrewm@0 384 // Initialise the audio device
andrewm@5 385 if(BeagleRT_initAudio(&settings, &oscBankHopSize) != 0)
andrewm@0 386 return -1;
andrewm@0 387
andrewm@0 388 // Initialise the status LED
andrewm@0 389 if(!gStatusLED.init(kStatusLEDPin)) {
andrewm@0 390 if(gVerbose)
andrewm@0 391 cout << "Couldn't initialise status LED pin\n";
andrewm@0 392 }
andrewm@0 393
andrewm@0 394 // Free file name string which is no longer needed
andrewm@0 395 if(gPartialFilename != 0)
andrewm@0 396 free(gPartialFilename);
andrewm@0 397
andrewm@0 398 if(!useAudioTest) {
andrewm@0 399 if(initSensorLoop(touchSensor0Address, touchSensor1Address, useNewSensors) != 0)
andrewm@0 400 return -1;
andrewm@0 401 }
andrewm@0 402
andrewm@0 403 if(gVerbose == 1)
andrewm@0 404 cout << "main() : creating audio thread" << endl;
andrewm@0 405
andrewm@5 406 if(BeagleRT_startAudio()) {
andrewm@0 407 cout << "Error: unable to start real-time audio" << endl;
andrewm@0 408 return -1;
andrewm@0 409 }
andrewm@0 410
andrewm@0 411 // LED on...
andrewm@0 412 gStatusLED.on();
andrewm@0 413
andrewm@0 414 if(forceSensors && !useAudioTest) {
andrewm@0 415 if(gVerbose==1)
andrewm@0 416 cout << "main() : creating control thread" << endl;
andrewm@0 417
andrewm@0 418 if(rt_task_create(&rtSensorThread, rtSensorThreadName, 0, 95, T_JOINABLE | T_FPU)) {
andrewm@0 419 cout << "Error:unable to create Xenomai control thread" << endl;
andrewm@0 420 return -1;
andrewm@0 421 }
andrewm@0 422 if(rt_task_start(&rtSensorThread, &sensorLoop, 0)) {
andrewm@0 423 cout << "Error:unable to start Xenomai control thread" << endl;
andrewm@0 424 return -1;
andrewm@0 425 }
andrewm@0 426 }
andrewm@0 427
andrewm@0 428 if(forceKeyboard) {
andrewm@0 429 if(gVerbose==1)
andrewm@0 430 cout << "main() : creating keyboard thread" << endl;
andrewm@0 431
andrewm@0 432 if ( pthread_create(&keyboardThread, NULL, keyboardLoop, NULL) ) {
andrewm@0 433 cout << "Error:unable to create keyboard thread" << endl;
andrewm@0 434 return -1;
andrewm@0 435 }
andrewm@0 436 }
andrewm@0 437
andrewm@0 438 if(forceLog) {
andrewm@0 439 if(gVerbose==1)
andrewm@0 440 cout << "main() : creating log thread" << endl;
andrewm@0 441
andrewm@0 442 if(initLogLoop()!=0) {
andrewm@0 443 cout << "Error:unable to create log thread" << endl;
andrewm@0 444 return -1;
andrewm@0 445 }
andrewm@0 446
andrewm@0 447 if ( pthread_create(&logThread, NULL, logLoop, NULL) ) {
andrewm@0 448 cout << "Error:unable to create keyboard thread" << endl;
andrewm@0 449 return -1;
andrewm@0 450 }
andrewm@0 451 }
andrewm@0 452
andrewm@0 453 // Set up interrupt handler to catch Control-C
andrewm@0 454 signal(SIGINT, interrupt_handler);
andrewm@0 455
andrewm@0 456 // load all other files into oscBanks
andrewm@0 457 loadAudioFiles(false);
andrewm@0 458 cout << "Finished loading analysis files\n";
andrewm@0 459 gIsLoading = false;
andrewm@0 460
andrewm@0 461 // Run until told to stop
andrewm@0 462 while(!gShouldStop) {
andrewm@0 463 usleep(100000);
andrewm@0 464 }
andrewm@0 465
andrewm@5 466 BeagleRT_stopAudio();
andrewm@0 467
andrewm@0 468 if(!useAudioTest)
andrewm@0 469 rt_task_join(&rtSensorThread);
andrewm@0 470
andrewm@5 471 BeagleRT_cleanupAudio();
andrewm@0 472
andrewm@0 473 pthread_join( keyboardThread, NULL);
andrewm@0 474 pthread_join( logThread, NULL);
andrewm@0 475
andrewm@0 476 for(unsigned int i = 0; i < gOscBanks.size(); i++)
andrewm@0 477 delete gOscBanks[i];
andrewm@0 478
andrewm@0 479 NE10_FREE(filterState[0]);
andrewm@0 480 NE10_FREE(filterState[1]);
andrewm@0 481 NE10_FREE(filterIn[0]);
andrewm@0 482 NE10_FREE(filterIn[1]);
andrewm@0 483 NE10_FREE(filterOut[0]);
andrewm@0 484 NE10_FREE(filterOut[1]);
andrewm@0 485
andrewm@0 486 printf("Program ended\nBye bye\n");
andrewm@0 487 return 0;
andrewm@0 488 }