123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- /*
- * 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_flow
- input(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,
- };
- int
- audiodither(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_flow
- output(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_flow
- error(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;
- }
- void
- play(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);
- }
- void
- usage(void)
- {
- fprint(2, "usage: mp3dec [-o outfile] [file...]\n");
- exits("usage");
- }
- void
- main(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);
- }
|