rminnichinitial1fcdf7916 years agoHistory
216 lines · 3.9 KB · C
RawDownload
/* * Simple mp3 player.  Derived from libmad's example minimad.c. */#include <u.h>#include <libc.h>#include "mad.h" /* Current input file */char *name;vlong offset;int rate = 44100; char *outfile;int vfd; /* /dev/volume */ static enum mad_flowinput(void *data, struct mad_stream *stream){	int fd, n, m;	static uchar buf[32768]; 	fd = (int)data;	n = stream->bufend - stream->next_frame;	memmove(buf, stream->next_frame, n);	m = read(fd, buf+n, sizeof buf-n);	offset += m;	if(m < 0)		sysfatal("reading input: %r");	if(m == 0)		return MAD_FLOW_STOP;	n += m;	mad_stream_buffer(stream, buf, n);	return MAD_FLOW_CONTINUE;} /* * Dither 28-bit down to 16-bit.  From mpg321.  * I'm skeptical, but it's supposed to make the * samples sound better than just truncation. */typedef struct Dither Dither;struct Dither{	mad_fixed_t error[3];	mad_fixed_t random;}; #define PRNG(x) (((x)*0x19660dL + 0x3c6ef35fL) & 0xffffffffL) enum{	FracBits = MAD_F_FRACBITS,	OutBits = 16,	Round = 1 << (FracBits+1-OutBits-1),	// sic	ScaleBits = FracBits + 1 - OutBits,	LowMask  = (1<<ScaleBits) - 1,	Min = -MAD_F_ONE,	Max = MAD_F_ONE - 1,}; intaudiodither(mad_fixed_t v, Dither *d){	int out;	mad_fixed_t random; 	/* noise shape */	v += d->error[0] - d->error[1] + d->error[2];	d->error[2] = d->error[1];	d->error[1] = d->error[0] / 2;		/* bias */	out = v + Round;		/* dither */	random = PRNG(d->random);	out += (random & LowMask) - (d->random & LowMask);	d->random = random;		/* clip */	if(out > Max){		out = Max;		if(v > Max)			v = Max;	}else if(out < Min){		out = Min;		if(v < Min)			v = Min;	}		/* quantize */	out &= ~LowMask;		/* error feedback */	d->error[0] = v - out;		/* scale */	return out >> ScaleBits;} static enum mad_flowoutput(void *data, struct mad_header const* header, struct mad_pcm *pcm){	int i, n, v;	mad_fixed_t const *left, *right;	static Dither d;	static uchar buf[16384], *p; 	if(pcm->samplerate != rate){		rate = pcm->samplerate;		if(vfd < 0)			fprint(2, "warning: audio sample rate is %d Hz\n", rate);		else			fprint(vfd, "speed %d", rate);	}	p = buf;	memset(&d, 0, sizeof d);	n = pcm->length;	switch(pcm->channels){	case 1:		left = pcm->samples[0];		for(i=0; i<n; i++){			v = audiodither(*left++, &d);			/* stereoize */			*p++ = v;			*p++ = v>>8;			*p++ = v;			*p++ = v>>8;		}		break;	case 2:		left = pcm->samples[0];		right = pcm->samples[1];		for(i=0; i<n; i++){			v = audiodither(*left++, &d);			*p++ = v;			*p++ = v>>8;			v = audiodither(*right++, &d);			*p++ = v;			*p++ = v>>8;		}		break;	}	assert(p<=buf+sizeof buf);	write(1, buf, p-buf);	return MAD_FLOW_CONTINUE;} static enum mad_flowerror(void *data, struct mad_stream *stream, struct mad_frame *frame){	if(stream->error == MAD_ERROR_LOSTSYNC)		return MAD_FLOW_CONTINUE;	sysfatal("%s:#%lld: %s",		name, offset-(stream->bufend-stream->next_frame),		mad_stream_errorstr(stream));	return 0;} voidplay(int fd, char *nam){	struct mad_decoder decoder;		name = nam;	mad_decoder_init(&decoder, (void*)fd, input, nil, nil, output, error, nil);	mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);	mad_decoder_finish(&decoder);} voidusage(void){	fprint(2, "usage: mp3dec [-o outfile] [file...]\n");	exits("usage");} voidmain(int argc, char **argv){	int i, fd;	char *p; 	ARGBEGIN{	case 'o':		outfile = EARGF(usage());		break;	default:		usage();	}ARGEND		if(outfile){		if((fd = create(outfile, OWRITE, 0666)) < 0)			sysfatal("create %s: %r", outfile);	}else{		if((fd = open("/dev/audio", OWRITE)) < 0)			sysfatal("open /dev/audio: %r");		vfd = open("/dev/volume", OWRITE);	}	if(fd != 1){		dup(fd, 1);		close(fd);	} 	if(argc == 0){		play(0, "standard input");	}else{		for(i=0; i<argc; i++){			if((fd = open(argv[i], OREAD)) < 0)				sysfatal("open %s: %r", argv[i]);			play(fd, argv[i]);			close(fd);		}	}	exits(0);}