annotate projects/d-box/main.cpp @ 39:638bc1ae2500 staging

Improved readibility of the DIGITAL code in the PRU, using register names instead of aliases and expanding some of the macros, removing unused macros. Binaries were not modified
author Giulio Moro <giuliomoro@yahoo.it>
date Wed, 13 May 2015 12:18:10 +0100
parents 901d205d1a3c
children be427da6fb9c
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@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@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@15 278 {"sensor-type", 1, 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@15 296 if ((c = BeagleRT_getopt_long(args.argc, args.argv, "hf:ki:sTQ:R:S:l: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@15 320 sensorType = atoi(optarg);
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@15 399 if(initSensorLoop(touchSensor0Address, touchSensor1Address, sensorType) != 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@15 453 // Set up interrupt handler to catch Control-C and SIGTERM
andrewm@0 454 signal(SIGINT, interrupt_handler);
andrewm@15 455 signal(SIGTERM, interrupt_handler);
andrewm@0 456
andrewm@0 457 // load all other files into oscBanks
andrewm@0 458 loadAudioFiles(false);
andrewm@0 459 cout << "Finished loading analysis files\n";
andrewm@0 460 gIsLoading = false;
andrewm@0 461
andrewm@0 462 // Run until told to stop
andrewm@0 463 while(!gShouldStop) {
andrewm@0 464 usleep(100000);
andrewm@0 465 }
andrewm@0 466
andrewm@5 467 BeagleRT_stopAudio();
andrewm@0 468
andrewm@0 469 if(!useAudioTest)
andrewm@0 470 rt_task_join(&rtSensorThread);
andrewm@0 471
andrewm@5 472 BeagleRT_cleanupAudio();
andrewm@0 473
andrewm@0 474 pthread_join( keyboardThread, NULL);
andrewm@0 475 pthread_join( logThread, NULL);
andrewm@0 476
andrewm@0 477 for(unsigned int i = 0; i < gOscBanks.size(); i++)
andrewm@0 478 delete gOscBanks[i];
andrewm@0 479
andrewm@0 480 NE10_FREE(filterState[0]);
andrewm@0 481 NE10_FREE(filterState[1]);
andrewm@0 482 NE10_FREE(filterIn[0]);
andrewm@0 483 NE10_FREE(filterIn[1]);
andrewm@0 484 NE10_FREE(filterOut[0]);
andrewm@0 485 NE10_FREE(filterOut[1]);
andrewm@0 486
andrewm@0 487 printf("Program ended\nBye bye\n");
andrewm@0 488 return 0;
andrewm@0 489 }