419 lines · 8.3 KB · Brainfuck
RawDownload
##	ESD Audio device. Serves /dev/audio and /dev/audioctl, and#	sends received audio to an Esound daemon, either local or#	somewhere on the network. Esound is the Enlightenment sound#	"thingy", and is available as a server application on many#	Unices. This program negotiates the ESD protocol etc.##	/dev/audio accepts audio data. /dev/audioctl accepts a #	restricted set of the usual Inferno audio header commands:##		rate n#			Where n is the sampling rate. E.g., 44100,#			22050, 8000, etc.##		chans n#			Where n is'1' for mono and '2' for stereo##		bits n#			Bits per sample. Valid values for n are '8' #			and '16'##		enc e#			Where e is the encoding method. This is #			currently not supported.##		The device defaults to 16 bit samples, 44.1KHz, stereo##		BUGS: we should mimic /dev/audioctl exactly.##	This software copyright 2003 Phillip Stanley-Marbell#		Contact: pip@gemusehaken.org, pstanley@ece.cmu.edu#implement EsdAudioDev; include "sys.m";include "draw.m";include "arg.m";include "esd.m"; EsdAudioDev : module{	init : fn(nil : ref Draw->Context, nil : list of string);}; sys	: Sys;arg	: Arg; #	Defaultshostname	:= "localhost";port		:= 16001;binddir		:= "/dev";debug		:= 1;current_format	: array of byte;current_rate	: array of byte;current_name	: array of byte;current_chans	:= 2;current_bits	:= 16; pgrp		: int;esdresetchan	: chan of int; init(nil : ref Draw->Context, args : list of string){	sys = load Sys Sys->PATH;	arg = load Arg Arg->PATH;	if (arg == nil)	{		sys->raise(sys->sprint("Could not load %s : %r", Arg->PATH));	}	arg->init(args);  	pgrp = sys->pctl(sys->NEWPGRP, nil); 	while((c := arg->opt()) != 0)	{		case c		{			'p'	=> port = int arg->arg();			'd'	=> binddir = string arg->arg();			'h'	=> hostname = string arg->arg();             		*	=>				usage();				exit;		}	} 	current_format	= FMT_16BIT_STEREO_STREAM;	current_rate	= RATE_44100;	current_name	= ESD_NAME; 	sys->bind("#s", binddir, sys->MBEFORE);	datachanref := sys->file2chan(binddir, "audio");	ctlchanref := sys->file2chan(binddir, "audioctl"); 	if (datachanref == nil || ctlchanref == nil)	{		sys->print("EsdAudioDev:: Couldn't create chan file in %s : %r\n",			binddir);		exit;	} 	spawn dataworker(datachanref);	spawn ctlworker(ctlchanref);  	return;} dataworker(chanref : ref sys->FileIO){	esddatachan := chan of array of byte;	esdresetchan = chan of int;	spawn esdconnect(esddatachan, esdresetchan); 	while (1)	alt	{		#	For now, ignore reads		(off, nbytes, fid, rc) := <-chanref.read =>		{			if (rc == nil) break; 			#	Finished serving contents of data[]			rc <-= (nil, "");		} 		(offset, writedata, fid, wc) := <-chanref.write =>		{			if (wc == nil)			{				break;			} 			esddatachan <- = writedata;			wc <-= (len writedata, "");		}	}} ctlworker(chanref : ref sys->FileIO){	index 	:= 0;	data	: array of byte;  	while (1)	alt	{		(off, nbytes, fid, rc) := <-chanref.read =>		{			if (rc == nil) break;						if (index == 0)			{				data = array of byte 					("rate "+string current_rate+"\n"+					"chans "+string current_chans+"\n"+					"bits "+string current_bits+"\n");			}						if (index < len data)			{				end := min(index+nbytes, len data);				rc <-= (data[index:end], "");				index = end;			}			else			{				#	Finished serving contents of data[]				rc <-= (nil, "");				index = 0;			}		} 		(offset, writedata, fid, wc) := <-chanref.write =>		{			if (wc == nil)			{				break;			} 			wc <-= (len writedata, "");			debugprint(string writedata); 			if (string writedata[:len "quit"] == "quit")			{				fd := sys->open("#p/"+string pgrp+"/ctl",					sys->OWRITE);				if (fd != nil)				{					sys->fprint(fd, "killgrp"); 					#	Killgrp into ctl doesn't cause suicide, so:					exit;				}				else				{					sys->raise("fail: EsdAudioDev: could not terminate");				}			} 			if (string writedata[:len "rate"] == "rate")			{				(nil, l) := sys->tokenize(string writedata, " ");				rate := hd tl l; 				if (int rate <= 0)				{					error("bogus sampling rate supplied");					continue;				} 				debugprint(sys->sprint("setting sampling rate to [%d]",					int rate));				current_rate = array [] of {byte ((int rate>>24)&16rFF),				byte ((int rate>>16)&16rFF),				byte ((int rate>>8)&16rFF),				byte ((int rate>>0)&16rFF)};				esdresetchan <-= 1;			} 			if (string writedata[:len "chans"] == "chans")			{				(nil, l) := sys->tokenize(string writedata, " ");				chans := hd tl l; 				if (int chans < 1 || int chans > 2)				{					error("bogus # of channels supplied");					continue;				} 				debugprint(sys->sprint("setting sampling chans to [%d]",					int chans));				current_chans = int chans;				reset_format();				esdresetchan <-= 1;			} 			if (string writedata[:len "bits"] == "bits")			{				(nil, l) := sys->tokenize(string writedata, " ");				bits := hd tl l; 				if (int bits != 8 && int bits != 16)				{					error("bogus sample #bits supplied");					continue;				} 				debugprint(sys->sprint("setting sampling #bits to [%d]",					int bits));				current_bits = int bits;				reset_format();				esdresetchan <-= 1;			}		}	}} esdconnect(esddatachan : chan of array of byte, esdresetchan : chan of int){	reply := array [ESD_OPCODE_LEN] of byte;	n := 0; 	(ok, conn) := sys->dial("tcp!"+hostname+"!"+string port, nil);	if (ok < 0)	{		error("error: could not connect to server");	}	debugprint(sys->sprint("connected to host %s...", hostname));	 #	#	Send initial connect opcode. This seems to be valid only#	#	for JEsd and not for the regular Esd:#	n = sys->write(conn.dfd, OPCODE_INIT, ESD_OPCODE_LEN);#	if (n != ESD_OPCODE_LEN)#	{#		error("could not write OPCODE_INIT");#	}#	debugprint(sys->sprint("wrote [%d] bytes to server", n)); 	#	Send ESD key	n = sys->write(conn.dfd, ESD_KEY, ESD_KEY_LEN);	if (n != ESD_KEY_LEN)	{		error("could not write ESD_KEY");	}	debugprint(sys->sprint("wrote [%d] bytes to server", n)); 	#	Send Endianness	n = sys->write(conn.dfd, ESD_ENDIAN, ESD_ENDIAN_LEN);	if (n != ESD_ENDIAN_LEN)	{		error("could not write ESD_ENDIAN");	}	debugprint(sys->sprint("wrote [%d] bytes to server", n));	debugprint("sent initial init messages. about to read response...."); #	#	Get connect response#	n = sys->read(conn.dfd, reply, len reply);##	if (int reply[0] != 1)#	{#		error("server responded negatively on init sequence");#	}#	debugprint(#		sys->sprint(#		"server responded with [%d] bytes, value of first byte is [%d]",#		n, int reply[0])); 	#	Send any received data as a stream	debugprint("about to loop on receive stream / write to server...");	while (1)	alt	{		data := <- esddatachan =>			n = sys->write(conn.dfd, OPCODE_STREAM, ESD_OPCODE_LEN);			if (n != ESD_OPCODE_LEN)			{				error("could not write OPCODE_STREAM");			}			n = sys->write(conn.dfd, current_format, len current_format);			if (n != len current_format)			{				error("could not write current_format");			}			n = sys->write(conn.dfd, current_rate, len current_rate);			if (n != len current_rate)			{				error("could not write current_rate");			}			n = sys->write(conn.dfd, current_name, len current_name);			if (n != len current_name)			{				error("could not write current_name");			}  			n = sys->write(conn.dfd, data, len data);			if (n != len data)			{				error("could not write data read from channel");			} 		<- esdresetchan =>			reply = nil;			conn.dfd = nil;			esdconnect(esddatachan, esdresetchan);	}} reset_format(){	if (current_chans == 1)	{		if (current_bits == 8)		{			current_format	= FMT_8BIT_MONO_STREAM;		}		else if (current_bits = 16)		{			current_format	= FMT_16BIT_MONO_STREAM;		}	}	else if (current_chans == 2)	{		if (current_bits == 8)		{			current_format	= FMT_8BIT_STEREO_STREAM;		}		else if (current_bits = 16)		{			current_format	= FMT_16BIT_STEREO_STREAM;		}	} 	return;} error(err: string){	fd := sys->open(binddir+"/ctl", sys->OWRITE);	if (fd != nil)	{		sys->fprint(fd, "error: %s", err);	}	else	{		sys->raise("fail: EsdAudioDev: could not write to ctl file");	} 	return;} debugprint(s: string){	if (debug)	{		sys->print("EsdAudioDev::DEBUG %s\n", s);	} 	return;} min (a, b : int) : int{	if (a < b)		return a;	return b;} usage(){	sys->print("Udsage:\nesdaudiodev [-p port] [-d directory] [-h hostname]\n\n"); 	return;}