andrewm@5: /*
andrewm@5:  * RTAudioCommandLine.cpp
andrewm@5:  *
andrewm@5:  *  Created on: Nov 8, 2014
andrewm@5:  *      Author: parallels
andrewm@5:  */
andrewm@5: 
andrewm@5: #include <iostream>
andrewm@5: #include <cstdlib>
andrewm@5: #include <cstring>
andrewm@5: #include <getopt.h>
giuliomoro@301: #include "../include/Bela.h"
giuliomoro@24: 
giuliomoro@171: #define OPT_PRU_FILE 1000
giuliomoro@171: #define OPT_PGA_GAIN_LEFT 1001
giuliomoro@171: #define OPT_PGA_GAIN_RIGHT 1002
andrewm@280: #define OPT_PRU_NUMBER 1003
giuliomoro@171: 
andrewm@5: 
andrewm@47: enum {
andrewm@47: 	kAmplifierMutePin = 61	// P8-26 controls amplifier mute
andrewm@47: };
andrewm@47: 
andrewm@5: // Default command-line options for RTAudio
andrewm@5: struct option gDefaultLongOptions[] =
andrewm@5: {
andrewm@5: 	{"period", 1, NULL, 'p'},
andrewm@5: 	{"verbose", 0, NULL, 'v'},
andrewm@50: 	{"use-analog", 1, NULL, 'N'},
andrewm@50: 	{"use-digital", 1, NULL, 'G'},
giuliomoro@19: 	{"analog-channels", 1, NULL, 'C'},
andrewm@50: 	{"digital-channels", 1, NULL, 'B'},
andrewm@5: 	{"mute-speaker", 1, NULL, 'M'},
andrewm@5: 	{"dac-level", 1, NULL, 'D'},
andrewm@5: 	{"adc-level", 1, NULL, 'A'},
giuliomoro@171: 	{"pga-gain-left", 1, NULL, OPT_PGA_GAIN_LEFT},
giuliomoro@171: 	{"pga-gain-right", 1, NULL, OPT_PGA_GAIN_RIGHT},
andrewm@5: 	{"hp-level", 1, NULL, 'H'},
andrewm@50: 	{"receive-port", 1, NULL, 'R'},
andrewm@50: 	{"transmit-port", 1, NULL, 'T'},
andrewm@50: 	{"server-name", 1, NULL, 'S'},
andrewm@45: 	{"pru-file", 1, NULL, OPT_PRU_FILE},
andrewm@280: 	{"pru-number", 1, NULL, OPT_PRU_NUMBER},
andrewm@5: 	{NULL, 0, NULL, 0}
andrewm@5: };
giuliomoro@24: 
andrewm@50: const char gDefaultShortOptions[] = "p:vN:M:C:D:A:H:G:B:R:T:S:";
andrewm@5: 
giuliomoro@301: // This function sets the default settings for the BelaInitSettings structure
giuliomoro@301: void Bela_defaultSettings(BelaInitSettings *settings)
andrewm@5: {
andrewm@5: 	// Set default values for settings
giuliomoro@178: 	settings->periodSize = 16;
andrewm@45: 	settings->useAnalog = 1;
andrewm@45: 	settings->useDigital = 1;
giuliomoro@537: 	settings->numAudioInChannels = 2;
giuliomoro@537: 	settings->numAudioOutChannels = 2;
giuliomoro@537: 
giuliomoro@537: 	settings->numAnalogInChannels = 8;
giuliomoro@537: 	settings->numAnalogOutChannels = 8;
andrewm@45: 	settings->numDigitalChannels = 16;
andrewm@45: 
andrewm@5: 	settings->beginMuted = 0;
andrewm@5: 	settings->dacLevel = DEFAULT_DAC_LEVEL;
andrewm@5: 	settings->adcLevel = DEFAULT_ADC_LEVEL;
giuliomoro@171: 	for(int n = 0; n < 2; n++)
giuliomoro@171: 		settings->pgaGain[n] = DEFAULT_PGA_GAIN;
andrewm@5: 	settings->headphoneLevel = DEFAULT_HP_LEVEL;
andrewm@280: 	settings->numMuxChannels = 0;
andrewm@45: 
andrewm@5: 	settings->verbose = 0;
andrewm@280: 	settings->pruNumber = 0;
andrewm@45: 	settings->pruFilename[0] = '\0';
andrewm@45: 
andrewm@45: 	// These two deliberately have no command-line flags by default.
andrewm@45: 	// A given program might prefer one mode or another, but it's unlikely
andrewm@45: 	// the user would want to switch at runtime
andrewm@45: 	settings->interleave = 1;
andrewm@45: 	settings->analogOutputsPersist = 1;
andrewm@45: 
andrewm@5: 	settings->codecI2CAddress = CODEC_I2C_ADDRESS;
andrewm@45: 	settings->receivePort = 9998;
andrewm@45: 	settings->transmitPort = 9999;
giuliomoro@24: 	strcpy(settings->serverName, "127.0.0.1");
andrewm@5: 	settings->ampMutePin = kAmplifierMutePin;
andrewm@5: }
andrewm@5: 
andrewm@5: // This function drops in place of getopt() in the main() function
andrewm@5: // and handles the initialisation of the RTAudio settings using
andrewm@5: // standard command-line arguments. System default arguments will
andrewm@5: // be stored in settings, otherwise arguments will be returned
andrewm@5: // as getopt() normally does.
andrewm@5: 
giuliomoro@301: int Bela_getopt_long(int argc, char *argv[], const char *customShortOptions, const struct option *customLongOptions, BelaInitSettings *settings)
andrewm@5: {
andrewm@5: 	static int firstRun = 1;
andrewm@5: 	static char totalShortOptions[256];
andrewm@5: 	static struct option totalLongOptions[256];
andrewm@5: 
andrewm@5: 	int c;
andrewm@5: 
andrewm@5: 	// Prep total option string the first time this is
andrewm@5: 	// run. As a getopt() substitute, it will be called repeatedly working its
andrewm@5: 	// way through argc and argv.
andrewm@5: 	if(firstRun) {
andrewm@5: 		firstRun = 0;
andrewm@5: 
andrewm@5: 		// Copy short options into one string
andrewm@5: 		strcpy(totalShortOptions, gDefaultShortOptions);
andrewm@5: 		strncat(totalShortOptions, customShortOptions, 256 - strlen(gDefaultShortOptions) - 1);
andrewm@5: 
andrewm@5: 		// Copy long options into one array
andrewm@5: 		int n = 0;
andrewm@5: 		while(1) {
andrewm@5: 			if(gDefaultLongOptions[n].name == NULL)
andrewm@5: 				break;
andrewm@5: 			totalLongOptions[n].name = gDefaultLongOptions[n].name;
andrewm@5: 			totalLongOptions[n].has_arg = gDefaultLongOptions[n].has_arg;
andrewm@5: 			totalLongOptions[n].flag = gDefaultLongOptions[n].flag;
andrewm@5: 			totalLongOptions[n].val = gDefaultLongOptions[n].val;
andrewm@5: 			n++;
andrewm@5: 		}
andrewm@5: 
andrewm@5: 		// Copy custom options into the array, if present
andrewm@5: 		if(customLongOptions == 0) {
andrewm@5: 			// Terminate the array
andrewm@5: 			totalLongOptions[n].name = NULL;
andrewm@5: 			totalLongOptions[n].has_arg = 0;
andrewm@5: 			totalLongOptions[n].flag = NULL;
andrewm@5: 			totalLongOptions[n].val = 0;
andrewm@5: 		}
andrewm@5: 		else {
andrewm@5: 			int customIndex = 0;
andrewm@5: 			while(n < 256) {
andrewm@5: 				if(customLongOptions[customIndex].name == NULL)
andrewm@5: 					break;
andrewm@5: 				totalLongOptions[n].name = customLongOptions[customIndex].name;
andrewm@5: 				totalLongOptions[n].has_arg = customLongOptions[customIndex].has_arg;
andrewm@5: 				totalLongOptions[n].flag = customLongOptions[customIndex].flag;
andrewm@5: 				totalLongOptions[n].val = customLongOptions[customIndex].val;
andrewm@5: 				n++;
andrewm@5: 				customIndex++;
andrewm@5: 			}
andrewm@5: 
andrewm@5: 			// Terminate the array
andrewm@5: 			totalLongOptions[n].name = NULL;
andrewm@5: 			totalLongOptions[n].has_arg = 0;
andrewm@5: 			totalLongOptions[n].flag = NULL;
andrewm@5: 			totalLongOptions[n].val = 0;
andrewm@5: 		}
andrewm@5: 	}
andrewm@5: 
andrewm@5: 	while(1) {
andrewm@5: 		if ((c = getopt_long(argc, argv, totalShortOptions, totalLongOptions, NULL)) < 0)
andrewm@5: 			return c;
andrewm@5: 
andrewm@5: 		switch (c) {
andrewm@5: 		case 'p':
andrewm@5: 			settings->periodSize = atoi(optarg);
andrewm@5: 			if(settings->periodSize < 1)
andrewm@5: 				settings->periodSize = 1;
andrewm@5: 			break;
andrewm@5: 		case 'v':
andrewm@5: 			settings->verbose = 1;
andrewm@5: 			break;
andrewm@50: 		case 'N':
giuliomoro@19: 			settings->useAnalog = atoi(optarg);
andrewm@5: 			break;
andrewm@50: 		case 'G':
giuliomoro@19: 			settings->useDigital = atoi(optarg);
giuliomoro@240: 			if(settings->useDigital == 0){
giuliomoro@240: 				settings->numDigitalChannels = 0;
giuliomoro@240: 			}
giuliomoro@16: 			break;
giuliomoro@537: 		case 'C': {
giuliomoro@537: 			// TODO: a different number of channels for inputs and outputs is not yet supported
giuliomoro@537: 			unsigned int numAnalogChannels = atoi(optarg);
giuliomoro@537: 			settings->numAnalogInChannels = numAnalogChannels;
giuliomoro@537: 			settings->numAnalogOutChannels = numAnalogChannels;
giuliomoro@537: 			if(numAnalogChannels >= 8) {
giuliomoro@537: 				// TODO: a different number of channels for inputs and outputs is not yet supported
giuliomoro@537: 
andrewm@280: 				// Use multiplexer capelet to run larger numbers of channels
giuliomoro@537: 				if(settings->numAnalogInChannels >= 64)
andrewm@280: 					settings->numMuxChannels = 8;
giuliomoro@537: 				else if(settings->numAnalogInChannels >= 32)
andrewm@280: 					settings->numMuxChannels = 4;
giuliomoro@537: 				else if(settings->numAnalogInChannels >= 16)
andrewm@280: 					settings->numMuxChannels = 2;
giuliomoro@537: 				settings->numAnalogInChannels = 8;
andrewm@280: 			}
giuliomoro@537: 			else if(numAnalogChannels >= 4){
giuliomoro@537: 				// TODO: a different number of channels for inputs and outputs is not yet supported
giuliomoro@537: 				settings->numAnalogInChannels = 4;
giuliomoro@537: 				settings->numAnalogOutChannels = 4;
giuliomoro@537: 			}
giuliomoro@537: 			else{
giuliomoro@537: 				// TODO: a different number of channels for inputs and outputs is not yet supported
giuliomoro@537: 				settings->numAnalogInChannels = 2;
giuliomoro@537: 				settings->numAnalogOutChannels = 2;
giuliomoro@537: 			}
andrewm@12: 			break;
giuliomoro@537: 		}
andrewm@50: 		case 'B':
giuliomoro@19: 			settings->numDigitalChannels = atoi(optarg);
giuliomoro@19: 			if(settings->numDigitalChannels >= 16)
giuliomoro@19: 				settings->numDigitalChannels = 16;
giuliomoro@19: 			else if (settings->numDigitalChannels < 1){
giuliomoro@19: 				settings->numDigitalChannels = 0;
giuliomoro@19: 				settings->useDigital = 0; //TODO: this actually works only if -G 0 is specified after -g 1.
giuliomoro@19: 											 //No worries, though: disabling numDigital will only prevent the pins from being exported.
giuliomoro@16: 			}
giuliomoro@16: 			break;
andrewm@5: 		case 'M':
andrewm@5: 			settings->beginMuted = atoi(optarg);
andrewm@5: 			break;
andrewm@5: 		case 'D':
andrewm@5: 			settings->dacLevel = atof(optarg);
andrewm@5: 			break;
andrewm@5: 		case 'A':
andrewm@5: 			settings->adcLevel = atof(optarg);
andrewm@5: 			break;
andrewm@5: 		case 'H':
andrewm@5: 			settings->headphoneLevel = atof(optarg);
andrewm@5: 			break;
andrewm@50: 		case 'R':
giuliomoro@24: 			settings->receivePort = atoi(optarg);
giuliomoro@24: 			break;
andrewm@50: 		case 'T':
giuliomoro@24: 			settings->transmitPort = atoi(optarg);
giuliomoro@24: 			break;
andrewm@50: 		case 'S':
giuliomoro@24: 			if(strlen(optarg)<MAX_SERVERNAME_LENGTH)
giuliomoro@24: 				strcpy(settings->serverName, optarg);
giuliomoro@24: 			else
giuliomoro@24: 				std::cerr << "Warning: server name is too long (>" << MAX_SERVERNAME_LENGTH << " characters)."
giuliomoro@24: 						" Using default severName Instead ( " << settings->serverName << " ).\n";
giuliomoro@24: 			break;
giuliomoro@29: 		case OPT_PRU_FILE:
andrewm@45: 			if(strlen(optarg) < MAX_PRU_FILENAME_LENGTH)
giuliomoro@16: 				strcpy(settings->pruFilename, optarg);
giuliomoro@16: 			else
giuliomoro@16: 				std::cerr << "Warning: filename for the PRU code is too long (>" << MAX_PRU_FILENAME_LENGTH << " characters). Using embedded PRU code instead\n";
giuliomoro@16: 			break;
giuliomoro@171: 		case OPT_PGA_GAIN_LEFT:
giuliomoro@171: 			settings->pgaGain[0] = atof(optarg);
giuliomoro@171: 			break;
giuliomoro@171: 		case OPT_PGA_GAIN_RIGHT:
giuliomoro@171: 			settings->pgaGain[1] = atof(optarg);
giuliomoro@171: 			break;
andrewm@280: 		case OPT_PRU_NUMBER:
andrewm@280: 			settings->pruNumber = atoi(optarg);
andrewm@280: 			break;
andrewm@5: 		case '?':
andrewm@5: 		default:
andrewm@5: 			return c;
andrewm@5: 		}
andrewm@5: 	}
andrewm@5: }
andrewm@5: 
andrewm@5: // This function prints standard usage information for default arguments
andrewm@5: // Call from within your own usage function
giuliomoro@301: void Bela_usage()
andrewm@5: {
giuliomoro@19: 	std::cerr << "   --period [-p] period:            Set the hardware period (buffer) size in analog samples\n";
giuliomoro@16: 	std::cerr << "   --dac-level [-D] dBs:            Set the DAC output level (0dB max; -63.5dB min)\n";
giuliomoro@16: 	std::cerr << "   --adc-level [-A] dBs:            Set the ADC input level (0dB max; -12dB min)\n";
giuliomoro@171: 	std::cerr << "   --pga-gain-left dBs:             Set the Programmable Gain Amplifier for the left audio channel (0dBmin; 59.5dB max; default: 16dB)\n";
giuliomoro@171: 	std::cerr << "   --pga-gain-right dBs:            Set the Programmable Gain Amplifier for the right audio channel (0dBmin; 59.5dB max; default: 16dB)\n";
giuliomoro@16: 	std::cerr << "   --hp-level [-H] dBs:             Set the headphone output level (0dB max; -63.5dB min)\n";
giuliomoro@16: 	std::cerr << "   --mute-speaker [-M] val:         Set whether to mute the speaker initially (default: no)\n";
andrewm@52: 	std::cerr << "   --use-analog [-N] val:           Set whether to use ADC/DAC analog (default: yes)\n";
andrewm@52: 	std::cerr << "   --use-digital [-G] val:          Set whether to use digital GPIO channels (default: yes)\n";
giuliomoro@19: 	std::cerr << "   --analog-channels [-C] val:      Set the number of ADC/DAC channels (default: 8)\n";
andrewm@52: 	std::cerr << "   --digital-channels [-B] val:     Set the number of GPIO channels (default: 16)\n";
andrewm@52: 	std::cerr << "   --receive-port [-R] val:         Set the receive port (default: 9998)\n";
andrewm@52: 	std::cerr << "   --transmit-port [-T] val:        Set the transmit port (default: 9999)\n";
andrewm@52: 	std::cerr << "   --server-name [-S] val:          Set the destination server name (default: '127.0.0.1')\n";
giuliomoro@24: 	std::cerr << "   --pru-file val:                  Set an optional external file to use for the PRU binary code\n";
andrewm@280: 	std::cerr << "   --pru-number val:                Set the PRU to use for I/O (options: 0 or 1, default: 0)\n";
giuliomoro@16: 	std::cerr << "   --verbose [-v]:                  Enable verbose logging information\n";
andrewm@5: }
giuliomoro@16: