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