mp3dec: resample to target frequency, play: guess mp3 from filename

This commit is contained in:
cinap_lenrek 2012-01-26 01:43:17 +01:00
parent 173a01ef8b
commit b70a7fc963
2 changed files with 74 additions and 85 deletions

View file

@ -32,13 +32,16 @@ fn play1 {
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]
}

View file

@ -29,16 +29,12 @@ input(void *, struct mad_stream *stream)
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,94 +43,84 @@ 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)
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;
}