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;
+}