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