ref: b8136286b6044b81945983a4651fcf9fb70ddad0
dir: /sys/src/games/mp3dec/main.c/
/* * 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; int debug = 0; 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){ if(memcmp(stream->this_frame, "TAG", 3)==0){ mad_stream_skip(stream, 128); return MAD_FLOW_CONTINUE; } } if(debug) fprint(2, "%s:#%lld: %s\n", name, offset-(stream->bufend-stream->next_frame), mad_stream_errorstr(stream)); return MAD_FLOW_CONTINUE; } 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 [ -d ] [ -o outfile ] [ file ... ]\n"); exits("usage"); } void main(int argc, char **argv) { int i, fd; char *p; ARGBEGIN{ case 'o': outfile = EARGF(usage()); break; case 'd': debug++; 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); }