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