diff options
| author | Goran Mekić <meka@tilda.center> | 2017-05-12 11:47:07 +0200 | 
|---|---|---|
| committer | Goran Mekić <meka@tilda.center> | 2017-05-14 21:43:18 +0200 | 
| commit | ba308173e6cbce1f54cfbe80c059641052681b72 (patch) | |
| tree | d7dca8fd76503f8ab97b26d22eb5545a4458d114 /drumgizmo | |
| parent | b50c35be377c659cd4eeccd2148dfe1a27e4adbc (diff) | |
Use getoptpp for CLI argument parsing
Diffstat (limited to 'drumgizmo')
| -rw-r--r-- | drumgizmo/Makefile.am | 3 | ||||
| -rw-r--r-- | drumgizmo/drumgizmoc.cc | 627 | 
2 files changed, 281 insertions, 349 deletions
| diff --git a/drumgizmo/Makefile.am b/drumgizmo/Makefile.am index e41e72c..de5cad6 100644 --- a/drumgizmo/Makefile.am +++ b/drumgizmo/Makefile.am @@ -9,7 +9,7 @@ drumgizmo_LDADD = $(JACK_LIBS) $(top_srcdir)/src/libdg.la  drumgizmo_LDFLAGS =  drumgizmo_CXXFLAGS = \ -	-I$(top_srcdir)/include -I$(top_srcdir)/src \ +	-I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/getoptpp \  	-I$(top_srcdir)/hugin -DWITH_HUG_MUTEX -DWITH_HUG_FILTER \  	$(JACK_CFLAGS) $(SSEFLAGS) @@ -86,6 +86,7 @@ EXTRA_DIST = \  	drumgizmoc.h \  	jackclient.h \  	enginefactory.h \ +	getoptpp/getoptpp.hpp \  	input/inputdummy.h \  	input/test.h \  	input/jackmidi.h \ diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 6244125..6bb4d1a 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -27,11 +27,9 @@  #include <iostream>  #include <config.h> -#include <getopt.h> +#include <getoptpp.hpp>  #include <string.h> -#include <stdio.h> -#include <stdlib.h>  #include <sstream>  #include <chrono>  #include <thread> @@ -46,398 +44,338 @@  #include "event.h" -static const char version_str[] = "DrumGizmo v" VERSION "\n"; - -static const char copyright_str[] = -	"Copyright (C) 2008-2011 Bent Bisballe Nyeng - Aasimon.org.\n" -	"This is free software.  You may redistribute copies of it under the terms " -	"of\n" -	"the GNU Lesser General Public License <http://www.gnu.org/licenses/gpl.html>.\n" -	"There is NO WARRANTY, to the extent permitted by law.\n" -	"\n" -	"Written by Bent Bisballe Nyeng (deva@aasimon.org)\n"; - -static const char usage_str[] = -    "Usage: %s [options] drumkitfile\n" -    "Options:\n" -    "  -a, --async-load       Load drumkit in the background and start the " -    "engine immediately.\n" -    "  -i, --inputengine      dummy|test|jackmidi|midifile  Use said event " -    "input engine.\n" -    "  -I, --inputparms       parmlist  Set input engine parameters.\n" -    "  -o, --outputengine     dummy|alsa|jackaudio|wavfile  Use said audio " -    "output engine.\n" -    "  -O, --outputparms      parmlist  Set output engine parameters.\n" -    "  -e, --endpos           Number of samples to process, -1: infinite.\n" -#ifndef DISABLE_HUGIN -    "  -D, --debug ddd        Enable debug messages on 'ddd'; see hugin " -    "documentation for details\n" -#endif /*DISABLE_HUGIN*/ -    "  -r, --no-resampling    Disable resampling.\n" -    "  -s, --streaming        Enable streaming.\n" -    "  -S, --streamingparms   Streaming options.\n" -    "  -v, --version          Print version information and exit.\n" -    "  -h, --help             Print this message and exit.\n" -    "\n" -    "Input engine parameters:\n" -    "  jackmidi:  midimap=<midimapfile>\n" -    "  midifile:  file=<midifile>, speed=<tempo> (default 1.0),\n" -    "             track=<miditrack> (default -1, all tracks)\n" -    "             midimap=<midimapfile>, loop=<true|false>\n" -    "  test:      p=<hit_propability> (default 0.1)\n" -    "             instr=<instrument> (default -1, random instrument)\n" -    "             len=<seconds> (default -1, forever)\n" -    "  dummy:\n" -    "\n" -    "Output engine parameters:\n" -    "  alsa:      dev=<device> (default 'default'), frames=<frames> (default " -    "32)\n" -    "             srate=<samplerate> (default 441000)\n" -    "  oss:       dev=<device> (default '/dev/dsp'), srate=<samplerate>,\n" -    "             max_fragments=<number> (default 4, see man page for more info),\n" -    "             fragment_size=<selector> (default 8, see man page for more info)\n" -    "  wavfile:   file=<filename> (default 'output'), srate=<samplerate> " -    "(default 44100)\n" -    "  jackaudio:\n" -    "  dummy:\n" -    "\n" -    "Streaming parameters:\n" -    "  limit:         Limit the amount of preloaded drumkit data to the size\n" -    // "  chunk_size:    chunk size in k,M,G\n" -    "\n"; - -int main(int argc, char* argv[]) +typedef struct parm_token  { -	int c; -	Settings settings; -	settings.disk_cache_enable = false; -	std::string hugin_filter; -	unsigned int hugin_flags = 0; -#ifndef DISABLE_HUGIN -	hugin_flags = HUG_FLAG_DEFAULT; -#endif /*DISABLE_HUGIN*/ +	std::string key; +	std::string value; +} parm_token_t; -	std::string outputengine; -	std::string inputengine; -	std::string iparms; -	std::string oparms; -	std::string sparms; -	bool async = false; -	int endpos = -1; -	EngineFactory factory; +static std::string version() +{ +	std::ostringstream output; +	output << "DrumGizmo v" << VERSION << std::endl; +	return output.str(); +} -	int option_index = 0; -	static struct option long_options[] = { -		{"async-load", no_argument, 0, 'a'}, -		{"inputengine", required_argument, 0, 'i'}, -		{"inputparms", required_argument, 0, 'I'}, -		{"outputengine", required_argument, 0, 'o'}, -		{"outputparms", required_argument, 0, 'O'}, -		{"endpos", required_argument, 0, 'e'}, -#ifndef DISABLE_HUGIN -		{"debug", required_argument, 0, 'D'}, -#endif /*DISABLE_HUGIN*/ -		{"streaming", no_argument, 0, 's'}, -		{"streamingparams", required_argument, 0, 'S'}, -		{"version", no_argument, 0, 'v'}, -		{"help", no_argument, 0, 'h'}, -		{"no-resample", no_argument, 0, 'r'}, -		{0, 0, 0, 0} -	}; -	while(1) -	{ -		c = getopt_long(argc, argv, "hvpo:O:i:I:e:arsS:" -#ifndef DISABLE_HUGIN -		                "D:" -#endif /*DISABLE_HUGIN*/ -		                , long_options, &option_index); -		if(c == -1) -		{ -			break; -		} +static std::string copyright() +{ +	std::ostringstream output; +	output << "Copyright (C) 2008-2011 Bent Bisballe Nyeng - Aasimon.org.\n"; +	output << "This is free software.  You may redistribute copies of it under the terms "; +	output << "of\n"; +	output << "the GNU Lesser General Public License <http://www.gnu.org/licenses/gpl.html>.\n"; +	output << "There is NO WARRANTY, to the extent permitted by law.\n"; +	output << "\n"; +	output << "Written by Bent Bisballe Nyeng (deva@aasimon.org)\n"; +	return output.str(); +} -		switch(c) -		{ -		case 'i': -			inputengine = optarg; -			if(inputengine == "help") -			{ -				std::cout << "Available Input Engines = { "; -				for(auto const& name : factory.getInputEngines()) -				{ -					std::cout << name << " "; -				} -				std::cout << "}\n"; -				return 0; -			} -			break; +static std::string usage(std::string name) +{ +	std::ostringstream output; +	output << "Usage: " << name << " [options] drumkitfile\n"; +	output << "Options:\n"; +	output << "  -a, --async-load       Load drumkit in the background and start the "; +	output << "engine immediately.\n"; +	output << "  -i, --inputengine      dummy|test|jackmidi|midifile  Use said event "; +	output << "input engine.\n"; +	output << "  -I, --inputparms       parmlist  Set input engine parameters.\n"; +	output << "  -o, --outputengine     dummy|alsa|jackaudio|wavfile  Use said audio "; +	output << "output engine.\n"; +	output << "  -O, --outputparms      parmlist  Set output engine parameters.\n"; +	output << "  -e, --endpos           Number of samples to process, -1: infinite.\n";  #ifndef DISABLE_HUGIN -		case 'D': -			hugin_flags |= HUG_FLAG_USE_FILTER; -			hugin_filter = optarg; -			break; +	output << "  -D, --debug ddd        Enable debug messages on 'ddd'; see hugin "; +	output << "documentation for details\n";  #endif /*DISABLE_HUGIN*/ +	output << "  -r, --no-resampling    Disable resampling.\n"; +	output << "  -s, --streaming        Enable streaming.\n"; +	output << "  -S, --streamingparms   Streaming options.\n"; +	output << "  -v, --version          Print version information and exit.\n"; +	output << "  -h, --help             Print this message and exit.\n"; +	output << "\n"; +	output << "Input engine parameters:\n"; +	output << "  jackmidi:  midimap=<midimapfile>\n"; +	output << "  midifile:  file=<midifile>, speed=<tempo> (default 1.0),\n"; +	output << "             track=<miditrack> (default -1, all tracks)\n"; +	output << "             midimap=<midimapfile>, loop=<true|false>\n"; +	output << "  test:      p=<hit_propability> (default 0.1)\n"; +	output << "             instr=<instrument> (default -1, random instrument)\n"; +	output << "             len=<seconds> (default -1, forever)\n"; +	output << "  dummy:\n"; +	output << "\n"; +	output << "Output engine parameters:\n"; +	output << "  alsa:      dev=<device> (default 'default'), frames=<frames> (default "; +	output << "32)\n"; +	output << "             srate=<samplerate> (default 441000)\n"; +	output << "  oss:       dev=<device> (default '/dev/dsp'), srate=<samplerate>,\n"; +	output << "             max_fragments=<number> (default 4, see man page for more info),\n"; +	output << "             fragment_size=<selector> (default 8, see man page for more info)\n"; +	output << "  wavfile:   file=<filename> (default 'output'), srate=<samplerate> "; +	output << "(default 44100)\n"; +	output << "  jackaudio:\n"; +	output << "  dummy:\n"; +	output << "\n"; +	output << "Streaming parameters:\n"; +	output << "  limit:         Limit the amount of preloaded drumkit data to the size\n"; +	// output << "  chunk_size:    chunk size in k,M,G\n" +	output << "\n"; +	return output.str(); +} -		case 'I': -			iparms = optarg; -			break; -		case 'o': -			outputengine = optarg; -			if(outputengine == "help") +std::vector<parm_token_t> parse_parameters(std::string &parms) +{ +	std::vector<parm_token_t> result; +	std::string parm; +	std::string val; +	bool inval = false; +	wordexp_t exp_result; +	for(size_t i = 0; i < parms.size(); ++i) +	{ +		if(parms[i] == ',') +		{ +			int error = wordexp(val.data(), &exp_result, 0); +			if(error)  			{ -				std::cout << "Available Output Engines = { "; -				for(auto const& name : factory.getOutputEngines()) -				{ -					std::cout << name << " "; -				} -				std::cout << "}\n"; -				return 0; +				std::cerr << "Wrong argument: "; +				std::cerr << parm << " = " << val << std::endl; +				exit(1);  			} -			break; - -		case 'O': -			oparms = optarg; -			break; - -		case 'a': -			async = true; -			break; - -		case 'e': -			endpos = atoi(optarg); -			break; - -		case '?': -		case 'h': -			printf("%s", version_str); -			printf(usage_str, argv[0]); -			return 0; - -		case 'v': -			printf("%s", version_str); -			printf("%s", copyright_str); -			return 0; - -		case 'r': -			settings.enable_resampling = false; -			break; - -		case 's': -			settings.disk_cache_enable = true; -			break; +			result.push_back({parm, exp_result.we_wordv[0]}); +			parm = ""; +			val = ""; +			inval = false; +			continue; +		} -		case 'S': -			sparms = optarg; -			break; +		if(parms[i] == '=') +		{ +			inval = true; +			continue; +		} -		default: -			break; +		if(inval) +		{ +			val += parms[i]; +		} +		else +		{ +			parm += parms[i];  		}  	} - -	hug_status_t status = hug_init(hugin_flags, HUG_OPTION_FILTER, -	                               hugin_filter.c_str(), HUG_OPTION_END); -	if(status != HUG_STATUS_OK) +	if(parm != "")  	{ -		printf("Error: %d\n", status); -		return 1; +		int error = wordexp(val.data(), &exp_result, 0); +		if(error) +		{ +			std::cerr << "Wrong argument: "; +			std::cerr << parm << " = " << val << std::endl; +			exit(1); +		} +		result.push_back({parm, exp_result.we_wordv[0]});  	} +	return result; +} -	DEBUG(drumgizmo, "Debug enabled."); - -	if(inputengine == "") -	{ -		printf("Missing input engine\n"); -		return 1; -	} -	auto ie = factory.createInput(inputengine); +int main(int argc, char* argv[]) +{ +	EngineFactory factory; +	Settings settings; +	bool async = false; +	dg::Options opt; +	int endpos = -1; +	std::string kitfile; +	std::unique_ptr<AudioInputEngine> ie; +	std::unique_ptr<AudioOutputEngine> oe; +	std::string hugin_filter; +	unsigned int hugin_flags = 0; +#ifndef DISABLE_HUGIN +	hugin_flags = HUG_FLAG_DEFAULT; +#endif /*DISABLE_HUGIN*/ -	if(ie == NULL) -	{ -		printf("Invalid input engine: %s\n", inputengine.c_str()); -		return 1; -	} +	opt.add("async-load", no_argument, 'a', [&]() { +		async = true; +	}); -	{ -		std::string parm; -		std::string val; -		bool inval = false; -		wordexp_t exp_result; -		for(size_t i = 0; i < iparms.size(); ++i) +	opt.add("inputengine", required_argument, 'i', [&]() { +		std::string engine = optarg; +		if(engine == "help")  		{ -			if(iparms[i] == ',') +			std::cout << "Available Input Engines = { "; +			for(auto const& name : factory.getInputEngines())  			{ -				int error = wordexp(val.data(), &exp_result, 0); -				if(error) -				{ -					std::cerr << "Wrong argument: "; -					std::cerr << parm << " = " << val << std::endl; -					return 1; -				} -				ie->setParm(parm, exp_result.we_wordv[0]); -				parm = ""; -				val = ""; -				inval = false; -				continue; +				std::cout << name << " ";  			} +			std::cout << "}\n"; +			exit(0); +		} +		if(engine == "") +		{ +			std::cerr << "Missing input engine" << std::endl;; +			exit(1); +		} +		ie = factory.createInput(engine); +		if(ie == NULL) +		{ +			std::cerr << "Invalid input engine: " << engine << std::endl; +			exit(1); +		} +	}); -			if(iparms[i] == '=') -			{ -				inval = true; -				continue; -			} +	opt.add("inputparms", required_argument, 'I', [&]() { +		std::string parms = optarg; +		auto tokens = parse_parameters(parms); +		for(auto &token : tokens) +		{ +			ie->setParm(token.key, token.value); +		} +	}); -			if(inval) -			{ -				val += iparms[i]; -			} -			else +	opt.add("outputengine", required_argument, 'o', [&]() { +		std::string engine = optarg; +		if(engine == "help") +		{ +			std::cout << "Available Output Engines = { "; +			for(auto const& name : factory.getOutputEngines())  			{ -				parm += iparms[i]; +				std::cout << name << " ";  			} +			std::cout << " }\n"; +			exit(0);  		} -		if(parm != "") +		if(engine == "")  		{ -			int error = wordexp(val.data(), &exp_result, 0); -			if(error) -			{ -				std::cerr << "Wrong argument: "; -				std::cerr << parm << " = " << val << std::endl; -				return 1; -			} -			ie->setParm(parm, exp_result.we_wordv[0]); +			std::cerr << "Missing output engine" << std::endl; +			exit(1);  		} -	} +		oe = factory.createOutput(engine); +		if(ie == NULL) +		{ +			std::cerr << "Invalid output engine: " << engine << std::endl;; +			exit(1); +		} +	}); -	{ -		if(sparms.size() != 0) +	opt.add("outputparms", required_argument, 'O', [&]() { +		std::string parms = optarg; +		auto tokens = parse_parameters(parms); +		for(auto &token : tokens)  		{ -			if(sparms[0] == ',' || sparms[sparms.size()-1] == ',') +			oe->setParm(token.key, token.value); +		} +	}); + +	opt.add("endpos", required_argument, 'e', [&]() { +			try  			{ -				std::cerr << "Invalid streamparms" << std::endl; -				return 1; +				endpos = std::stoi(optarg);  			} -			std::string token; -			std::istringstream tokenstream(sparms); -			while(getline(tokenstream, token, ',')) +			catch(...)  			{ -				std::size_t index = token.find('='); -				if(index == std::string::npos || index == token.size()-1 || index == 0) -				{ -					std::cerr << "Invalid streamparms" << std::endl; -					return 1; -				} +				std::cerr << "Invalid endpos size " << optarg << std::endl; +			} +	}); -				std::string parm = token.substr(0, index); -				std::string value = token.substr(index+1); +#ifndef DISABLE_HUGIN +	opt.add("debug", required_argument, 'D', [&]() { +		hugin_flags |= HUG_FLAG_USE_FILTER; +		hugin_filter = optarg; +	}); +#endif /*DISABLE_HUGIN*/ -				if(parm == "limit") -				{ -					settings.disk_cache_upper_limit = byteSizeParser(value); -					if(settings.disk_cache_upper_limit == 0) -					{ -						std::cerr << "Invalid argument for streamparms limit: " << value << std::endl; -						return 1; -					} -				} -				// else if(parm == "chunk_size") -				// { -				// 	settings.disk_cache_chunk_size = byteSizeParser(value); -				// 	if(settings.disk_cache_chunk_size == 0) -				// 	{ -				// 		std::cerr << "Invalid argument for streamparms chunk_size: " << value << std::endl; -				// 		return 1; -				// 	} -				// } -				else +	opt.add("no-resampling", no_argument, 'r', [&]() { +		settings.enable_resampling = false; +	}); + +	opt.add("streaming", no_argument, 's', [&]() { +		settings.disk_cache_enable = true; +	}); + +	opt.add("streamingparms", required_argument, 'S', [&]() { +		std::string parms = optarg; +		auto tokens = parse_parameters(parms); +		for(auto &token : tokens) +		{ +			if(token.key == "limit") +			{ +				settings.disk_cache_upper_limit = byteSizeParser(token.value); +				if(settings.disk_cache_upper_limit == 0)  				{ -					std::cerr << "Unknown streamingparms argument " << parm << std::endl; -					return 1; +					std::cerr << "Invalid argument for streamparms limit: " << token.value << std::endl; +					exit(1);  				}  			} +			// else if(token.key == "chunk_size") +			// { +			// 	settings.disk_cache_chunk_size = byteSizeParser(token.value); +			// 	if(settings.disk_cache_chunk_size == 0) +			// 	{ +			// 		std::cerr << "Invalid argument for streamparms chunk_size: " << token.value << std::endl; +			// 		exit(1); +			// 	} +			// } +			else +			{ +				std::cerr << "Unknown streamingparms argument " << token.key << std::endl; +				exit(1); +			}  		} -	} +	}); + +	opt.add("version", no_argument, 'v', [&]() { +			std::cout << version(); +			std::cout << copyright(); +			exit(0); +		}); -	if(outputengine == "") +	opt.add("help", no_argument, 'h', [&]() { +			std::cout << version(); +			std::cout << usage(argv[0]); +			exit(0); +		}); + +	if(!opt.process(argc, argv))  	{ -		printf("Missing output engine\n");  		return 1;  	} - -	auto oe = factory.createOutput(outputengine); - -	if(oe == NULL) +	hug_status_t status = hug_init(hugin_flags, HUG_OPTION_FILTER, +	                               hugin_filter.data(), HUG_OPTION_END); +	if(status != HUG_STATUS_OK)  	{ -		printf("Invalid output engine: %s\n", outputengine.c_str()); +		std::cerr << "Error: " << status << std::endl;  		return 1;  	} +	DEBUG(drumgizmo, "Debug enabled."); +	if(opt.arguments().empty())  	{ -		std::string parm; -		std::string val; -		bool inval = false; -		for(size_t i = 0; i < oparms.size(); ++i) -		{ -			if(oparms[i] == ',') -			{ -				oe->setParm(parm, val); -				parm = ""; -				val = ""; -				inval = false; -				continue; -			} - -			if(oparms[i] == '=') -			{ -				inval = true; -				continue; -			} - -			if(inval) -			{ -				val += oparms[i]; -			} -			else -			{ -				parm += oparms[i]; -			} -		} -		if(parm != "") -		{ -			oe->setParm(parm, val); -		} +		std::cerr << "Missing kitfile." << std::endl; +		std::cerr << usage(argv[0]) << std::endl; +		return 1;  	} - -	std::string kitfile; - -	if(option_index < argc) +	else if(opt.arguments().size() > 1)  	{ -		while(optind < argc) -		{ -			if(kitfile != "") -			{ -				printf("Can only handle a single kitfile.\n"); -				printf(usage_str, argv[0]); -				return 1; -			} -			kitfile = argv[optind++]; -		} -		printf("\n"); +		std::cerr << "Can only handle a single kitfile." << std::endl; +		std::cerr << usage(argv[0]) << std::endl; +		return 1;  	}  	else  	{ -		printf("Missing kitfile.\n"); -		printf(usage_str, argv[0]); -		return 1; +		kitfile = opt.arguments()[0]; +		std::ifstream tmp_kit(kitfile); +		if(!tmp_kit.good()) +		{ +			std::cerr << "Can not open " << kitfile << std::endl; +			return 1; +		}  	} - -	printf("Using kitfile: %s\n", kitfile.c_str()); +	std::cout << "Using kitfile: " << kitfile << std::endl;  	DrumGizmo gizmo(settings, *oe.get(), *ie.get()); @@ -446,7 +384,7 @@ int main(int argc, char* argv[])  	settings.drumkit_file.store(kitfile);  	settings.reload_counter++; -	printf("Loading drumkit, this may take a while:\n"); +	std::cout << "Loading drumkit, this may take a while:" << std::endl;  	if(!async)  	{ @@ -457,12 +395,11 @@ int main(int argc, char* argv[])  			int total = settings.number_of_files.load();  			int loaded =  settings.number_of_files_loaded.load(); -			printf("\r%d of %d     ", loaded, total); -			fflush(stdout); +			std::cout << "\r" << loaded << " of " << total << std::flush;  			if(settings.drumkit_load_status.load() == LoadStatus::Error)  			{ -				printf("\nFailed to load \"%s\".\n", kitfile.c_str()); +				std::cout << "\nFailed to load " << kitfile << std::endl;  				return 1;  			} @@ -471,7 +408,7 @@ int main(int argc, char* argv[])  				//break;  			}  		} -		printf("\ndone\n"); +		std::cout << "\ndone" << std::endl;  	}  	gizmo.setSamplerate(oe->getSamplerate()); @@ -479,11 +416,10 @@ int main(int argc, char* argv[])  	if(!gizmo.init())  	{ -		printf("Failed init engine.\n"); +		std::cout << "Failed init engine." << std::endl;  		return 1;  	} -	// former drumgizmo run call  	size_t pos = 0;  	size_t nsamples = oe->getBufferSize();  	sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t)); @@ -504,13 +440,8 @@ int main(int argc, char* argv[])  	ie->stop();  	oe->stop(); -  	free(samples); -	// end former drumgizmo run call - -	printf("Quit.\n"); -	fflush(stdout); - +	std::cout << "Quit." << std::endl;  	hug_close();  	return 0; | 
