243 lines · 5.1 KB · Brainfuck
RawDownload
implement Esd; include "sys.m";include "draw.m";include "math.m";include "arg.m";include "esd.m"; sys		: Sys;arg		: Arg;math		: Math;Connection	: import Sys; Esd : module{	init : fn(nil : ref Draw->Context, nil : list of string);}; SAME, DIFFERENT : con 1 << iota;port		:= ESD_PORT;debug		:= 1;pgrp		: int;client_endian	:= SAME; init(nil : ref Draw->Context, args : list of string){	sys = load Sys Sys->PATH;	arg = load Arg Arg->PATH;        math = load Math Math->PATH;	if (arg == nil || math == nil)	{		sys->raise(sys->sprint("Could not load %s : %r", Arg->PATH+"and "+Math->PATH));	}	arg->init(args);  	pgrp = sys->pctl(sys->NEWPGRP, nil); 	while((c := arg->opt()) != 0)	{		case c		{			'p'	=> port = int arg->arg();             		*	=>				usage();				exit;		}	} 	(n, conn) := sys->announce("tcp!*!"+string port);	if (n < 0)	{		sys->print("Esd : announce failed : %r\n");		exit;	} 	while (1)	{		listen(conn);	}} listen(conn : Connection){	buf := array [sys->ATOMICIO] of byte;		(ok, c) := sys->listen(conn);	if (ok < 0)	{		error(sys->sprint("Listen failed : %r\n"));	} 	rfd := sys->open(conn.dir + "/remote", Sys->OREAD);	n := sys->read(rfd, buf, len buf);	spawn hdlrthread(c);  	return;} hdlrthread(conn : Connection){	buf := array [sys->ATOMICIO] of byte; 	#	The connections data file is not opened by default,	#	must explicitly do so to accept the new connection	rdfd := sys->open(conn.dir + "/data", Sys->OREAD);	wdfd := sys->open(conn.dir + "/data", Sys->OWRITE);	rfd := sys->open(conn.dir + "/remote", Sys->OREAD); 	#	The client IP is now available, once we have accepted connection.	#	The following will print the actual client IP address:	iplen := sys->read(rfd, buf, len buf);	debugprint(sys->sprint("Got new connection from %s",			string buf[:iplen])); 	#	Read Key. BUG: Currently ignore the key sent	keylen := sys->read(rdfd, buf, ESD_KEY_LEN);	if (keylen != ESD_KEY_LEN)	{		error(sys->sprint("Runt key from client, length %d",			keylen));	}	debugprint("client sent key :"+string buf[:keylen]); 	#	Read Endianness.	endianlen := sys->read(rdfd, buf, ESD_ENDIAN_LEN);	if (endianlen != ESD_ENDIAN_LEN)	{		error(sys->sprint("Runt endianess from client, length %d",			endianlen));	}	debugprint("client sent endian: "+string buf[:endianlen]);	if (int buf[0] == 'N')	{		client_endian = DIFFERENT;	} 	#	Send response	debugprint("About to write response to client...");	if (sys->write(wdfd, scrub(OPCODE_RESPONSE), ESD_OPCODE_LEN) !=		ESD_OPCODE_LEN)	{		error(sys->sprint("Could not write response to client: %r"));	}	debugprint("Wrote response to client..."); 	#	Currently only accept "stream" writes. Read the opcode type from client	oplen := sys->read(rdfd, buf, ESD_OPCODE_LEN);	if (oplen != ESD_OPCODE_LEN)	{		error(sys->sprint("Runt opcode from client, length %d",			oplen));	}			scrub(buf[:ESD_OPCODE_LEN]);		if (buf[0] == OPCODE_STREAM[0] && buf[1] == OPCODE_STREAM[1] &&		buf[2] == OPCODE_STREAM[2] && buf[3] == OPCODE_STREAM[3])	{		debugprint("got a stream opcode...");		#	Read and configure format and rate		if (sys->read(rdfd, buf, ESD_FMT_LEN+ESD_RATE_LEN) != ESD_FMT_LEN+ESD_RATE_LEN)		{			error("Runt read getting format and rate from client");		}		configure_audio(scrub(buf[:ESD_FMT_LEN]), scrub(buf[ESD_FMT_LEN:ESD_FMT_LEN+ESD_RATE_LEN])); 		#	Read and discard name. Don't support Esd sample playing etc.		if (sys->read(rdfd, buf, ESD_NAME_LEN) != ESD_NAME_LEN)		{			error("Runt read getting stream name from client");		} 		#afd := sys->open("/tmp/junk.data", Sys->OWRITE);		afd := sys->open("/dev/audio", Sys->OWRITE);		if (afd == nil)		{			error(sys->sprint(				"Could not open /dev/audio for writing: %r"));		}			#		#	Read actual sound data and write to audio device.		#	Writes are done in units smaller than ATOMICIO due 		#	to some strange things occuring on large writes.		#	Not yet sure who's the culprit.		#		chunklen := sys->read(rdfd, buf, 1024);		while(chunklen != 0)		{			if (sys->write(afd, buf[:chunklen], chunklen) != chunklen)			{				error(sys->sprint(					"Could not write %d bytes to /dev/audio: %r",					chunklen));			}			debugprint(sys->sprint("wrote %d bytes to /dev/audio", chunklen));			chunklen = sys->read(rdfd, buf, 1024);		}	}	else	{		sys->sprint("Unknown opcode from client, %d%d%d%d",			int buf[0], int buf[1], int buf[2], int buf[3]);	}  	return;} #	Scrub() rearranges the byte order. Scrub is used#	both taking the return value, or knowing it acts#	on array reference.scrub(data : array of byte) : array of byte{	if (client_endian == SAME)	{		return data;	} 	for (i := 0; i < len data/2; i++)	{		tmp1 := int data[i];		tmp2 := int data[len data - i - 1]; sys->print("\n\n%d --> %d\n", tmp1, tmp2); 		data[i] = byte tmp2;		data[len data - i - 1] = byte tmp1;	}		return data;} configure_audio(fmt : array of byte, rate: array of byte){	debugprint("ignoring config info, hope you don't mind");} debugprint(s: string){	if (debug)	{		sys->print("Esd::DEBUG %s\n", s);	} 	return;} usage(){	sys->print("Usage:\nesd [-p port]\n\n"); 	return;} error(s: string){	sys->raise("fail: Esd:: "+s); 	exit;}