Example #1
0
int
main (int argc, char *argv[])
{

//////////////////////////////////////////////
//will be removed
//	sample_rate=44100;
	sample_rate=48000;
//	period_size=2048;
	period_size=4096;
//	period_size=256;
	//period_size=128;
	bytes_per_sample=4;

	//osc
	const char *listenPort;

	//command line options parsing
	//http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html
	static struct option long_options[] =
	{
		{"help",	no_argument,		0, 'h'},
		{"version",     no_argument,            0, 'v'},
		{"loinfo",      no_argument,            0, 'x'},
		{"out",		required_argument, 	0, 'o'},
		{"offset",	required_argument, 	0, 'f'},
		{"16",          no_argument,            0, 'y'},
		{"max",		required_argument,	0, 'm'},//max (allocate) buffer
		{"update",	required_argument,	0, 'u'},//screen info update every nth cycle
		{"limit",	required_argument,	0, 'l'},//test, stop after n processed
		{0, 0, 0, 0}
	};

	//print program header
	if(argc>1 && strcmp(argv[1],"--version"))
	{
		print_header("audio_post_send");
	}

	if (argc - optind < 1)
	{
		fprintf (stderr, "Missing arguments, see --help.\n\n");
		exit(1);
	}

	int opt;
 	//do until command line options parsed
	while (1)
	{
		/* getopt_long stores the option index here. */
		int option_index = 0;

		opt = getopt_long (argc, argv, "", long_options, &option_index);

		/* Detect the end of the options. */
		if (opt == -1)
		{
			break;
		}
		switch (opt)
		{
			case 0:

			 /* If this option set a flag, do nothing else now. */
			if (long_options[option_index].flag != 0)
			{
				break;
			}

			case 'h':
				print_help();
				break;

			case 'v':
				print_version();
				break;

			case 'x':
				check_lo_props(1);
				return 1;

			case 'o':
				output_port_count=atoi(optarg);

				if(output_port_count>max_channel_count)
				{
					fprintf(stderr,"*** limiting playback ports to %d, sry\n",max_channel_count);
					output_port_count=max_channel_count;
				}
				port_count=fmin(input_port_count,output_port_count);
				break;

			case 'f':
				channel_offset=atoi(optarg);
				break;

			case 'm':
				//min 1 MB
				max_buffer_size=fmax(1,(uint64_t)atoll(optarg)*1000*1000);
				break;

			case 'u':
				update_display_every_nth_cycle=fmax(1,(uint64_t)atoll(optarg));
				break;

			case 'l':
				receive_max=fmax(1,(uint64_t)atoll(optarg));
				test_mode=1;
				fprintf(stderr,"*** limiting number of messages: %" PRId64 "\n",receive_max);

				break;

			case '?': //invalid commands
				/* getopt_long already printed an error message. */
				fprintf (stderr, "Wrong arguments, see --help.\n\n");
				exit(1);

				break;
 	 
			default:
				break;
		 } //end switch op
	}//end while(1)

	//remaining non optional parameters listening port, remote host, remote port
	if(argc-optind != 3)
	{
		fprintf (stderr, "Wrong arguments, see --help.\n\n");
		exit(1);
	}

	if(check_lo_props(0)>0)
	{
		return 1;
	}

	if(have_libjack()!=0)
	{
		fprintf(stderr,"/!\\ libjack not found (JACK not installed?). this is fatal: audio_post_send needs JACK to run.\n");
		//io_quit("nolibjack");
		exit(1);
	}

	listenPort=argv[optind];

	//tcp target
	remote_tcp_host=argv[optind+1];
	remote_tcp_port=argv[optind+2];

	loa_tcp = lo_address_new_with_proto(LO_TCP, remote_tcp_host, remote_tcp_port);

	//initialize time
	gettimeofday(&tv, NULL);
	tt_prev.sec=tv.tv_sec;
	tt_prev.frac=tv.tv_usec;

	//print startup info

	fprintf(stderr,"listening on UDP port: %s\n",listenPort);
	//udp/tcp use the same port for now
	fprintf(stderr,"started TCP server on port: %s\n",listenPort);

	fprintf(stderr,"channels (forward): %d\n",output_port_count);
	fprintf(stderr,"channel offset: %d\n",channel_offset);

	fprintf(stderr, "TCP target: %s:%s\n",remote_tcp_host,remote_tcp_port);

	fprintf(stderr, "period size (TCP forward): %d samples\n",period_size);

	fprintf(stderr, "delay between TCP sends: %d ms\n",delay_between_tcp_sends);
	fprintf(stderr, "delay between TCP retries on broken connection: %d ms\n",delay_between_tcp_retries);

	//ringbuffer size bytes
	uint64_t rb_size;

	//use as given via param --max or:
	if(max_buffer_size==0)
	{
		//default
		//10 MB           .  .  
		max_buffer_size=10000000;
	}

	//
	rb_size=max_buffer_size;

	fprintf(stderr,"allocated buffer size: %" PRId64 " bytes (%.2f MB)\n",max_buffer_size,(float)max_buffer_size/1000/1000);

	//====================================
	//main ringbuffer osc blobs -> jack output
	rb = rb_new (rb_size);
	//helper ringbuffer: used when remote period size < local period size
	rb_helper = rb_new (rb_size);

	if(rb==NULL)
	{
		fprintf(stderr,"could not create a ringbuffer with that size.\n");
		fprintf(stderr,"try --max <smaller size>.\n");
		exit(1);
	}

	/* install a signal handler to properly quits jack client */
#ifndef _WIN
	signal(SIGQUIT, signal_handler);
	signal(SIGHUP, signal_handler);
#endif
	signal(SIGTERM, signal_handler);
	signal(SIGINT, signal_handler);

	//add osc hooks & start UDP server
	registerOSCMessagePatterns(listenPort);
	lo_server_thread_start(lo_st);

	//start TCP server, for forwarding to final receiver
	lo_st_tcp = lo_server_thread_new_with_proto(listenPort, LO_TCP, error);
	lo_server_thread_start(lo_st_tcp);

	fflush(stderr);

	/* keep running until the Ctrl+C */
	while(1) 
	{
		//possibly clean shutdown without any glitches
		if(shutdown_in_progress==1)
		{
			signal_handler(42);
		}

		//if tcp message could not be sent
		if(process()<0)
		{
			//wait x and update info
			int i;
			for(i=0;i<delay_between_tcp_retries;i++)
			{
				usleep(1000);
				print_info();
			}
		}
		else
		{
			//wait y and update info
			int i;
			for(i=0;i<delay_between_tcp_sends;i++)
			{
				usleep(1000);
				print_info();
			}
		}
	}
	exit (0);
}
//================================================================
int main(int argc, char *argv[])
{
	//jack
	const char **ports;
	//jack_options_t options = JackNullOption;
	jack_status_t status;

//options struct was here

	if(argc-optind<1)
	{
		print_header("jack_audio_receive");
		fprintf(stderr, "Missing arguments, see --help.\n\n");
		exit(1);
	}

	int opt;
 	//do until command line options parsed
	while(1)
	{
		/* getopt_long stores the option index here. */
		int option_index=0;

		opt=getopt_long(argc, argv, "", long_options, &option_index);

		/* Detect the end of the options. */
		if(opt==-1)
		{
			break;
		}
		switch(opt)
		{
			case 0:

			 /* If this option set a flag, do nothing else now. */
			if(long_options[option_index].flag!=0)
			{
				break;
			}

			case 'h':
				print_header("jack_audio_receive");
				print_help();
				break;

			case 'v':
				print_version();
				break;

			case 'x':
				print_header("jack_audio_receive");
				check_lo_props(1);
				return 1;

			case 'o':
				output_port_count=atoi(optarg);

				if(output_port_count>max_channel_count)
				{
					output_port_count=max_channel_count;
				}
				port_count=fmin(input_port_count,output_port_count);
				break;

			case 'f':
				channel_offset=atoi(optarg);
				break;

			case 'y':
				bytes_per_sample=2;
				break;

			case 'n':
				client_name=optarg;
				break;

			case 's':
				server_name=optarg;
				jack_opts |= JackServerName;
				break;

			case 'b':
				pre_buffer_size=fmax(1,(uint64_t)atoll(optarg));
				break;

			case 'm':
				max_buffer_size=fmax(1,(uint64_t)atoll(optarg));
				break;

			case 'u':
				update_display_every_nth_cycle=fmax(1,(uint64_t)atoll(optarg));
				break;

			case 'l':
				receive_max=fmax(1,(uint64_t)atoll(optarg));
				test_mode=1;
				break;

			case 'a':
				io_host=optarg;
				break;

			case 'c':
				io_port=optarg;
				break;

			case 't':
				use_tcp=1;
				remote_tcp_server_port=optarg;
				break;

			case '?': //invalid commands
				/* getopt_long already printed an error message. */
				print_header("jack_audio_receive");
				fprintf(stderr, "Wrong arguments, see --help.\n\n");
				exit(1);

				break;
 	 
			default:
				break;
		 } //end switch op
	}//end while(1)


	//remaining non optional parameters listening port
	if(argc-optind!=1)
	{
		print_header("jack_audio_receive");
		fprintf(stderr, "Wrong arguments, see --help.\n\n");
		exit(1);
	}

	localPort=argv[optind];

	//for commuication with a gui / other controller / visualizer
	loio=lo_address_new_with_proto(LO_UDP, io_host, io_port);

	//if was set to use random port
	if(atoi(localPort)==0)
	{
		//for lo_server_thread_new_with_proto
		localPort=NULL;
	}

	//add osc hooks & start osc server early (~right after cmdline parsing)
	registerOSCMessagePatterns(localPort);

	lo_server_thread_start(lo_st);

	//read back port (in case of random)
	//could use 
	//int lo_server_thread_get_port(lo_server_thread st)
	const char *osc_server_url=lo_server_get_url(lo_server_thread_get_server(lo_st));
	localPort=lo_url_get_port(osc_server_url);
	//int lport=lo_server_thread_get_port(lo_st);

	//notify osc gui
	if(io_())
	{
		lo_message msgio=lo_message_new();
		lo_message_add_float(msgio, version);
		lo_message_add_float(msgio, format_version);
		lo_send_message(loio, "/startup", msgio);
		lo_message_free(msgio);
	}

	if(check_lo_props(0)>0)
	{
		return 1;
	}


	if(use_tcp==1)
	{
		lo_proto=LO_TCP;
	}

	if(shutup==0)
	{
		print_header("jack_audio_receive");

		if(output_port_count>max_channel_count)
		{
			fprintf(stderr,"/!\\ limiting playback ports to %d, sry\n",max_channel_count);
		}

		if(test_mode==1)
		{
			fprintf(stderr,"/!\\ limiting number of messages: %" PRId64 "\n",receive_max);
		}
	}

	//check for default jack server env var
	char *evar=getenv("JACK_DEFAULT_SERVER");
	if(evar==NULL || strlen(evar)<1)
	{
#ifndef _WIN
		unsetenv("JACK_DEFAULT_SERVER");
#endif
	}

	else if(server_name==NULL)
	{
		//use env var if no server was given with --sname
		server_name=evar;
	}

	if(server_name==NULL || strlen(server_name)<=0)
	{
		server_name="default";
	}

	if(client_name==NULL)
	{
		client_name="receive";
	}

	if(have_libjack()!=0)
	{
		fprintf(stderr,"/!\\ libjack not found (JACK not installed?). this is fatal: jack_audio_receive needs JACK to run.\n");
		io_quit("nolibjack");
		exit(1);
	}

	//initialize time
	gettimeofday(&tv, NULL);
	tt_prev.sec=tv.tv_sec;
	tt_prev.frac=tv.tv_usec;

	//create an array of input ports
	ioPortArray=(jack_port_t**) malloc(output_port_count * sizeof(jack_port_t*));

	//open a client connection to the JACK server
	client=jack_client_open(client_name, jack_opts, &status, server_name);
	if(client==NULL) 
		{
		fprintf(stderr,"jack_client_open() failed, status = 0x%2.0x\n", status);
		if(status & JackServerFailed) 
		{
			fprintf(stderr,"Unable to connect to JACK server.\n");
			io_quit("nojack");
		}
		exit(1);
	}

	if(use_tcp==1)
	{

		if(shutup==0)
		{
			fprintf(stderr,"receiving on TCP port: %s\n",localPort);
		}
	}
	else
	{
		if(shutup==0)
		{
			fprintf(stderr,"receiving on UDP port: %s\n",localPort);
	}
	}

	client_name=jack_get_client_name(client);

	if(shutup==0)
	{
		fprintf(stderr,"started JACK client '%s' on server '%s'\n",client_name,server_name);
		if(status & JackNameNotUnique) 
		{
			fprintf(stderr, "/!\\ name '%s' was automatically assigned\n", client_name);
		}
	}

	if(status & JackNameNotUnique) 
	{
		io_simple("/client_name_changed");
	}

	//print startup info

	read_jack_properties();

	if(shutup==0)
	{
		print_common_jack_properties();

		fprintf(stderr,"channels (playback): %d\n",output_port_count);
		fprintf(stderr,"channel offset: %d\n",channel_offset);

		print_bytes_per_sample();

		fprintf(stderr,"multi-channel period size: %d bytes\n",
			output_port_count*period_size*bytes_per_sample
		);

		char *strat="fill with zero (silence)";
		if(zero_on_underflow==0)
		{
			strat="re-use last available period";
		}

		fprintf(stderr,"underflow strategy: %s\n",strat);

		if(rebuffer_on_restart==1)
		{
			fprintf(stderr,"rebuffer on sender restart: yes\n");
		}
		else
		{
			fprintf(stderr,"rebuffer on sender restart: no\n");
		}

		if(rebuffer_on_underflow==1)
		{
			fprintf(stderr,"rebuffer on underflow: yes\n");
		}
		else
		{
			fprintf(stderr,"rebuffer on underflow: no\n");
		}

		if(allow_remote_buffer_control==1)
		{
			fprintf(stderr,"allow external buffer control: yes\n");
		}
		else
		{
			fprintf(stderr,"allow external buffer control: no\n");
		}

		if(close_on_incomp==1)
		{
			fprintf(stderr,"shutdown receiver when incompatible data received: yes\n");
		}
		else
		{
			fprintf(stderr,"shutdown receiver when incompatible data received: no\n");
		}

	}//end cond. print

	char buf[64];
	format_seconds(buf,(float)pre_buffer_size*period_size/(float)sample_rate);

	uint64_t rb_size_pre=pre_buffer_size*output_port_count*period_size*bytes_per_sample;

	if(shutup==0)
	{
		fprintf(stderr,"initial buffer size: %" PRId64 " mc periods (%s, %" PRId64 " bytes, %.2f MB)\n",
			pre_buffer_size,
			buf,
			rb_size_pre,
			(float)rb_size_pre/1000/1000
		);
	}
	buf[0]='\0';

	//ringbuffer size bytes
	uint64_t rb_size;

	//ringbuffer mc periods
	int max_buffer_mc_periods;

	//max given as param (user knows best. if pre=max, overflows are likely)
	if(max_buffer_size>0)
	{
		max_buffer_mc_periods=fmax(pre_buffer_size,max_buffer_size);
		rb_size=max_buffer_mc_periods
			*output_port_count*period_size*bytes_per_sample;
	}
	else //"auto"
	{
		//make max buffer 0.5 seconds larger than pre buffer
		max_buffer_mc_periods=pre_buffer_size+ceil(0.5*(float)sample_rate/period_size);
		rb_size=max_buffer_mc_periods
			*output_port_count*period_size*bytes_per_sample;
	}

	max_buffer_size=max_buffer_mc_periods;

	format_seconds(buf,(float)max_buffer_mc_periods*period_size/sample_rate);
	if(shutup==0)
	{
		fprintf(stderr,"allocated buffer size: %" PRId64 " mc periods (%s, %" PRId64 " bytes, %.2f MB)\n",
			max_buffer_size,
			buf,
			rb_size,
			(float)rb_size/1000/1000
		);
	}
	buf[0]='\0';

	io_dump_config();

	//====================================
	//main ringbuffer osc blobs -> jack output
	rb=jack_ringbuffer_create(rb_size);
	//helper ringbuffer: used when remote period size < local period size
	rb_helper=jack_ringbuffer_create(rb_size);

	if(rb==NULL)
	{
		fprintf(stderr,"could not create a ringbuffer with that size.\n");
		fprintf(stderr,"try --max <smaller size>.\n");
		io_quit("ringbuffer_too_large");
		exit(1);
	}

	//JACK will call process() for every cycle (given by JACK)
	//NULL could be config/data struct
	jack_set_process_callback(client, process, NULL);

	jack_set_xrun_callback(client, xrun_handler, NULL);

	//register hook to know when JACK shuts down or the connection 
	//was lost (i.e. client zombified)
	jack_on_shutdown(client, jack_shutdown_handler, 0);

	// Register each output port
	int port;
	for(port=0; port<output_port_count; port ++)
	{
		// Create port name
		char* portName;
		if(asprintf(&portName, "output_%d", (port+1)) < 0) 
		{
			fprintf(stderr,"Could not create portname for port %d", port);
			io_quit("port_error");
			exit(1);
		}

		// Register the output port
		ioPortArray[port]=jack_port_register(client, portName, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
		if(ioPortArray[port]==NULL) 
		{
			fprintf(stderr,"Could not create output port %d\n", (port+1));
			io_quit("port_error");
			exit(1);
		}
	}

	/* Tell the JACK server that we are ready to roll. Our
	 * process() callback will start running now. */
	if(jack_activate(client)) 
	{
		fprintf(stderr, "cannot activate client");
		io_quit("cannot_activate_client");
		exit(1);
	}

	/* Connect the ports. You can't do this before the client is
	 * activated, because we can't make connections to clients
	 * that aren't running. Note the confusing (but necessary)
	 * orientation of the driver backend ports: playback ports are
	 * "input" to the backend, and capture ports are "output" from
	 * it.
	 */
	//prevent to get physical midi ports
	const char* pat="audio";

	ports=jack_get_ports(client, NULL, pat, JackPortIsPhysical|JackPortIsInput);

	if(ports==NULL) 
	{
		if(shutup==0)		
		{
			fprintf(stderr,"no physical playback ports\n");
		}
		//exit(1);
	}
	
	if(autoconnect==1)
	{
		fprintf(stderr, "\n");

		int j=0;
		int i;
		for(i=0;i<output_port_count;i++)
		{
			if(ports[i]!=NULL 
				&& ioPortArray[j]!=NULL 
				&& jack_port_name(ioPortArray[j])!=NULL)
			{
				if(!jack_connect(client, jack_port_name(ioPortArray[j]), ports[i]))
				{
					if(shutup==0)
					{
						fprintf(stderr, "autoconnect: %s -> %s\n",
							jack_port_name(ioPortArray[j]),ports[i]
						);
					}
					io_simple_string_double("/autoconnect",jack_port_name(ioPortArray[j]),ports[i]);
					j++;
				}
				else
				{
					if(shutup==0)
					{
						fprintf(stderr, "autoconnect: failed: %s -> %s\n",
							jack_port_name(ioPortArray[j]),ports[i]
						);
					}
				}
			}
			else
			{
				//no more playback ports
				break;
			}
		}//end for all output ports

		if(shutup==0)
		{
			fprintf(stderr, "\n");
		}
	}

	free(ports);

	fflush(stderr);

	/* install a signal handler to properly quits jack client */
#ifndef _WIN
	signal(SIGQUIT, signal_handler);
	signal(SIGHUP, signal_handler);
#endif
	signal(SIGTERM, signal_handler);
	signal(SIGINT, signal_handler);

	if(use_tcp==1)
	{
		//10 MB max
		int desired_max_tcp_size=10000000;

		lo_server s=lo_server_thread_get_server(lo_st);
		int ret_set_size=lo_server_max_msg_size(s, desired_max_tcp_size);

		if(shutup==0)
		{
			printf("set tcp max size return: %d\n",ret_set_size);
			io_simple("/tcp_max_size_xxxx");
		}
	}

	not_yet_ready=0;

	io_simple("/start_main_loop");

	//run possibly forever until not interrupted by any means
	while(1) 
	{
		//possibly clean shutdown without any glitches
		if(shutdown_in_progress==1)
		{
			signal_handler(42);
		}
#ifdef WIN_
		Sleep(1000);
#else
		sleep(1);
#endif
	}

	exit(0);
}//end main