xref: /plan9/sys/src/games/mp3dec/main.c (revision f30ccc91ab9e7f92bd5dd82b1eebdeb503fd3465)
1*f30ccc91SDavid du Colombier /*
2*f30ccc91SDavid du Colombier  * Simple mp3 player.  Derived from libmad's example minimad.c.
3*f30ccc91SDavid du Colombier  */
4*f30ccc91SDavid du Colombier #include <u.h>
5*f30ccc91SDavid du Colombier #include <libc.h>
6*f30ccc91SDavid du Colombier #include "mad.h"
7*f30ccc91SDavid du Colombier 
8*f30ccc91SDavid du Colombier /* Current input file */
9*f30ccc91SDavid du Colombier char *name;
10*f30ccc91SDavid du Colombier vlong offset;
11*f30ccc91SDavid du Colombier int rate = 44100;
12*f30ccc91SDavid du Colombier 
13*f30ccc91SDavid du Colombier char *outfile;
14*f30ccc91SDavid du Colombier int vfd; /* /dev/volume */
15*f30ccc91SDavid du Colombier 
16*f30ccc91SDavid du Colombier static enum mad_flow
input(void * data,struct mad_stream * stream)17*f30ccc91SDavid du Colombier input(void *data, struct mad_stream *stream)
18*f30ccc91SDavid du Colombier {
19*f30ccc91SDavid du Colombier 	int fd, n, m;
20*f30ccc91SDavid du Colombier 	static uchar buf[32768];
21*f30ccc91SDavid du Colombier 
22*f30ccc91SDavid du Colombier 	fd = (int)data;
23*f30ccc91SDavid du Colombier 	n = stream->bufend - stream->next_frame;
24*f30ccc91SDavid du Colombier 	memmove(buf, stream->next_frame, n);
25*f30ccc91SDavid du Colombier 	m = read(fd, buf+n, sizeof buf-n);
26*f30ccc91SDavid du Colombier 	offset += m;
27*f30ccc91SDavid du Colombier 	if(m < 0)
28*f30ccc91SDavid du Colombier 		sysfatal("reading input: %r");
29*f30ccc91SDavid du Colombier 	if(m == 0)
30*f30ccc91SDavid du Colombier 		return MAD_FLOW_STOP;
31*f30ccc91SDavid du Colombier 	n += m;
32*f30ccc91SDavid du Colombier 	mad_stream_buffer(stream, buf, n);
33*f30ccc91SDavid du Colombier 	return MAD_FLOW_CONTINUE;
34*f30ccc91SDavid du Colombier }
35*f30ccc91SDavid du Colombier 
36*f30ccc91SDavid du Colombier /*
37*f30ccc91SDavid du Colombier  * Dither 28-bit down to 16-bit.  From mpg321.
38*f30ccc91SDavid du Colombier  * I'm skeptical, but it's supposed to make the
39*f30ccc91SDavid du Colombier  * samples sound better than just truncation.
40*f30ccc91SDavid du Colombier  */
41*f30ccc91SDavid du Colombier typedef struct Dither Dither;
42*f30ccc91SDavid du Colombier struct Dither
43*f30ccc91SDavid du Colombier {
44*f30ccc91SDavid du Colombier 	mad_fixed_t error[3];
45*f30ccc91SDavid du Colombier 	mad_fixed_t random;
46*f30ccc91SDavid du Colombier };
47*f30ccc91SDavid du Colombier 
48*f30ccc91SDavid du Colombier #define PRNG(x) (((x)*0x19660dL + 0x3c6ef35fL) & 0xffffffffL)
49*f30ccc91SDavid du Colombier 
50*f30ccc91SDavid du Colombier enum
51*f30ccc91SDavid du Colombier {
52*f30ccc91SDavid du Colombier 	FracBits = MAD_F_FRACBITS,
53*f30ccc91SDavid du Colombier 	OutBits = 16,
54*f30ccc91SDavid du Colombier 	Round = 1 << (FracBits+1-OutBits-1),	// sic
55*f30ccc91SDavid du Colombier 	ScaleBits = FracBits + 1 - OutBits,
56*f30ccc91SDavid du Colombier 	LowMask  = (1<<ScaleBits) - 1,
57*f30ccc91SDavid du Colombier 	Min = -MAD_F_ONE,
58*f30ccc91SDavid du Colombier 	Max = MAD_F_ONE - 1,
59*f30ccc91SDavid du Colombier };
60*f30ccc91SDavid du Colombier 
61*f30ccc91SDavid du Colombier int
audiodither(mad_fixed_t v,Dither * d)62*f30ccc91SDavid du Colombier audiodither(mad_fixed_t v, Dither *d)
63*f30ccc91SDavid du Colombier {
64*f30ccc91SDavid du Colombier 	int out;
65*f30ccc91SDavid du Colombier 	mad_fixed_t random;
66*f30ccc91SDavid du Colombier 
67*f30ccc91SDavid du Colombier 	/* noise shape */
68*f30ccc91SDavid du Colombier 	v += d->error[0] - d->error[1] + d->error[2];
69*f30ccc91SDavid du Colombier 	d->error[2] = d->error[1];
70*f30ccc91SDavid du Colombier 	d->error[1] = d->error[0] / 2;
71*f30ccc91SDavid du Colombier 
72*f30ccc91SDavid du Colombier 	/* bias */
73*f30ccc91SDavid du Colombier 	out = v + Round;
74*f30ccc91SDavid du Colombier 
75*f30ccc91SDavid du Colombier 	/* dither */
76*f30ccc91SDavid du Colombier 	random = PRNG(d->random);
77*f30ccc91SDavid du Colombier 	out += (random & LowMask) - (d->random & LowMask);
78*f30ccc91SDavid du Colombier 	d->random = random;
79*f30ccc91SDavid du Colombier 
80*f30ccc91SDavid du Colombier 	/* clip */
81*f30ccc91SDavid du Colombier 	if(out > Max){
82*f30ccc91SDavid du Colombier 		out = Max;
83*f30ccc91SDavid du Colombier 		if(v > Max)
84*f30ccc91SDavid du Colombier 			v = Max;
85*f30ccc91SDavid du Colombier 	}else if(out < Min){
86*f30ccc91SDavid du Colombier 		out = Min;
87*f30ccc91SDavid du Colombier 		if(v < Min)
88*f30ccc91SDavid du Colombier 			v = Min;
89*f30ccc91SDavid du Colombier 	}
90*f30ccc91SDavid du Colombier 
91*f30ccc91SDavid du Colombier 	/* quantize */
92*f30ccc91SDavid du Colombier 	out &= ~LowMask;
93*f30ccc91SDavid du Colombier 
94*f30ccc91SDavid du Colombier 	/* error feedback */
95*f30ccc91SDavid du Colombier 	d->error[0] = v - out;
96*f30ccc91SDavid du Colombier 
97*f30ccc91SDavid du Colombier 	/* scale */
98*f30ccc91SDavid du Colombier 	return out >> ScaleBits;
99*f30ccc91SDavid du Colombier }
100*f30ccc91SDavid du Colombier 
101*f30ccc91SDavid du Colombier static enum mad_flow
output(void * data,struct mad_header const * header,struct mad_pcm * pcm)102*f30ccc91SDavid du Colombier output(void *data, struct mad_header const* header, struct mad_pcm *pcm)
103*f30ccc91SDavid du Colombier {
104*f30ccc91SDavid du Colombier 	int i, n, v;
105*f30ccc91SDavid du Colombier 	mad_fixed_t const *left, *right;
106*f30ccc91SDavid du Colombier 	static Dither d;
107*f30ccc91SDavid du Colombier 	static uchar buf[16384], *p;
108*f30ccc91SDavid du Colombier 
109*f30ccc91SDavid du Colombier 	if(pcm->samplerate != rate){
110*f30ccc91SDavid du Colombier 		rate = pcm->samplerate;
111*f30ccc91SDavid du Colombier 		if(vfd < 0)
112*f30ccc91SDavid du Colombier 			fprint(2, "warning: audio sample rate is %d Hz\n", rate);
113*f30ccc91SDavid du Colombier 		else
114*f30ccc91SDavid du Colombier 			fprint(vfd, "speed %d", rate);
115*f30ccc91SDavid du Colombier 	}
116*f30ccc91SDavid du Colombier 	p = buf;
117*f30ccc91SDavid du Colombier 	memset(&d, 0, sizeof d);
118*f30ccc91SDavid du Colombier 	n = pcm->length;
119*f30ccc91SDavid du Colombier 	switch(pcm->channels){
120*f30ccc91SDavid du Colombier 	case 1:
121*f30ccc91SDavid du Colombier 		left = pcm->samples[0];
122*f30ccc91SDavid du Colombier 		for(i=0; i<n; i++){
123*f30ccc91SDavid du Colombier 			v = audiodither(*left++, &d);
124*f30ccc91SDavid du Colombier 			/* stereoize */
125*f30ccc91SDavid du Colombier 			*p++ = v;
126*f30ccc91SDavid du Colombier 			*p++ = v>>8;
127*f30ccc91SDavid du Colombier 			*p++ = v;
128*f30ccc91SDavid du Colombier 			*p++ = v>>8;
129*f30ccc91SDavid du Colombier 		}
130*f30ccc91SDavid du Colombier 		break;
131*f30ccc91SDavid du Colombier 	case 2:
132*f30ccc91SDavid du Colombier 		left = pcm->samples[0];
133*f30ccc91SDavid du Colombier 		right = pcm->samples[1];
134*f30ccc91SDavid du Colombier 		for(i=0; i<n; i++){
135*f30ccc91SDavid du Colombier 			v = audiodither(*left++, &d);
136*f30ccc91SDavid du Colombier 			*p++ = v;
137*f30ccc91SDavid du Colombier 			*p++ = v>>8;
138*f30ccc91SDavid du Colombier 			v = audiodither(*right++, &d);
139*f30ccc91SDavid du Colombier 			*p++ = v;
140*f30ccc91SDavid du Colombier 			*p++ = v>>8;
141*f30ccc91SDavid du Colombier 		}
142*f30ccc91SDavid du Colombier 		break;
143*f30ccc91SDavid du Colombier 	}
144*f30ccc91SDavid du Colombier 	assert(p<=buf+sizeof buf);
145*f30ccc91SDavid du Colombier 	write(1, buf, p-buf);
146*f30ccc91SDavid du Colombier 	return MAD_FLOW_CONTINUE;
147*f30ccc91SDavid du Colombier }
148*f30ccc91SDavid du Colombier 
149*f30ccc91SDavid du Colombier static enum mad_flow
error(void * data,struct mad_stream * stream,struct mad_frame * frame)150*f30ccc91SDavid du Colombier error(void *data, struct mad_stream *stream, struct mad_frame *frame)
151*f30ccc91SDavid du Colombier {
152*f30ccc91SDavid du Colombier 	if(stream->error == MAD_ERROR_LOSTSYNC)
153*f30ccc91SDavid du Colombier 		return MAD_FLOW_CONTINUE;
154*f30ccc91SDavid du Colombier 	sysfatal("%s:#%lld: %s",
155*f30ccc91SDavid du Colombier 		name, offset-(stream->bufend-stream->next_frame),
156*f30ccc91SDavid du Colombier 		mad_stream_errorstr(stream));
157*f30ccc91SDavid du Colombier 	return 0;
158*f30ccc91SDavid du Colombier }
159*f30ccc91SDavid du Colombier 
160*f30ccc91SDavid du Colombier void
play(int fd,char * nam)161*f30ccc91SDavid du Colombier play(int fd, char *nam)
162*f30ccc91SDavid du Colombier {
163*f30ccc91SDavid du Colombier 	struct mad_decoder decoder;
164*f30ccc91SDavid du Colombier 
165*f30ccc91SDavid du Colombier 	name = nam;
166*f30ccc91SDavid du Colombier 	mad_decoder_init(&decoder, (void*)fd, input, nil, nil, output, error, nil);
167*f30ccc91SDavid du Colombier 	mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
168*f30ccc91SDavid du Colombier 	mad_decoder_finish(&decoder);
169*f30ccc91SDavid du Colombier }
170*f30ccc91SDavid du Colombier 
171*f30ccc91SDavid du Colombier void
usage(void)172*f30ccc91SDavid du Colombier usage(void)
173*f30ccc91SDavid du Colombier {
174*f30ccc91SDavid du Colombier 	fprint(2, "usage: mp3dec [-o outfile] [file...]\n");
175*f30ccc91SDavid du Colombier 	exits("usage");
176*f30ccc91SDavid du Colombier }
177*f30ccc91SDavid du Colombier 
178*f30ccc91SDavid du Colombier void
main(int argc,char ** argv)179*f30ccc91SDavid du Colombier main(int argc, char **argv)
180*f30ccc91SDavid du Colombier {
181*f30ccc91SDavid du Colombier 	int i, fd;
182*f30ccc91SDavid du Colombier 	char *p;
183*f30ccc91SDavid du Colombier 
184*f30ccc91SDavid du Colombier 	ARGBEGIN{
185*f30ccc91SDavid du Colombier 	case 'o':
186*f30ccc91SDavid du Colombier 		outfile = EARGF(usage());
187*f30ccc91SDavid du Colombier 		break;
188*f30ccc91SDavid du Colombier 	default:
189*f30ccc91SDavid du Colombier 		usage();
190*f30ccc91SDavid du Colombier 	}ARGEND
191*f30ccc91SDavid du Colombier 
192*f30ccc91SDavid du Colombier 	if(outfile){
193*f30ccc91SDavid du Colombier 		if((fd = create(outfile, OWRITE, 0666)) < 0)
194*f30ccc91SDavid du Colombier 			sysfatal("create %s: %r", outfile);
195*f30ccc91SDavid du Colombier 	}else{
196*f30ccc91SDavid du Colombier 		if((fd = open("/dev/audio", OWRITE)) < 0)
197*f30ccc91SDavid du Colombier 			sysfatal("open /dev/audio: %r");
198*f30ccc91SDavid du Colombier 		vfd = open("/dev/volume", OWRITE);
199*f30ccc91SDavid du Colombier 	}
200*f30ccc91SDavid du Colombier 	if(fd != 1){
201*f30ccc91SDavid du Colombier 		dup(fd, 1);
202*f30ccc91SDavid du Colombier 		close(fd);
203*f30ccc91SDavid du Colombier 	}
204*f30ccc91SDavid du Colombier 
205*f30ccc91SDavid du Colombier 	if(argc == 0){
206*f30ccc91SDavid du Colombier 		play(0, "standard input");
207*f30ccc91SDavid du Colombier 	}else{
208*f30ccc91SDavid du Colombier 		for(i=0; i<argc; i++){
209*f30ccc91SDavid du Colombier 			if((fd = open(argv[i], OREAD)) < 0)
210*f30ccc91SDavid du Colombier 				sysfatal("open %s: %r", argv[i]);
211*f30ccc91SDavid du Colombier 			play(fd, argv[i]);
212*f30ccc91SDavid du Colombier 			close(fd);
213*f30ccc91SDavid du Colombier 		}
214*f30ccc91SDavid du Colombier 	}
215*f30ccc91SDavid du Colombier 	exits(0);
216*f30ccc91SDavid du Colombier }
217