Mercurial > hg > beaglert
view 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 |
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; int sensorType = 0; 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, RTAudioSettings *settings) { // Default filename; gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt"); // TODO: complete this struct option long_option[] = { {"help", 0, NULL, 'h'}, {"audioin", 1, NULL, 'i'}, {"file", 1, NULL, 'f'}, {"keyboard", 1, NULL, 'k'}, {"audio-test", 0, NULL, 'T'}, {"sensor-type", 1, 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; BeagleRT_defaultSettings(settings); while (1) { int c; if ((c = BeagleRT_getopt_long(args.argc, args.argv, "hf:ki:sTQ:R:S:l:u:o:n:g:", long_option, settings)) < 0) break; switch (c) { case 'h': morehelp++; 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 'T': useAudioTest = true; break; case 'S': sensorType = atoi(optarg); 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; } } gPeriodSize = settings->periodSize; gVerbose = settings->verbose; } int main(int argc, char *argv[]) { RTAudioSettings settings; // Standard audio settings RT_TASK rtSensorThread; const char rtSensorThreadName[] = "dbox-sensor"; int oscBankHopSize; // Parse command-line arguments args.argc = argc; args.argv = argv; parseArguments(args, &settings); 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(BeagleRT_initAudio(&settings, &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, sensorType) != 0) return -1; } if(gVerbose == 1) cout << "main() : creating audio thread" << endl; if(BeagleRT_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 and SIGTERM signal(SIGINT, interrupt_handler); signal(SIGTERM, 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); } BeagleRT_stopAudio(); if(!useAudioTest) rt_task_join(&rtSensorThread); BeagleRT_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; }