Mercurial > hg > beaglert
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/projects/d-box/main.cpp Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,490 @@ +/* + * 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; +}