Mercurial > hg > beaglert
view projects/d-box/main.cpp @ 0:8a575ba3ab52
Initial commit.
author | andrewm |
---|---|
date | Fri, 31 Oct 2014 19:10:17 +0100 |
parents | |
children | 09f03ac40fcc |
line wrap: on
line source
/* * RTAudio.cpp * * Central control code for hard real-time audio on BeagleBone Black * using PRU and Xenomai Linux extensions. This code began as part * of the Hackable Instruments project (EPSRC) at Queen Mary University * of London, 2013-14. * * (c) 2014 Victor Zappi and Andrew McPherson * Queen Mary University of London */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <math.h> #include <iostream> #include <signal.h> // interrupt handler #include <assert.h> #include <vector> #include <dirent.h> // to handle files in dirs #include <mntent.h> // to check if device is mounted #include <sys/mount.h> // mount() #include <sys/time.h> // elapsed time #include <NE10.h> // neon library // thread priority #include <pthread.h> #include <sched.h> // get_opt_long #include <getopt.h> #include "../../include/RTAudio.h" #include "config.h" #include "sensors.h" #include "OscillatorBank.h" #include "StatusLED.h" #include "logger.h" using namespace std; //---------------------------------------- // main variables //---------------------------------------- vector<OscillatorBank*> gOscBanks; int gCurrentOscBank = 0; int gNextOscBank = 0; int oscBnkOversampling = 1; // oscillator bank frame oversampling const int kStatusLEDPin = 30; // P9-11 controls status LED StatusLED gStatusLED; pthread_t keyboardThread; pthread_t logThread; // general settings int gVerbose = 0; // verbose flag bool forceKeyboard = true; // activate/deactivate keyboard control bool forceSensors = false; // activate/deactivate sensor control bool forceLog = true; // activate/deactivate log on boot partition bool useSD = true; // activate/deactivate file loading from SD [as opposed to emmc] bool useAudioTest = false; // activate/deactivate sensors and test audio only // audio settings unsigned int gPeriodSize = 8; // period size for audio char* gPartialFilename = 0; // name of the partials file to load bool gAudioIn = false; // stereo audio in status int touchSensor0Address = 0x0C; // I2C addresses of touch sensors int touchSensor1Address = 0x0B; bool useNewSensors = false; char sdPath[256] = "/dev/mmcblk0p2"; // system path of the SD, partition 2 char mountPath[256] = "/root/d-box/usersounds"; // mount point of SD partition 2 [where user files are] char gUserDirName[256] = "usersounds"; // Directory in which user analysis files can be found [dir of mountPath] char gDefaultDirName[256] = "sounds"; // Directory in which built in analysis files can be found char *gDirName; bool gIsLoading = false; int fileCnt = 0; std::vector <std::string> files; char gId = 'f'; // from 0 to 14, hexadecimal [0-d]! f means not set char gGroup = '2'; // 0 is no info, 1 info. 2 is not set // audio in filter extern ne10_float32_t *filterState[2]; extern ne10_float32_t *filterIn[2]; extern ne10_float32_t *filterOut[2]; struct arg_data { int argc; char **argv; }; arg_data args; int readFiles() { if(useSD) gDirName = gUserDirName; else gDirName = gDefaultDirName; DIR *dir; struct dirent *ent; // From http://stackoverflow.com/questions/612097/how-can-i-get-a-list-of-files-in-a-directory-using-c-or-c if ((dir = opendir (gDirName)) != NULL) { /* print all the files and directories within directory */ while ((ent = readdir (dir)) != NULL) { // Ignore dotfiles and . and .. paths if(!strncmp(ent->d_name, ".", 1)) continue; //dbox_printf("%s\n", ent->d_name); // take only .dbx and .txt files string name = string(ent->d_name); int len = name.length(); bool dboxFile = false; if( (name[len-4]=='.') && (name[len-3]=='d') && (name[len-2]=='b') && (name[len-1]=='x') ) dboxFile = true; if( (name[len-4]=='.') && (name[len-3]=='t') && (name[len-2]=='x') && (name[len-1]=='t') ) dboxFile = true; if(dboxFile) { fileCnt++; //dbox_printf("%s\n", ent->d_name); files.push_back( std::string( ent->d_name ) ); } } closedir (dir); } else { /* could not open directory */ printf("Could not open directory %s\n", gDirName); return 1; } // order by name std::sort( files.begin(), files.end() ); if(fileCnt==0) { printf("No .dbx or .txt files in %s!\n", gDirName); return 1; } return 0; } /* Load sounds from the directory */ void loadAudioFiles(bool loadFirstFile) { char fullFileName[256]; if(loadFirstFile) { strcpy(fullFileName, gDirName); strcat(fullFileName, "/"); strncat(fullFileName, files[0].c_str(), 255 - strlen(gDirName)); dbox_printf("Loading first file %s...\n", fullFileName); OscillatorBank *bank = new OscillatorBank(fullFileName); if(bank->initBank(oscBnkOversampling)) { bank->setLoopHops(100, bank->getLastHop()); gOscBanks.push_back(bank); } } else { for(int i=1; i<fileCnt; i++){ strcpy(fullFileName, gDirName); strcat(fullFileName, "/"); strncat(fullFileName, files[i].c_str(), 255 - strlen(gDirName)); dbox_printf("Loading file %s...\n", fullFileName); OscillatorBank *bank = new OscillatorBank(fullFileName); if(bank->initBank(oscBnkOversampling)) { bank->setLoopHops(100, bank->getLastHop()); gOscBanks.push_back(bank); } } } } // adapted from http://program-nix.blogspot.co.uk/2008/08/c-language-check-filesystem-is-mounted.html int checkIfMounted (char * dev_path) { FILE * mtab = NULL; struct mntent * part = NULL; int is_mounted = 0; if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL) { while ( ( part = getmntent ( mtab) ) != NULL) { if ( ( part->mnt_fsname != NULL ) && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 ) is_mounted = 1; } endmntent(mtab); } return is_mounted; } int mountSDuserPartition() { if(checkIfMounted(sdPath)) { printf("device %s already mounted, fair enough, let's move on\n", sdPath); return 0; } // if mount rootfs from SD [rootfs eMMC not used] or from eMMC via properly formatted SD [SD rootfs used as storage volume] // we always use rootfs on SD as storage volume ----> "/dev/mmcblk0p2" int ret = mount(sdPath, "/root/d-box/usersounds", "vfat", 0, NULL); if (ret!=0) { printf("Error in mount...%s\n", strerror(ret)); return 1; } return 0; } int initSoundFiles() { if(gVerbose==1) cout << "---------------->Init Audio Thread" << endl; if(useSD) { // mount the SD partition where user sounds are located // [this is p2, p1 is already mounted and we will log data there] if(mountSDuserPartition()!=0) return -1; } gIsLoading = true; // read files from SD and order them alphabetically if(readFiles()!=0) return 1; // load first file into oscBank loadAudioFiles(true); return 0; } //--------------------------------------------------------------------------------------------------------- // Handle Ctrl-C void interrupt_handler(int var) { // kill keyboard thread mercilessly if(forceKeyboard) pthread_cancel(keyboardThread); gShouldStop = true; } void parseArguments(arg_data args) { // Default filename; gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt"); // TODO: complete this struct option long_option[] = { {"help", 0, NULL, 'h'}, {"period", 1, NULL, 'p'}, {"verbose", 1, NULL, 'v'}, {"audioin", 1, NULL, 'i'}, {"file", 1, NULL, 'f'}, {"keyboard", 1, NULL, 'k'}, {"audio-test", 0, NULL, 'A'}, {"new-sensors", 0, NULL, 'S'}, {"sensor0", 1, NULL, 'Q'}, {"sensor1", 1, NULL, 'R'}, {"log", 1, NULL, 'l'}, {"usesd", 1, NULL, 'u'}, {"oversamp", 1, NULL, 'o'}, {"boxnumber", 1, NULL, 'n'}, {"group", 1, NULL, 'g'}, {NULL, 0, NULL, 0}, }; int morehelp = 0; int tmp = -1; while (1) { int c; if ((c = getopt_long(args.argc, args.argv, "hp:vf:ki:sAQ:R:Sl:u:o:n:g:", long_option, NULL)) < 0) break; switch (c) { case 'h': morehelp++; break; case 'p': gPeriodSize = atoi(optarg); break; case 'v': gVerbose = 1; break; case 'f': free(gPartialFilename); gPartialFilename = strdup(optarg); break; case 'k': forceKeyboard = true; break; case 'i': gAudioIn = (atoi(optarg)==0) ? false : true; break; case 's': forceSensors = true; break; case 'A': useAudioTest = true; break; case 'S': useNewSensors = true; break; case 'Q': touchSensor0Address = atoi(optarg); break; case 'R': touchSensor1Address = atoi(optarg); break; case 'l': tmp = atoi(optarg); if(tmp==0) forceLog = false; else if(tmp>0) forceLog = true; break; case 'u': tmp = atoi(optarg); if(tmp==0) useSD = false; else if(tmp>0) useSD = true; break; case 'o': oscBnkOversampling = atoi(optarg); break; case 'n': gId = *optarg; cout << "-set box number to: " << gId << endl; break; case 'g': gGroup = *optarg; cout << "-set group to: " << gId << endl; break; default: break; } } } int main(int argc, char *argv[]) { RT_TASK rtSensorThread; const char rtSensorThreadName[] = "dbox-sensor"; int oscBankHopSize; // Parse command-line arguments args.argc = argc; args.argv = argv; parseArguments(args); setVerboseLevel(gVerbose); if(gVerbose == 1 && useAudioTest) cout << "main() : running in audio test mode" << endl; // Load sound files from directory if(initSoundFiles() != 0) return -1; oscBankHopSize = gOscBanks[gCurrentOscBank]->getHopSize()/gOscBanks[gCurrentOscBank]->getMinSpeed(); // Initialise the audio device if(initAudio(gPeriodSize, 1, &oscBankHopSize) != 0) return -1; // Initialise the status LED if(!gStatusLED.init(kStatusLEDPin)) { if(gVerbose) cout << "Couldn't initialise status LED pin\n"; } // Free file name string which is no longer needed if(gPartialFilename != 0) free(gPartialFilename); if(!useAudioTest) { if(initSensorLoop(touchSensor0Address, touchSensor1Address, useNewSensors) != 0) return -1; } if(gVerbose == 1) cout << "main() : creating audio thread" << endl; if(startAudio()) { cout << "Error: unable to start real-time audio" << endl; return -1; } // LED on... gStatusLED.on(); if(forceSensors && !useAudioTest) { if(gVerbose==1) cout << "main() : creating control thread" << endl; if(rt_task_create(&rtSensorThread, rtSensorThreadName, 0, 95, T_JOINABLE | T_FPU)) { cout << "Error:unable to create Xenomai control thread" << endl; return -1; } if(rt_task_start(&rtSensorThread, &sensorLoop, 0)) { cout << "Error:unable to start Xenomai control thread" << endl; return -1; } } if(forceKeyboard) { if(gVerbose==1) cout << "main() : creating keyboard thread" << endl; if ( pthread_create(&keyboardThread, NULL, keyboardLoop, NULL) ) { cout << "Error:unable to create keyboard thread" << endl; return -1; } } if(forceLog) { if(gVerbose==1) cout << "main() : creating log thread" << endl; if(initLogLoop()!=0) { cout << "Error:unable to create log thread" << endl; return -1; } if ( pthread_create(&logThread, NULL, logLoop, NULL) ) { cout << "Error:unable to create keyboard thread" << endl; return -1; } } // Set up interrupt handler to catch Control-C signal(SIGINT, interrupt_handler); // load all other files into oscBanks loadAudioFiles(false); cout << "Finished loading analysis files\n"; gIsLoading = false; // Run until told to stop while(!gShouldStop) { usleep(100000); } stopAudio(); if(!useAudioTest) rt_task_join(&rtSensorThread); cleanupAudio(); pthread_join( keyboardThread, NULL); pthread_join( logThread, NULL); for(unsigned int i = 0; i < gOscBanks.size(); i++) delete gOscBanks[i]; NE10_FREE(filterState[0]); NE10_FREE(filterState[1]); NE10_FREE(filterIn[0]); NE10_FREE(filterIn[1]); NE10_FREE(filterOut[0]); NE10_FREE(filterOut[1]); printf("Program ended\nBye bye\n"); return 0; }