ref: b70a7fc963cd58445c55b1d1702e151a39e3fc07
parent: 173a01ef8b43690f2f3f02d75d03d83436d8022d
author: cinap_lenrek <[email protected]>
date: Wed Jan 25 20:43:17 EST 2012
mp3dec: resample to target frequency, play: guess mp3 from filename
--- a/rc/bin/play
+++ b/rc/bin/play
@@ -32,13 +32,16 @@
case *plain*
sed 's/ //g' | while(j=`{read}){
echo $"j >[1=2]
+ t=$typ
if(~ $"j http:* https:* HTTP:* HTTPS:*){
- if(~ $#typ 0) typ=mp3
- hget -r 'Icy-MetaData: 0' $j | play1 $typ
+ if(~ $#t 0) t=mp3
+ hget -r 'Icy-MetaData: 0' $j | play1 $t
}
if not {
- if(test -r $"j)
- play1 <$"j
+ if(test -r $"j){
+ if(~ $#t 0 && ~ $"j *mp3*) t=mp3
+ play1 $t <$"j
+ }
if not {
echo $argv0: 'can''t open file:' $"j >[1=2]
}
--- a/sys/src/cmd/audio/mp3dec/main.c
+++ b/sys/src/cmd/audio/mp3dec/main.c
@@ -29,16 +29,12 @@
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
+typedef struct Chan Chan;
+struct Chan
{
- mad_fixed_t error[3];
- mad_fixed_t random;
+ ulong phase;
+ mad_fixed_t last;
+ mad_fixed_t rand;
};
#define PRNG(x) (((x)*0x19660dL + 0x3c6ef35fL) & 0xffffffffL)
@@ -47,7 +43,6 @@
{
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,
@@ -54,87 +49,78 @@
Max = MAD_F_ONE - 1,
};
-int
-audiodither(mad_fixed_t v, Dither *d)
+static uchar*
+resample(Chan *c, mad_fixed_t *src, uchar *dst, int mono, ulong delta, ulong count)
{
- int out;
- mad_fixed_t random;
+ mad_fixed_t last, val, out, rand;
+ ulong phase, pos;
+ vlong v;
- /* 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;
+ rand = c->rand;
+ last = c->last;
+ phase = c->phase;
+ pos = phase >> 16;
+ while(pos < count){
+ val = src[pos];
+ if(pos)
+ last = src[pos-1];
+
+ /* interpolate */
+ v = val;
+ v -= last;
+ v *= (phase & 0xFFFF);
+ out = last + (v >> 16);
+
+ /* dithering */
+ out += (rand & LowMask) - LowMask/2;
+ rand = PRNG(rand);
+
+ /* cliping */
+ if(out > Max)
+ out = Max;
+ else if(out < Min)
+ out = Min;
+
+ out >>= ScaleBits;
+
+ *dst++ = out;
+ *dst++ = out >> 8;
+ if(mono){
+ *dst++ = out;
+ *dst++ = out >> 8;
+ } else
+ dst += 2;
+ phase += delta;
+ pos = phase >> 16;
}
-
- /* quantize */
- out &= ~LowMask;
-
- /* error feedback */
- d->error[0] = v - out;
-
- /* scale */
- return out >> ScaleBits;
+ c->rand = rand;
+ c->last = val;
+ if(delta < 0x10000)
+ c->phase = phase & 0xFFFF;
+ else
+ c->phase = phase - (count << 16);
+
+ return dst;
}
static enum mad_flow
output(void *, 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;
+ static uchar *buf;
+ static int nbuf;
+ static Chan c1, c0;
+ ulong n, delta;
+ uchar *p;
- if(pcm->samplerate != rate){
- rate = pcm->samplerate;
- fprint(2, "warning: audio sample rate is %d Hz\n", rate);
+ delta = (pcm->samplerate << 16) / rate;
+ n = 4 * (pcm->samplerate + pcm->length * rate) / pcm->samplerate;
+ if(n > nbuf){
+ nbuf = n;
+ buf = realloc(buf, nbuf);
}
- 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);
+ if(pcm->channels == 2)
+ resample(&c1, pcm->samples[1], buf+2, 0, delta, pcm->length);
+ p = resample(&c0, pcm->samples[0], buf, pcm->channels == 1, delta, pcm->length);
write(1, buf, p-buf);
return MAD_FLOW_CONTINUE;
}