Alibek Omarov
7 years ago
24 changed files with 8520 additions and 0 deletions
@ -0,0 +1,321 @@
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
This is an optimized DCT from Jeff Tsay's maplay 1.2+ package. |
||||
Saved one multiplication by doing the 'twiddle factor' stuff |
||||
together with the window mul. (MH) |
||||
|
||||
This uses Byeong Gi Lee's Fast Cosine Transform algorithm, but the |
||||
9 point IDCT needs to be reduced further. Unfortunately, I don't |
||||
know how to do that, because 9 is not an even number. - Jeff. |
||||
|
||||
Original Message: |
||||
|
||||
9 Point Inverse Discrete Cosine Transform |
||||
|
||||
This piece of code is Copyright 1997 Mikko Tommila and is freely usable |
||||
by anybody. The algorithm itself is of course in the public domain. |
||||
|
||||
Again derived heuristically from the 9-point WFTA. |
||||
|
||||
The algorithm is optimized (?) for speed, not for small rounding errors or |
||||
good readability. |
||||
|
||||
36 additions, 11 multiplications |
||||
|
||||
Again this is very likely sub-optimal. |
||||
|
||||
The code is optimized to use a minimum number of temporary variables, |
||||
so it should compile quite well even on 8-register Intel x86 processors. |
||||
This makes the code quite obfuscated and very difficult to understand. |
||||
|
||||
References: |
||||
[1] S. Winograd: "On Computing the Discrete Fourier Transform", |
||||
Mathematics of Computation, Volume 32, Number 141, January 1978, |
||||
Pages 175-199 |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include <math.h> |
||||
|
||||
#define MACRO(v) { \ |
||||
float tmpval; \ |
||||
tmpval = tmp[(v)] + tmp[17-(v)]; \ |
||||
out2[9+(v)] = REAL_MUL(tmpval, w[27+(v)]); \ |
||||
out2[8-(v)] = REAL_MUL(tmpval, w[26-(v)]); \ |
||||
tmpval = tmp[(v)] - tmp[17-(v)]; \ |
||||
ts[SBLIMIT*(8-(v))] = out1[8-(v)] + REAL_MUL(tmpval, w[8-(v)]); \ |
||||
ts[SBLIMIT*(9+(v))] = out1[9+(v)] + REAL_MUL(tmpval, w[9+(v)]); } |
||||
|
||||
#define DCT12_PART1 \ |
||||
in5 = in[5*3]; \ |
||||
in5 += (in4 = in[4*3]); \ |
||||
in4 += (in3 = in[3*3]); \ |
||||
in3 += (in2 = in[2*3]); \ |
||||
in2 += (in1 = in[1*3]); \ |
||||
in1 += (in0 = in[0*3]); \ |
||||
\ |
||||
in5 += in3; in3 += in1; \ |
||||
\ |
||||
in2 = REAL_MUL(in2, COS6_1); \ |
||||
in3 = REAL_MUL(in3, COS6_1); |
||||
|
||||
#define DCT12_PART2 \ |
||||
in0 += REAL_MUL(in4, COS6_2); \ |
||||
\ |
||||
in4 = in0 + in2; \ |
||||
in0 -= in2; \ |
||||
\ |
||||
in1 += REAL_MUL(in5, COS6_2); \ |
||||
\ |
||||
in5 = REAL_MUL((in1 + in3), tfcos12[0]); \ |
||||
in1 = REAL_MUL((in1 - in3), tfcos12[2]); \ |
||||
\ |
||||
in3 = in4 + in5; \ |
||||
in4 -= in5; \ |
||||
\ |
||||
in2 = in0 + in1; \ |
||||
in0 -= in1; |
||||
|
||||
// calculation of the inverse MDCT
|
||||
// used to be static without 3dnow - does that floatly matter?
|
||||
void dct36( float *inbuf, float *o1, float *o2, float *wintab, float *tsbuf ) |
||||
{ |
||||
float tmp[18]; |
||||
|
||||
{ |
||||
register float *in = inbuf; |
||||
|
||||
in[17] += in[16]; in[16] += in[15]; in[15] += in[14]; |
||||
in[14] += in[13]; in[13] += in[12]; in[12] += in[11]; |
||||
in[11] += in[10]; in[10] += in[9]; in[9] += in[8]; |
||||
in[8] += in[7]; in[7] += in[6]; in[6] += in[5]; |
||||
in[5] += in[4]; in[4] += in[3]; in[3] += in[2]; |
||||
in[2] += in[1]; in[1] += in[0]; |
||||
|
||||
in[17] += in[15]; in[15] += in[13]; in[13] += in[11]; in[11] += in[9]; |
||||
in[9] += in[7]; in[7] += in[5]; in[5] += in[3]; in[3] += in[1]; |
||||
|
||||
{ |
||||
float t3; |
||||
{ |
||||
float t0, t1, t2; |
||||
|
||||
t0 = REAL_MUL(COS6_2, (in[8] + in[16] - in[4])); |
||||
t1 = REAL_MUL(COS6_2, in[12]); |
||||
|
||||
t3 = in[0]; |
||||
t2 = t3 - t1 - t1; |
||||
tmp[1] = tmp[7] = t2 - t0; |
||||
tmp[4] = t2 + t0 + t0; |
||||
t3 += t1; |
||||
|
||||
t2 = REAL_MUL(COS6_1, (in[10] + in[14] - in[2])); |
||||
tmp[1] -= t2; |
||||
tmp[7] += t2; |
||||
} |
||||
{ |
||||
float t0, t1, t2; |
||||
|
||||
t0 = REAL_MUL(cos9[0], (in[4] + in[8] )); |
||||
t1 = REAL_MUL(cos9[1], (in[8] - in[16])); |
||||
t2 = REAL_MUL(cos9[2], (in[4] + in[16])); |
||||
|
||||
tmp[2] = tmp[6] = t3 - t0 - t2; |
||||
tmp[0] = tmp[8] = t3 + t0 + t1; |
||||
tmp[3] = tmp[5] = t3 - t1 + t2; |
||||
} |
||||
} |
||||
{ |
||||
float t1, t2, t3; |
||||
|
||||
t1 = REAL_MUL(cos18[0], (in[2] + in[10])); |
||||
t2 = REAL_MUL(cos18[1], (in[10] - in[14])); |
||||
t3 = REAL_MUL(COS6_1, in[6]); |
||||
|
||||
{ |
||||
float t0 = t1 + t2 + t3; |
||||
tmp[0] += t0; |
||||
tmp[8] -= t0; |
||||
} |
||||
|
||||
t2 -= t3; |
||||
t1 -= t3; |
||||
|
||||
t3 = REAL_MUL(cos18[2], (in[2] + in[14])); |
||||
|
||||
t1 += t3; |
||||
tmp[3] += t1; |
||||
tmp[5] -= t1; |
||||
|
||||
t2 -= t3; |
||||
tmp[2] += t2; |
||||
tmp[6] -= t2; |
||||
} |
||||
{ |
||||
float t0, t1, t2, t3, t4, t5, t6, t7; |
||||
|
||||
t1 = REAL_MUL(COS6_2, in[13]); |
||||
t2 = REAL_MUL(COS6_2, (in[9] + in[17] - in[5])); |
||||
|
||||
t3 = in[1] + t1; |
||||
t4 = in[1] - t1 - t1; |
||||
t5 = t4 - t2; |
||||
|
||||
t0 = REAL_MUL(cos9[0], (in[5] + in[9])); |
||||
t1 = REAL_MUL(cos9[1], (in[9] - in[17])); |
||||
|
||||
tmp[13] = REAL_MUL((t4 + t2 + t2), tfcos36[17-13]); |
||||
t2 = REAL_MUL(cos9[2], (in[5] + in[17])); |
||||
|
||||
t6 = t3 - t0 - t2; |
||||
t0 += t3 + t1; |
||||
t3 += t2 - t1; |
||||
|
||||
t2 = REAL_MUL(cos18[0], (in[3] + in[11])); |
||||
t4 = REAL_MUL(cos18[1], (in[11] - in[15])); |
||||
t7 = REAL_MUL(COS6_1, in[7]); |
||||
|
||||
t1 = t2 + t4 + t7; |
||||
tmp[17] = REAL_MUL((t0 + t1), tfcos36[17-17]); |
||||
tmp[9] = REAL_MUL((t0 - t1), tfcos36[17-9]); |
||||
t1 = REAL_MUL(cos18[2], (in[3] + in[15])); |
||||
t2 += t1 - t7; |
||||
|
||||
tmp[14] = REAL_MUL((t3 + t2), tfcos36[17-14]); |
||||
t0 = REAL_MUL(COS6_1, (in[11] + in[15] - in[3])); |
||||
tmp[12] = REAL_MUL((t3 - t2), tfcos36[17-12]); |
||||
|
||||
t4 -= t1 + t7; |
||||
|
||||
tmp[16] = REAL_MUL((t5 - t0), tfcos36[17-16]); |
||||
tmp[10] = REAL_MUL((t5 + t0), tfcos36[17-10]); |
||||
tmp[15] = REAL_MUL((t6 + t4), tfcos36[17-15]); |
||||
tmp[11] = REAL_MUL((t6 - t4), tfcos36[17-11]); |
||||
} |
||||
|
||||
|
||||
|
||||
{ |
||||
register float *out2 = o2; |
||||
register float *w = wintab; |
||||
register float *out1 = o1; |
||||
register float *ts = tsbuf; |
||||
|
||||
MACRO(0); |
||||
MACRO(1); |
||||
MACRO(2); |
||||
MACRO(3); |
||||
MACRO(4); |
||||
MACRO(5); |
||||
MACRO(6); |
||||
MACRO(7); |
||||
MACRO(8); |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
void dct12( float *in, float *rawout1, float *rawout2, register float *wi, register float *ts ) |
||||
{ |
||||
{ |
||||
float in0,in1,in2,in3,in4,in5; |
||||
register float *out1 = rawout1; |
||||
ts[SBLIMIT*0] = out1[0]; ts[SBLIMIT*1] = out1[1]; ts[SBLIMIT*2] = out1[2]; |
||||
ts[SBLIMIT*3] = out1[3]; ts[SBLIMIT*4] = out1[4]; ts[SBLIMIT*5] = out1[5]; |
||||
|
||||
DCT12_PART1 |
||||
|
||||
{ |
||||
float tmp0,tmp1 = (in0 - in4); |
||||
{ |
||||
float tmp2 = REAL_MUL((in1 - in5), tfcos12[1]); |
||||
tmp0 = tmp1 + tmp2; |
||||
tmp1 -= tmp2; |
||||
} |
||||
ts[(17-1)*SBLIMIT] = out1[17-1] + REAL_MUL(tmp0, wi[11-1]); |
||||
ts[(12+1)*SBLIMIT] = out1[12+1] + REAL_MUL(tmp0, wi[6+1]); |
||||
ts[(6 +1)*SBLIMIT] = out1[6 +1] + REAL_MUL(tmp1, wi[1]); |
||||
ts[(11-1)*SBLIMIT] = out1[11-1] + REAL_MUL(tmp1, wi[5-1]); |
||||
} |
||||
|
||||
DCT12_PART2 |
||||
|
||||
ts[(17-0)*SBLIMIT] = out1[17-0] + REAL_MUL(in2, wi[11-0]); |
||||
ts[(12+0)*SBLIMIT] = out1[12+0] + REAL_MUL(in2, wi[6+0]); |
||||
ts[(12+2)*SBLIMIT] = out1[12+2] + REAL_MUL(in3, wi[6+2]); |
||||
ts[(17-2)*SBLIMIT] = out1[17-2] + REAL_MUL(in3, wi[11-2]); |
||||
|
||||
ts[(6 +0)*SBLIMIT] = out1[6+0] + REAL_MUL(in0, wi[0]); |
||||
ts[(11-0)*SBLIMIT] = out1[11-0] + REAL_MUL(in0, wi[5-0]); |
||||
ts[(6 +2)*SBLIMIT] = out1[6+2] + REAL_MUL(in4, wi[2]); |
||||
ts[(11-2)*SBLIMIT] = out1[11-2] + REAL_MUL(in4, wi[5-2]); |
||||
} |
||||
|
||||
in++; |
||||
|
||||
{ |
||||
float in0,in1,in2,in3,in4,in5; |
||||
register float *out2 = rawout2; |
||||
|
||||
DCT12_PART1 |
||||
|
||||
{ |
||||
float tmp0,tmp1 = (in0 - in4); |
||||
{ |
||||
float tmp2 = REAL_MUL((in1 - in5), tfcos12[1]); |
||||
tmp0 = tmp1 + tmp2; |
||||
tmp1 -= tmp2; |
||||
} |
||||
out2[5-1] = REAL_MUL(tmp0, wi[11-1]); |
||||
out2[0+1] = REAL_MUL(tmp0, wi[6+1]); |
||||
ts[(12+1)*SBLIMIT] += REAL_MUL(tmp1, wi[1]); |
||||
ts[(17-1)*SBLIMIT] += REAL_MUL(tmp1, wi[5-1]); |
||||
} |
||||
|
||||
DCT12_PART2 |
||||
|
||||
out2[5-0] = REAL_MUL(in2, wi[11-0]); |
||||
out2[0+0] = REAL_MUL(in2, wi[6+0]); |
||||
out2[0+2] = REAL_MUL(in3, wi[6+2]); |
||||
out2[5-2] = REAL_MUL(in3, wi[11-2]); |
||||
|
||||
ts[(12+0)*SBLIMIT] += REAL_MUL(in0, wi[0]); |
||||
ts[(17-0)*SBLIMIT] += REAL_MUL(in0, wi[5-0]); |
||||
ts[(12+2)*SBLIMIT] += REAL_MUL(in4, wi[2]); |
||||
ts[(17-2)*SBLIMIT] += REAL_MUL(in4, wi[5-2]); |
||||
} |
||||
|
||||
in++; |
||||
|
||||
{ |
||||
float in0,in1,in2,in3,in4,in5; |
||||
register float *out2 = rawout2; |
||||
out2[12]=out2[13]=out2[14]=out2[15]=out2[16]=out2[17]=0.0; |
||||
|
||||
DCT12_PART1 |
||||
|
||||
{ |
||||
float tmp0,tmp1 = (in0 - in4); |
||||
{ |
||||
float tmp2 = REAL_MUL((in1 - in5), tfcos12[1]); |
||||
tmp0 = tmp1 + tmp2; |
||||
tmp1 -= tmp2; |
||||
} |
||||
out2[11-1] = REAL_MUL(tmp0, wi[11-1]); |
||||
out2[6 +1] = REAL_MUL(tmp0, wi[6+1]); |
||||
out2[0+1] += REAL_MUL(tmp1, wi[1]); |
||||
out2[5-1] += REAL_MUL(tmp1, wi[5-1]); |
||||
} |
||||
|
||||
DCT12_PART2 |
||||
|
||||
out2[11-0] = REAL_MUL(in2, wi[11-0]); |
||||
out2[6 +0] = REAL_MUL(in2, wi[6+0]); |
||||
out2[6 +2] = REAL_MUL(in3, wi[6+2]); |
||||
out2[11-2] = REAL_MUL(in3, wi[11-2]); |
||||
|
||||
out2[0+0] += REAL_MUL(in0, wi[0]); |
||||
out2[5-0] += REAL_MUL(in0, wi[5-0]); |
||||
out2[0+2] += REAL_MUL(in4, wi[2]); |
||||
out2[5-2] += REAL_MUL(in4, wi[5-2]); |
||||
} |
||||
} |
@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
dct64.c: DCT64, the plain C version |
||||
|
||||
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1 |
||||
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
||||
initially written by Michael Hipp |
||||
*/ |
||||
|
||||
/*
|
||||
* Discrete Cosine Transform (DCT) for subband synthesis |
||||
* |
||||
* -funroll-loops (for gcc) will remove the loops for better performance |
||||
* using loops in the source-code enhances readabillity |
||||
* |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
|
||||
void dct64( float *out0, float *out1, float *samples ) |
||||
{ |
||||
float bufs[64]; |
||||
|
||||
{ |
||||
register float *b1, *b2, *bs; |
||||
register float *costab; |
||||
register int i, j; |
||||
|
||||
b1 = samples; |
||||
bs = bufs; |
||||
costab = pnts[0]+16; |
||||
b2 = b1 + 32; |
||||
|
||||
for( i = 15; i >= 0; i-- ) |
||||
*bs++ = (*b1++ + *--b2); |
||||
|
||||
for( i = 15; i >= 0; i-- ) |
||||
*bs++ = REAL_MUL((*--b2 - *b1++), *--costab); |
||||
|
||||
b1 = bufs; |
||||
costab = pnts[1] + 8; |
||||
b2 = b1 + 16; |
||||
|
||||
{ |
||||
for( i = 7; i >= 0; i-- ) |
||||
*bs++ = (*b1++ + *--b2); |
||||
|
||||
for( i = 7; i >= 0; i-- ) |
||||
*bs++ = REAL_MUL((*--b2 - *b1++), *--costab); |
||||
b2 += 32; |
||||
costab += 8; |
||||
|
||||
for( i = 7; i >= 0; i-- ) |
||||
*bs++ = (*b1++ + *--b2); |
||||
for( i = 7; i >= 0; i-- ) |
||||
*bs++ = REAL_MUL((*b1++ - *--b2), *--costab); |
||||
b2 += 32; |
||||
} |
||||
|
||||
bs = bufs; |
||||
costab = pnts[2]; |
||||
b2 = b1 + 8; |
||||
|
||||
for( j = 2; j; j-- ) |
||||
{ |
||||
for( i = 3; i >= 0; i-- ) |
||||
*bs++ = (*b1++ + *--b2); |
||||
for( i = 3;i >= 0; i-- ) |
||||
*bs++ = REAL_MUL((*--b2 - *b1++), costab[i]); |
||||
b2 += 16; |
||||
|
||||
for( i = 3; i >= 0; i-- ) |
||||
*bs++ = (*b1++ + *--b2); |
||||
for( i = 3;i >= 0; i-- ) |
||||
*bs++ = REAL_MUL((*b1++ - *--b2), costab[i]); |
||||
b2 += 16; |
||||
} |
||||
|
||||
b1 = bufs; |
||||
costab = pnts[3]; |
||||
b2 = b1 + 4; |
||||
|
||||
for( j = 4; j; j-- ) |
||||
{ |
||||
*bs++ = (*b1++ + *--b2); |
||||
*bs++ = (*b1++ + *--b2); |
||||
*bs++ = REAL_MUL((*--b2 - *b1++), costab[1]); |
||||
*bs++ = REAL_MUL((*--b2 - *b1++), costab[0]); |
||||
b2 += 8; |
||||
|
||||
*bs++ = (*b1++ + *--b2); |
||||
*bs++ = (*b1++ + *--b2); |
||||
*bs++ = REAL_MUL((*b1++ - *--b2), costab[1]); |
||||
*bs++ = REAL_MUL((*b1++ - *--b2), costab[0]); |
||||
b2 += 8; |
||||
} |
||||
|
||||
bs = bufs; |
||||
costab = pnts[4]; |
||||
|
||||
for( j = 8; j; j-- ) |
||||
{ |
||||
float v0, v1; |
||||
|
||||
v0 = *b1++; |
||||
v1 = *b1++; |
||||
*bs++ = (v0 + v1); |
||||
*bs++ = REAL_MUL((v0 - v1), (*costab)); |
||||
v0 = *b1++; |
||||
v1 = *b1++; |
||||
*bs++ = (v0 + v1); |
||||
*bs++ = REAL_MUL((v1 - v0), (*costab)); |
||||
} |
||||
} |
||||
|
||||
{ |
||||
register float *b1; |
||||
register int i; |
||||
|
||||
for( b1 =bufs, i = 8; i; i--, b1 += 4 ) |
||||
b1[2] += b1[3]; |
||||
|
||||
for( b1 = bufs, i = 4; i; i--, b1 += 8 ) |
||||
{ |
||||
b1[4] += b1[6]; |
||||
b1[6] += b1[5]; |
||||
b1[5] += b1[7]; |
||||
} |
||||
|
||||
for( b1 = bufs, i = 2; i; i--, b1 += 16 ) |
||||
{ |
||||
b1[8] += b1[12]; |
||||
b1[12] += b1[10]; |
||||
b1[10] += b1[14]; |
||||
b1[14] += b1[9]; |
||||
b1[9] += b1[13]; |
||||
b1[13] += b1[11]; |
||||
b1[11] += b1[15]; |
||||
} |
||||
} |
||||
|
||||
out0[0x10*16] = REAL_SCALE_DCT64( bufs[0] ); |
||||
out0[0x10*15] = REAL_SCALE_DCT64( bufs[16+0] + bufs[16+8] ); |
||||
out0[0x10*14] = REAL_SCALE_DCT64( bufs[8] ); |
||||
out0[0x10*13] = REAL_SCALE_DCT64( bufs[16+8] + bufs[16+4] ); |
||||
out0[0x10*12] = REAL_SCALE_DCT64( bufs[4] ); |
||||
out0[0x10*11] = REAL_SCALE_DCT64( bufs[16+4] + bufs[16+12] ); |
||||
out0[0x10*10] = REAL_SCALE_DCT64( bufs[12] ); |
||||
out0[0x10* 9] = REAL_SCALE_DCT64( bufs[16+12] + bufs[16+2] ); |
||||
out0[0x10* 8] = REAL_SCALE_DCT64( bufs[2] ); |
||||
out0[0x10* 7] = REAL_SCALE_DCT64( bufs[16+2] + bufs[16+10] ); |
||||
out0[0x10* 6] = REAL_SCALE_DCT64( bufs[10] ); |
||||
out0[0x10* 5] = REAL_SCALE_DCT64( bufs[16+10] + bufs[16+6] ); |
||||
out0[0x10* 4] = REAL_SCALE_DCT64( bufs[6] ); |
||||
out0[0x10* 3] = REAL_SCALE_DCT64( bufs[16+6] + bufs[16+14] ); |
||||
out0[0x10* 2] = REAL_SCALE_DCT64( bufs[14] ); |
||||
out0[0x10* 1] = REAL_SCALE_DCT64( bufs[16+14] + bufs[16+1] ); |
||||
out0[0x10* 0] = REAL_SCALE_DCT64( bufs[1] ); |
||||
|
||||
out1[0x10* 0] = REAL_SCALE_DCT64( bufs[1] ); |
||||
out1[0x10* 1] = REAL_SCALE_DCT64( bufs[16+1] + bufs[16+9] ); |
||||
out1[0x10* 2] = REAL_SCALE_DCT64( bufs[9] ); |
||||
out1[0x10* 3] = REAL_SCALE_DCT64( bufs[16+9] + bufs[16+5] ); |
||||
out1[0x10* 4] = REAL_SCALE_DCT64( bufs[5] ); |
||||
out1[0x10* 5] = REAL_SCALE_DCT64( bufs[16+5] + bufs[16+13] ); |
||||
out1[0x10* 6] = REAL_SCALE_DCT64( bufs[13] ); |
||||
out1[0x10* 7] = REAL_SCALE_DCT64( bufs[16+13] + bufs[16+3] ); |
||||
out1[0x10* 8] = REAL_SCALE_DCT64( bufs[3] ); |
||||
out1[0x10* 9] = REAL_SCALE_DCT64( bufs[16+3] + bufs[16+11] ); |
||||
out1[0x10*10] = REAL_SCALE_DCT64( bufs[11] ); |
||||
out1[0x10*11] = REAL_SCALE_DCT64( bufs[16+11] + bufs[16+7] ); |
||||
out1[0x10*12] = REAL_SCALE_DCT64( bufs[7] ); |
||||
out1[0x10*13] = REAL_SCALE_DCT64( bufs[16+7] + bufs[16+15] ); |
||||
out1[0x10*14] = REAL_SCALE_DCT64( bufs[15] ); |
||||
out1[0x10*15] = REAL_SCALE_DCT64( bufs[16+15] ); |
||||
} |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
fmt123.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef FMT123_H |
||||
#define FMT123_H |
||||
|
||||
#define MPG123_RATES 9 |
||||
#define MPG123_ENCODINGS 2 |
||||
|
||||
enum mpg123_enc_enum |
||||
{ |
||||
// 0000 0000 0000 1111 Some 8 bit integer encoding. */
|
||||
MPG123_ENC_8 = 0x00f, |
||||
// 0000 0000 0100 0000 Some 16 bit integer encoding.
|
||||
MPG123_ENC_16 = 0x040, |
||||
// 0000 0000 1000 0000 Some signed integer encoding.
|
||||
MPG123_ENC_SIGNED = 0x080, |
||||
// 0000 0000 1101 0000 signed 16 bit
|
||||
MPG123_ENC_SIGNED_16 = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10), |
||||
// 0000 0000 0110 0000 unsigned 16 bit
|
||||
MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20), |
||||
// 0000 0000 0000 0001 unsigned 8 bit
|
||||
MPG123_ENC_UNSIGNED_8 = 0x01, |
||||
// 0000 0000 1000 0010 signed 8 bit
|
||||
MPG123_ENC_SIGNED_8 = (MPG123_ENC_SIGNED|0x02), |
||||
// 0000 0000 0000 0100 ulaw 8 bit
|
||||
MPG123_ENC_ULAW_8 = 0x04, |
||||
// 0000 0000 0000 1000 alaw 8 bit
|
||||
MPG123_ENC_ALAW_8 = 0x08, |
||||
}; |
||||
|
||||
#endif//FMT123_H
|
@ -0,0 +1,435 @@
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
frame.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
|
||||
enum mpg123_channelcount |
||||
{ |
||||
MPG123_MONO = 1, |
||||
MPG123_STEREO = 2 |
||||
}; |
||||
|
||||
// only the standard rates
|
||||
static const long my_rates[MPG123_RATES] = |
||||
{ |
||||
8000, 11025, 12000, |
||||
16000, 22050, 24000, |
||||
32000, 44100, 48000, |
||||
}; |
||||
|
||||
static const int my_encodings[MPG123_ENCODINGS] = |
||||
{ |
||||
MPG123_ENC_SIGNED_16, |
||||
MPG123_ENC_UNSIGNED_16, |
||||
}; |
||||
|
||||
// the list of actually possible encodings.
|
||||
static const int good_encodings[] = |
||||
{ |
||||
MPG123_ENC_SIGNED_16, |
||||
MPG123_ENC_UNSIGNED_16, |
||||
}; |
||||
|
||||
// check if encoding is a valid one in this build.
|
||||
static int good_enc( const int enc ) |
||||
{ |
||||
size_t i; |
||||
|
||||
for( i = 0; i < sizeof( good_encodings ) / sizeof( int ); ++i ) |
||||
{ |
||||
if( enc == good_encodings[i] ) |
||||
return TRUE; |
||||
} |
||||
|
||||
return FALSE; |
||||
} |
||||
|
||||
void mpg123_rates( const long **list, size_t *number ) |
||||
{ |
||||
if( number != NULL ) *number = sizeof( my_rates ) / sizeof( long ); |
||||
if( list != NULL ) *list = my_rates; |
||||
} |
||||
|
||||
// now that's a bit tricky... One build of the library knows only a subset of the encodings.
|
||||
void mpg123_encodings( const int **list, size_t *number ) |
||||
{ |
||||
if( number != NULL ) *number = sizeof( good_encodings ) / sizeof( int ); |
||||
if( list != NULL ) *list = good_encodings; |
||||
} |
||||
|
||||
int mpg123_encsize( int encoding ) |
||||
{ |
||||
return sizeof( short ); |
||||
} |
||||
|
||||
static int rate2num( long r ) |
||||
{ |
||||
int i; |
||||
|
||||
for( i = 0; i < MPG123_RATES; i++ ) |
||||
{ |
||||
if( my_rates[i] == r ) |
||||
return i; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
static int enc2num( int encoding ) |
||||
{ |
||||
int i; |
||||
|
||||
for( i = 0; i < MPG123_ENCODINGS; ++i ) |
||||
{ |
||||
if( my_encodings[i] == encoding ) |
||||
return i; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
static int cap_fit( mpg123_handle_t *fr, audioformat_t *nf, int f0, int f2) |
||||
{ |
||||
int i; |
||||
int c = nf->channels - 1; |
||||
int rn = rate2num( nf->rate ); |
||||
|
||||
if( rn >= 0 ) |
||||
{ |
||||
for( i = f0; i <f2; i++ ) |
||||
{ |
||||
if( fr->p.audio_caps[c][rn][i] ) |
||||
{ |
||||
nf->encoding = my_encodings[i]; |
||||
return 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int freq_fit( mpg123_handle_t *fr, audioformat_t *nf, int f0, int f2 ) |
||||
{ |
||||
nf->rate = frame_freq( fr ) >> fr->p.down_sample; |
||||
|
||||
if( cap_fit( fr, nf, f0, f2 )) |
||||
return 1; |
||||
|
||||
if( fr->p.flags & MPG123_AUTO_RESAMPLE ) |
||||
{ |
||||
nf->rate >>= 1; |
||||
if( cap_fit( fr, nf, f0, f2 )) |
||||
return 1; |
||||
|
||||
nf->rate >>= 1; |
||||
if( cap_fit( fr, nf, f0, f2 )) |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// match constraints against supported audio formats, store possible setup in frame
|
||||
// return: -1: error; 0: no format change; 1: format change
|
||||
int frame_output_format( mpg123_handle_t *fr ) |
||||
{ |
||||
int f0 = 0; |
||||
int f2 = MPG123_ENCODINGS; |
||||
mpg123_parm_t *p = &fr->p; |
||||
audioformat_t nf; |
||||
|
||||
// initialize new format, encoding comes later
|
||||
nf.channels = fr->stereo; |
||||
|
||||
// force stereo is stronger
|
||||
if( p->flags & MPG123_FORCE_MONO ) |
||||
nf.channels = 1; |
||||
|
||||
if( p->flags & MPG123_FORCE_STEREO ) |
||||
nf.channels = 2; |
||||
|
||||
if( freq_fit( fr, &nf, f0, 2 )) |
||||
goto end; // try rates with 16bit
|
||||
|
||||
if( freq_fit( fr, &nf, f0 <=2 ? 2 : f0, f2 )) |
||||
goto end; // ... 8bit
|
||||
|
||||
// try again with different stereoness
|
||||
if( nf.channels == 2 && !( p->flags & MPG123_FORCE_STEREO )) |
||||
nf.channels = 1; |
||||
else if( nf.channels == 1 && !( p->flags & MPG123_FORCE_MONO )) |
||||
nf.channels = 2; |
||||
|
||||
if( freq_fit( fr, &nf, f0, 2 )) |
||||
goto end; // try rates with 16bit
|
||||
if( freq_fit( fr, &nf, f0 <= 2 ? 2 : f0, f2 )) |
||||
goto end; // ... 8bit
|
||||
|
||||
fr->err = MPG123_BAD_OUTFORMAT; |
||||
return -1; |
||||
end: |
||||
// here is the _good_ end.
|
||||
// we had a successful match, now see if there's a change
|
||||
if( nf.rate == fr->af.rate && nf.channels == fr->af.channels && nf.encoding == fr->af.encoding ) |
||||
{ |
||||
return 0; // the same format as before
|
||||
} |
||||
else |
||||
{ // a new format
|
||||
fr->af.rate = nf.rate; |
||||
fr->af.channels = nf.channels; |
||||
fr->af.encoding = nf.encoding; |
||||
|
||||
// cache the size of one sample in bytes, for ease of use.
|
||||
fr->af.encsize = mpg123_encsize( fr->af.encoding ); |
||||
if( fr->af.encsize < 1 ) |
||||
{ |
||||
fr->err = MPG123_BAD_OUTFORMAT; |
||||
return -1; |
||||
} |
||||
|
||||
// set up the decoder synth format. Might differ.
|
||||
// without high-precision synths, 16 bit signed is the basis for
|
||||
// everything higher than 8 bit.
|
||||
if( fr->af.encsize > 2 ) |
||||
{ |
||||
fr->af.dec_enc = MPG123_ENC_SIGNED_16; |
||||
} |
||||
else |
||||
{ |
||||
switch( fr->af.encoding ) |
||||
{ |
||||
case MPG123_ENC_UNSIGNED_16: |
||||
fr->af.dec_enc = MPG123_ENC_SIGNED_16; |
||||
break; |
||||
default: |
||||
fr->af.dec_enc = fr->af.encoding; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
fr->af.dec_encsize = mpg123_encsize( fr->af.dec_enc ); |
||||
|
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
static int mpg123_fmt_none( mpg123_parm_t *mp ) |
||||
{ |
||||
if( mp == NULL ) |
||||
return MPG123_BAD_PARS; |
||||
|
||||
memset( mp->audio_caps, 0, sizeof( mp->audio_caps )); |
||||
return MPG123_OK; |
||||
} |
||||
|
||||
int mpg123_fmt_all( mpg123_parm_t *mp ) |
||||
{ |
||||
size_t rate, ch, enc; |
||||
|
||||
if( mp == NULL ) |
||||
return MPG123_BAD_PARS; |
||||
|
||||
for( ch = 0; ch < NUM_CHANNELS; ++ch ) |
||||
{ |
||||
for( rate = 0; rate < MPG123_RATES+1; ++rate ) |
||||
{ |
||||
for( enc = 0; enc < MPG123_ENCODINGS; ++enc ) |
||||
mp->audio_caps[ch][rate][enc] = good_enc( my_encodings[enc] ); |
||||
} |
||||
} |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
static int mpg123_fmt( mpg123_parm_t *mp, long rate, int channels, int encodings ) |
||||
{ |
||||
int ie, ic, ratei; |
||||
int ch[2] = { 0, 1 }; |
||||
|
||||
if( mp == NULL ) |
||||
return MPG123_BAD_PARS; |
||||
|
||||
if(!( channels & ( MPG123_MONO|MPG123_STEREO ))) |
||||
return MPG123_BAD_CHANNEL; |
||||
|
||||
if(!( channels & MPG123_STEREO )) |
||||
ch[1] = 0; |
||||
else if(!( channels & MPG123_MONO )) |
||||
ch[0] = 1; |
||||
|
||||
ratei = rate2num( rate ); |
||||
if( ratei < 0 ) return MPG123_BAD_RATE; |
||||
|
||||
// now match the encodings
|
||||
for( ic = 0; ic < 2; ++ic ) |
||||
{ |
||||
for( ie = 0; ie < MPG123_ENCODINGS; ++ie ) |
||||
{ |
||||
if( good_enc( my_encodings[ie] ) && (( my_encodings[ie] & encodings ) == my_encodings[ie] )) |
||||
mp->audio_caps[ch[ic]][ratei][ie] = 1; |
||||
} |
||||
|
||||
if( ch[0] == ch[1] ) |
||||
break; // no need to do it again
|
||||
} |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
static int mpg123_fmt_support( mpg123_parm_t *mp, long rate, int encoding ) |
||||
{ |
||||
int ratei, enci; |
||||
int ch = 0; |
||||
|
||||
ratei = rate2num( rate ); |
||||
enci = enc2num( encoding ); |
||||
|
||||
if( mp == NULL || ratei < 0 || enci < 0 ) |
||||
return 0; |
||||
|
||||
if( mp->audio_caps[0][ratei][enci] ) |
||||
ch |= MPG123_MONO; |
||||
|
||||
if( mp->audio_caps[1][ratei][enci] ) |
||||
ch |= MPG123_STEREO; |
||||
|
||||
return ch; |
||||
} |
||||
|
||||
int mpg123_format_none( mpg123_handle_t *mh ) |
||||
{ |
||||
int r; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
r = mpg123_fmt_none( &mh->p ); |
||||
|
||||
if( r != MPG123_OK ) |
||||
{ |
||||
mh->err = r; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
return r; |
||||
} |
||||
|
||||
int mpg123_format_all( mpg123_handle_t *mh ) |
||||
{ |
||||
int r; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
r = mpg123_fmt_all( &mh->p ); |
||||
|
||||
if( r != MPG123_OK ) |
||||
{ |
||||
mh->err = r; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
return r; |
||||
} |
||||
|
||||
int mpg123_format( mpg123_handle_t *mh, long rate, int channels, int encodings ) |
||||
{ |
||||
int r; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
r = mpg123_fmt( &mh->p, rate, channels, encodings ); |
||||
|
||||
if( r != MPG123_OK ) |
||||
{ |
||||
mh->err = r; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
return r; |
||||
} |
||||
|
||||
int mpg123_format_support( mpg123_handle_t *mh, long rate, int encoding ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return 0; |
||||
|
||||
return mpg123_fmt_support( &mh->p, rate, encoding ); |
||||
} |
||||
|
||||
// call this one to ensure that any valid format will be something different than this.
|
||||
void invalidate_format( audioformat_t *af ) |
||||
{ |
||||
af->encoding = 0; |
||||
af->channels = 0; |
||||
af->rate = 0; |
||||
} |
||||
|
||||
// number of bytes the decoder produces.
|
||||
mpg_off_t decoder_synth_bytes( mpg123_handle_t *fr, mpg_off_t s ) |
||||
{ |
||||
return s * fr->af.dec_encsize * fr->af.channels; |
||||
} |
||||
|
||||
// samples/bytes for output buffer after post-processing.
|
||||
// take into account: channels, bytes per sample -- NOT resampling!
|
||||
mpg_off_t samples_to_bytes( mpg123_handle_t *fr, mpg_off_t s ) |
||||
{ |
||||
return s * fr->af.encsize * fr->af.channels; |
||||
} |
||||
|
||||
mpg_off_t bytes_to_samples( mpg123_handle_t *fr, mpg_off_t b ) |
||||
{ |
||||
return b / fr->af.encsize / fr->af.channels; |
||||
} |
||||
|
||||
// number of bytes needed for decoding _and_ post-processing.
|
||||
mpg_off_t outblock_bytes( mpg123_handle_t *fr, mpg_off_t s ) |
||||
{ |
||||
int encsize = (fr->af.encsize > fr->af.dec_encsize ? fr->af.encsize : fr->af.dec_encsize); |
||||
return s * encsize * fr->af.channels; |
||||
} |
||||
|
||||
static void conv_s16_to_u16( outbuffer_t *buf ) |
||||
{ |
||||
int16_t *ssamples = (int16_t *)buf->data; |
||||
uint16_t *usamples = (uint16_t *)buf->data; |
||||
size_t count = buf->fill / sizeof( int16_t ); |
||||
size_t i; |
||||
|
||||
for( i = 0; i < count; ++i ) |
||||
{ |
||||
long tmp = (long)ssamples[i] + 32768; |
||||
usamples[i] = (uint16_t)tmp; |
||||
} |
||||
} |
||||
|
||||
void postprocess_buffer( mpg123_handle_t *fr ) |
||||
{ |
||||
switch( fr->af.dec_enc ) |
||||
{ |
||||
case MPG123_ENC_SIGNED_16: |
||||
switch( fr->af.encoding ) |
||||
{ |
||||
case MPG123_ENC_UNSIGNED_16: |
||||
conv_s16_to_u16(&fr->buffer); |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
} |
@ -0,0 +1,750 @@
@@ -0,0 +1,750 @@
|
||||
/*
|
||||
frame.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include <math.h> |
||||
|
||||
static void *aligned_pointer( void *base, uint alignment ) |
||||
{ |
||||
// work in unsigned integer realm, explicitly.
|
||||
// tricking the compiler into integer operations like % by invoking base-NULL is dangerous:
|
||||
// it results into ptrdiff_t, which gets negative on big addresses. Big screw up, that.
|
||||
// i try to do it "properly" here: Casting only to size_t and no artihmethic with void*.
|
||||
|
||||
size_t baseval = (size_t)(char *)base; |
||||
size_t aoff = baseval % alignment; |
||||
|
||||
if( aoff ) |
||||
return (char *)base + alignment - aoff; |
||||
return base; |
||||
} |
||||
|
||||
static void frame_default_parm( mpg123_parm_t *mp ) |
||||
{ |
||||
mp->outscale = 1.0; |
||||
mp->flags = 0; |
||||
mp->flags |= MPG123_GAPLESS; |
||||
mp->flags |= MPG123_AUTO_RESAMPLE; |
||||
mp->down_sample = 0; |
||||
mp->rva = 0; |
||||
mp->halfspeed = 0; |
||||
mp->doublespeed = 0; |
||||
mp->verbose = 0; |
||||
mp->timeout = 0; |
||||
mp->resync_limit = 1024; |
||||
mp->index_size = INDEX_SIZE; |
||||
mp->preframes = 4; // that's good for layer 3 ISO compliance bitstream.
|
||||
mpg123_fmt_all( mp ); |
||||
|
||||
// default of keeping some 4K buffers at hand, should cover the "usual" use case
|
||||
// (using 16K pipe buffers as role model).
|
||||
mp->feedpool = 5; |
||||
mp->feedbuffer = 4096; |
||||
} |
||||
|
||||
// reset everythign except dynamic memory.
|
||||
static void frame_fixed_reset( mpg123_handle_t *fr ) |
||||
{ |
||||
open_bad( fr ); |
||||
fr->to_decode = FALSE; |
||||
fr->to_ignore = FALSE; |
||||
fr->metaflags = 0; |
||||
fr->outblock = 0; // this will be set before decoding!
|
||||
fr->num = -1; |
||||
fr->input_offset = -1; |
||||
fr->playnum = -1; |
||||
fr->state_flags = FRAME_ACCURATE; |
||||
fr->silent_resync = 0; |
||||
fr->audio_start = 0; |
||||
fr->clip = 0; |
||||
fr->oldhead = 0; |
||||
fr->firsthead = 0; |
||||
fr->vbr = MPG123_CBR; |
||||
fr->abr_rate = 0; |
||||
fr->track_frames = 0; |
||||
fr->track_samples = -1; |
||||
fr->framesize=0; |
||||
fr->mean_frames = 0; |
||||
fr->mean_framesize = 0; |
||||
fr->freesize = 0; |
||||
fr->lastscale = -1; |
||||
fr->rva.level[0] = -1; |
||||
fr->rva.level[1] = -1; |
||||
fr->rva.gain[0] = 0; |
||||
fr->rva.gain[1] = 0; |
||||
fr->rva.peak[0] = 0; |
||||
fr->rva.peak[1] = 0; |
||||
fr->fsizeold = 0; |
||||
fr->firstframe = 0; |
||||
fr->ignoreframe = fr->firstframe - fr->p.preframes; |
||||
fr->header_change = 0; |
||||
fr->lastframe = -1; |
||||
fr->fresh = 1; |
||||
fr->new_format = 0; |
||||
frame_gapless_init( fr, -1, 0, 0 ); |
||||
fr->lastoff = 0; |
||||
fr->firstoff = 0; |
||||
fr->bo = 1; // the usual bo
|
||||
fr->halfphase = 0; // here or indeed only on first-time init?
|
||||
fr->error_protection = 0; |
||||
fr->freeformat_framesize = -1; |
||||
} |
||||
|
||||
int frame_index_setup( mpg123_handle_t *fr ) |
||||
{ |
||||
int ret = MPG123_ERR; |
||||
|
||||
if( fr->p.index_size >= 0 ) |
||||
{ |
||||
// simple fixed index.
|
||||
fr->index.grow_size = 0; |
||||
ret = fi_resize( &fr->index, (size_t)fr->p.index_size ); |
||||
} |
||||
else |
||||
{ |
||||
// a growing index. we give it a start, though.
|
||||
fr->index.grow_size = (size_t)(-fr->p.index_size ); |
||||
|
||||
if( fr->index.size < fr->index.grow_size ) |
||||
ret = fi_resize( &fr->index, fr->index.grow_size ); |
||||
else ret = MPG123_OK; // we have minimal size already... and since growing is OK...
|
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void frame_init_par( mpg123_handle_t *fr, mpg123_parm_t *mp ) |
||||
{ |
||||
fr->own_buffer = TRUE; |
||||
fr->buffer.data = NULL; |
||||
fr->buffer.rdata = NULL; |
||||
fr->buffer.fill = 0; |
||||
fr->buffer.size = 0; |
||||
fr->rawbuffs = NULL; |
||||
fr->rawbuffss = 0; |
||||
fr->rawdecwin = NULL; |
||||
fr->rawdecwins = 0; |
||||
fr->layerscratch = NULL; |
||||
fr->xing_toc = NULL; |
||||
|
||||
// unnecessary: fr->buffer.size = fr->buffer.fill = 0;
|
||||
// frame_outbuffer is missing...
|
||||
// frame_buffers is missing... that one needs cpu opt setting!
|
||||
// after these... frame_reset is needed before starting full decode
|
||||
invalidate_format( &fr->af ); |
||||
fr->rdat.r_read = NULL; |
||||
fr->rdat.r_lseek = NULL; |
||||
fr->rdat.iohandle = NULL; |
||||
fr->rdat.r_read_handle = NULL; |
||||
fr->rdat.r_lseek_handle = NULL; |
||||
fr->rdat.cleanup_handle = NULL; |
||||
fr->wrapperdata = NULL; |
||||
fr->wrapperclean = NULL; |
||||
fr->decoder_change = 1; |
||||
fr->err = MPG123_OK; |
||||
|
||||
if( mp == NULL ) frame_default_parm( &fr->p ); |
||||
else memcpy( &fr->p, mp, sizeof( mpg123_parm_t )); |
||||
|
||||
bc_prepare( &fr->rdat.buffer, fr->p.feedpool, fr->p.feedbuffer ); |
||||
|
||||
fr->down_sample = 0; // initialize to silence harmless errors when debugging.
|
||||
frame_fixed_reset( fr ); // reset only the fixed data, dynamic buffers are not there yet!
|
||||
fr->synth = NULL; |
||||
fr->synth_mono = NULL; |
||||
fr->make_decode_tables = NULL; |
||||
|
||||
fi_init( &fr->index ); |
||||
frame_index_setup( fr ); // apply the size setting.
|
||||
} |
||||
|
||||
static void frame_decode_buffers_reset(mpg123_handle_t *fr) |
||||
{ |
||||
memset(fr->rawbuffs, 0, fr->rawbuffss); |
||||
} |
||||
|
||||
int frame_buffers( mpg123_handle_t *fr ) |
||||
{ |
||||
int buffssize = 4352; |
||||
|
||||
buffssize += 15; // for 16-byte alignment
|
||||
|
||||
if(fr->rawbuffs != NULL && fr->rawbuffss != buffssize) |
||||
{ |
||||
free(fr->rawbuffs); |
||||
fr->rawbuffs = NULL; |
||||
} |
||||
|
||||
if( fr->rawbuffs == NULL ) |
||||
fr->rawbuffs = (byte *)malloc( buffssize ); |
||||
|
||||
if( fr->rawbuffs == NULL ) |
||||
return -1; |
||||
|
||||
fr->rawbuffss = buffssize; |
||||
fr->short_buffs[0][0] = aligned_pointer( fr->rawbuffs, 16 ); |
||||
fr->short_buffs[0][1] = fr->short_buffs[0][0] + 0x110; |
||||
fr->short_buffs[1][0] = fr->short_buffs[0][1] + 0x110; |
||||
fr->short_buffs[1][1] = fr->short_buffs[1][0] + 0x110; |
||||
fr->float_buffs[0][0] = aligned_pointer( fr->rawbuffs, 16 ); |
||||
fr->float_buffs[0][1] = fr->float_buffs[0][0] + 0x110; |
||||
fr->float_buffs[1][0] = fr->float_buffs[0][1] + 0x110; |
||||
fr->float_buffs[1][1] = fr->float_buffs[1][0] + 0x110; |
||||
|
||||
// now the different decwins... all of the same size, actually
|
||||
// the MMX ones want 32byte alignment, which I'll try to ensure manually
|
||||
{ |
||||
int decwin_size = (512 + 32) * sizeof( float ); |
||||
|
||||
// hm, that's basically realloc() ...
|
||||
if( fr->rawdecwin != NULL && fr->rawdecwins != decwin_size ) |
||||
{ |
||||
free( fr->rawdecwin ); |
||||
fr->rawdecwin = NULL; |
||||
} |
||||
|
||||
if( fr->rawdecwin == NULL ) |
||||
fr->rawdecwin = (byte *)malloc( decwin_size ); |
||||
|
||||
if( fr->rawdecwin == NULL ) |
||||
return -1; |
||||
|
||||
fr->rawdecwins = decwin_size; |
||||
fr->decwin = (float *)fr->rawdecwin; |
||||
} |
||||
|
||||
// layer scratch buffers are of compile-time fixed size, so allocate only once.
|
||||
if( fr->layerscratch == NULL ) |
||||
{ |
||||
// allocate specific layer3 buffers
|
||||
size_t scratchsize = 0; |
||||
float *scratcher; |
||||
|
||||
scratchsize += sizeof( float ) * 2 * SBLIMIT * SSLIMIT; // hybrid_in
|
||||
scratchsize += sizeof( float ) * 2 * SSLIMIT * SBLIMIT; // hybrid_out
|
||||
|
||||
fr->layerscratch = malloc( scratchsize + 63 ); |
||||
if(fr->layerscratch == NULL) return -1; |
||||
|
||||
// get aligned part of the memory, then divide it up.
|
||||
scratcher = aligned_pointer( fr->layerscratch, 64 ); |
||||
|
||||
// those funky pointer casts silence compilers...
|
||||
// One might change the code at hand to really just use 1D arrays,
|
||||
// but in practice, that would not make a (positive) difference.
|
||||
fr->layer3.hybrid_in = (float(*)[SBLIMIT][SSLIMIT])scratcher; |
||||
scratcher += 2 * SBLIMIT * SSLIMIT; |
||||
fr->layer3.hybrid_out = (float(*)[SSLIMIT][SBLIMIT])scratcher; |
||||
scratcher += 2 * SSLIMIT * SBLIMIT; |
||||
|
||||
// note: These buffers don't need resetting here.
|
||||
} |
||||
|
||||
// only reset the buffers we created just now.
|
||||
frame_decode_buffers_reset( fr ); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int frame_buffers_reset( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->buffer.fill = 0; // hm, reset buffer fill... did we do a flush?
|
||||
fr->bsnum = 0; |
||||
|
||||
// wondering: could it be actually _wanted_ to retain buffer contents over different files? (special gapless / cut stuff)
|
||||
fr->bsbuf = fr->bsspace[1]; |
||||
fr->bsbufold = fr->bsbuf; |
||||
fr->bitreservoir = 0; |
||||
frame_decode_buffers_reset( fr ); |
||||
memset( fr->bsspace, 0, 2 * ( MAXFRAMESIZE + 512 )); |
||||
memset( fr->ssave, 0, 34 ); |
||||
fr->hybrid_blc[0] = fr->hybrid_blc[1] = 0; |
||||
memset( fr->hybrid_block, 0, sizeof( float ) * 2 * 2 * SBLIMIT * SSLIMIT ); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void frame_init( mpg123_handle_t *fr ) |
||||
{ |
||||
frame_init_par( fr, NULL ); |
||||
} |
||||
|
||||
int frame_outbuffer( mpg123_handle_t *fr ) |
||||
{ |
||||
size_t size = fr->outblock; |
||||
|
||||
if( !fr->own_buffer ) |
||||
{ |
||||
if( fr->buffer.size < size ) |
||||
{ |
||||
fr->err = MPG123_BAD_BUFFER; |
||||
return MPG123_ERR; |
||||
} |
||||
} |
||||
|
||||
if( fr->buffer.rdata != NULL && fr->buffer.size != size ) |
||||
{ |
||||
free( fr->buffer.rdata ); |
||||
fr->buffer.rdata = NULL; |
||||
} |
||||
|
||||
fr->buffer.size = size; |
||||
fr->buffer.data = NULL; |
||||
|
||||
// be generous: use 16 byte alignment
|
||||
if( fr->buffer.rdata == NULL ) |
||||
fr->buffer.rdata = (byte *)malloc( fr->buffer.size + 15 ); |
||||
|
||||
if( fr->buffer.rdata == NULL ) |
||||
{ |
||||
fr->err = MPG123_OUT_OF_MEM; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
fr->buffer.data = aligned_pointer( fr->buffer.rdata, 16 ); |
||||
fr->own_buffer = TRUE; |
||||
fr->buffer.fill = 0; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
static void frame_free_toc( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->xing_toc != NULL ) |
||||
{ |
||||
free( fr->xing_toc ); |
||||
fr->xing_toc = NULL; |
||||
} |
||||
} |
||||
|
||||
// Just copy the Xing TOC over...
|
||||
int frame_fill_toc( mpg123_handle_t *fr, byte *in ) |
||||
{ |
||||
if( fr->xing_toc == NULL ) |
||||
fr->xing_toc = malloc( 100 ); |
||||
|
||||
if( fr->xing_toc != NULL ) |
||||
{ |
||||
memcpy( fr->xing_toc, in, 100 ); |
||||
return TRUE; |
||||
} |
||||
|
||||
return FALSE; |
||||
} |
||||
|
||||
// prepare the handle for a new track.
|
||||
// reset variables, buffers...
|
||||
int frame_reset( mpg123_handle_t *fr ) |
||||
{ |
||||
frame_buffers_reset( fr ); |
||||
frame_fixed_reset( fr ); |
||||
frame_free_toc( fr ); |
||||
fi_reset( &fr->index ); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void frame_free_buffers( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->rawbuffs != NULL ) |
||||
free( fr->rawbuffs ); |
||||
fr->rawbuffs = NULL; |
||||
fr->rawbuffss = 0; |
||||
|
||||
if( fr->rawdecwin != NULL ) |
||||
free( fr->rawdecwin ); |
||||
fr->rawdecwin = NULL; |
||||
fr->rawdecwins = 0; |
||||
|
||||
if( fr->layerscratch != NULL ) |
||||
free( fr->layerscratch ); |
||||
} |
||||
|
||||
void frame_exit( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->buffer.rdata != NULL ) |
||||
free( fr->buffer.rdata ); |
||||
|
||||
fr->buffer.rdata = NULL; |
||||
frame_free_buffers( fr ); |
||||
frame_free_toc( fr ); |
||||
fi_exit( &fr->index ); |
||||
|
||||
// clean up possible mess from LFS wrapper.
|
||||
if( fr->wrapperclean != NULL ) |
||||
{ |
||||
fr->wrapperclean( fr->wrapperdata ); |
||||
fr->wrapperdata = NULL; |
||||
} |
||||
|
||||
bc_cleanup( &fr->rdat.buffer ); |
||||
} |
||||
|
||||
int mpg123_framedata( mpg123_handle_t *mh, ulong *header, byte **bodydata, size_t *bodybytes ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
if( !mh->to_decode ) |
||||
return MPG123_ERR; |
||||
|
||||
if( header != NULL ) |
||||
*header = mh->oldhead; |
||||
|
||||
if( bodydata != NULL ) |
||||
*bodydata = mh->bsbuf; |
||||
|
||||
if( bodybytes != NULL ) |
||||
*bodybytes = mh->framesize; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
// Fuzzy frame offset searching (guessing).
|
||||
// When we don't have an accurate position, we may use an inaccurate one.
|
||||
// Possibilities:
|
||||
// - use approximate positions from Xing TOC (not yet parsed)
|
||||
// - guess wildly from mean framesize and offset of first frame / beginning of file.
|
||||
static mpg_off_t frame_fuzzy_find( mpg123_handle_t *fr, mpg_off_t want_frame, mpg_off_t *get_frame ) |
||||
{ |
||||
mpg_off_t ret = fr->audio_start; // default is to go to the beginning.
|
||||
|
||||
*get_frame = 0; |
||||
|
||||
// but we try to find something better.
|
||||
// Xing VBR TOC works with relative positions, both in terms of audio frames and stream bytes.
|
||||
// thus, it only works when whe know the length of things.
|
||||
// oh... I assume the offsets are relative to the _total_ file length.
|
||||
if( fr->xing_toc != NULL && fr->track_frames > 0 && fr->rdat.filelen > 0 ) |
||||
{ |
||||
// one could round...
|
||||
int toc_entry = (int)((double)want_frame * 100.0 / fr->track_frames ); |
||||
|
||||
// it is an index in the 100-entry table.
|
||||
if( toc_entry < 0 ) toc_entry = 0; |
||||
if( toc_entry > 99 ) toc_entry = 99; |
||||
|
||||
// now estimate back what frame we get.
|
||||
*get_frame = (mpg_off_t)((double)toc_entry / 100.0 * fr->track_frames ); |
||||
fr->state_flags &= ~FRAME_ACCURATE; |
||||
fr->silent_resync = 1; |
||||
|
||||
// question: Is the TOC for whole file size (with/without ID3) or the "real" audio data only?
|
||||
// ID3v1 info could also matter.
|
||||
ret = (mpg_off_t)((double)fr->xing_toc[toc_entry] / 256.0 * fr->rdat.filelen); |
||||
} |
||||
else if( fr->mean_framesize > 0 ) |
||||
{ |
||||
// just guess with mean framesize (may be exact with CBR files).
|
||||
// query filelen here or not?
|
||||
fr->state_flags &= ~FRAME_ACCURATE; // fuzzy!
|
||||
fr->silent_resync = 1; |
||||
*get_frame = want_frame; |
||||
ret = (mpg_off_t)(fr->audio_start + fr->mean_framesize * want_frame); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
// find the best frame in index just before the wanted one, seek to there
|
||||
// then step to just before wanted one with read_frame
|
||||
// do not care tabout the stuff that was in buffer but not played back
|
||||
// everything that left the decoder is counted as played
|
||||
// decide if you want low latency reaction and accurate timing info or stable long-time playback with buffer!
|
||||
mpg_off_t frame_index_find( mpg123_handle_t *fr, mpg_off_t want_frame, mpg_off_t* get_frame ) |
||||
{ |
||||
mpg_off_t gopos = 0; // default is file start if no index position
|
||||
|
||||
*get_frame = 0; |
||||
|
||||
// possibly use VBRI index, too? I'd need an example for this...
|
||||
if( fr->index.fill ) |
||||
{ |
||||
size_t fi; // find in index
|
||||
|
||||
// at index fi there is frame step*fi...
|
||||
fi = want_frame / fr->index.step; |
||||
|
||||
if( fi >= fr->index.fill ) |
||||
{ |
||||
// if we are beyond the end of frame index...
|
||||
// when fuzzy seek is allowed, we have some limited tolerance for the frames we want to read rather then jump over.
|
||||
if( fr->p.flags & MPG123_FUZZY && want_frame - (fr->index.fill- 1) * fr->index.step > 10 ) |
||||
{ |
||||
gopos = frame_fuzzy_find( fr, want_frame, get_frame ); |
||||
if( gopos > fr->audio_start ) |
||||
return gopos; // only in that case, we have a useful guess.
|
||||
// else... just continue, fuzzyness didn't help.
|
||||
} |
||||
|
||||
// use the last available position, slowly advancing from that one.
|
||||
fi = fr->index.fill - 1; |
||||
} |
||||
|
||||
// we have index position, that yields frame and byte offsets.
|
||||
*get_frame = fi * fr->index.step; |
||||
gopos = fr->index.data[fi]; |
||||
fr->state_flags |= FRAME_ACCURATE; // when using the frame index, we are accurate.
|
||||
} |
||||
else |
||||
{ |
||||
if( fr->p.flags & MPG123_FUZZY ) |
||||
return frame_fuzzy_find( fr, want_frame, get_frame ); |
||||
|
||||
// a bit hackish here... but we need to be fresh when looking for the first header again.
|
||||
fr->firsthead = 0; |
||||
fr->oldhead = 0; |
||||
} |
||||
|
||||
return gopos; |
||||
} |
||||
|
||||
mpg_off_t frame_ins2outs( mpg123_handle_t *fr, mpg_off_t ins ) |
||||
{ |
||||
mpg_off_t outs = 0; |
||||
|
||||
switch( fr->down_sample ) |
||||
{ |
||||
case 0: |
||||
outs = ins >> fr->down_sample; |
||||
break; |
||||
default: break; |
||||
} |
||||
|
||||
return outs; |
||||
} |
||||
|
||||
mpg_off_t frame_outs( mpg123_handle_t *fr, mpg_off_t num ) |
||||
{ |
||||
mpg_off_t outs = 0; |
||||
|
||||
switch( fr->down_sample ) |
||||
{ |
||||
case 0: |
||||
outs = (fr->spf >> fr->down_sample) * num; |
||||
break; |
||||
default: break; |
||||
} |
||||
|
||||
return outs; |
||||
} |
||||
|
||||
// compute the number of output samples we expect from this frame.
|
||||
// this is either simple spf() or a tad more elaborate for ntom.
|
||||
mpg_off_t frame_expect_outsamples( mpg123_handle_t *fr ) |
||||
{ |
||||
mpg_off_t outs = 0; |
||||
|
||||
switch( fr->down_sample ) |
||||
{ |
||||
case 0: |
||||
outs = fr->spf >> fr->down_sample; |
||||
break; |
||||
default: break; |
||||
} |
||||
|
||||
return outs; |
||||
} |
||||
|
||||
mpg_off_t frame_offset( mpg123_handle_t *fr, mpg_off_t outs ) |
||||
{ |
||||
mpg_off_t num = 0; |
||||
|
||||
switch( fr->down_sample ) |
||||
{ |
||||
case 0: |
||||
num = outs / (fr->spf >> fr->down_sample); |
||||
break; |
||||
default: break; |
||||
} |
||||
|
||||
return num; |
||||
} |
||||
|
||||
// input in _input_ samples
|
||||
void frame_gapless_init( mpg123_handle_t *fr, mpg_off_t framecount, mpg_off_t bskip, mpg_off_t eskip ) |
||||
{ |
||||
fr->gapless_frames = framecount; |
||||
|
||||
if( fr->gapless_frames > 0 && bskip >= 0 && eskip >= 0 ) |
||||
{ |
||||
fr->begin_s = bskip + GAPLESS_DELAY; |
||||
fr->end_s = framecount * fr->spf - eskip + GAPLESS_DELAY; |
||||
} |
||||
else fr->begin_s = fr->end_s = 0; |
||||
|
||||
// these will get proper values later, from above plus resampling info.
|
||||
fr->begin_os = 0; |
||||
fr->end_os = 0; |
||||
fr->fullend_os = 0; |
||||
} |
||||
|
||||
void frame_gapless_realinit( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->begin_os = frame_ins2outs( fr, fr->begin_s ); |
||||
fr->end_os = frame_ins2outs( fr, fr->end_s ); |
||||
|
||||
if( fr->gapless_frames > 0 ) |
||||
fr->fullend_os = frame_ins2outs( fr, fr->gapless_frames * fr->spf ); |
||||
else fr->fullend_os = 0; |
||||
} |
||||
|
||||
// at least note when there is trouble...
|
||||
void frame_gapless_update( mpg123_handle_t *fr, mpg_off_t total_samples ) |
||||
{ |
||||
mpg_off_t gapless_samples = fr->gapless_frames * fr->spf; |
||||
|
||||
if( fr->gapless_frames < 1 ) |
||||
return; |
||||
|
||||
if( gapless_samples > total_samples ) |
||||
{ |
||||
// This invalidates the current position... but what should I do?
|
||||
frame_gapless_init( fr, -1, 0, 0 ); |
||||
frame_gapless_realinit( fr ); |
||||
fr->lastframe = -1; |
||||
fr->lastoff = 0; |
||||
} |
||||
} |
||||
|
||||
// compute the needed frame to ignore from, for getting accurate/consistent output for intended firstframe.
|
||||
static mpg_off_t ignoreframe( mpg123_handle_t *fr ) |
||||
{ |
||||
mpg_off_t preshift = fr->p.preframes; |
||||
|
||||
// layer 3 _really_ needs at least one frame before.
|
||||
if( fr->lay == 3 && preshift < 1 ) |
||||
preshift = 1; |
||||
|
||||
// layer 1 & 2 really do not need more than 2.
|
||||
if(fr->lay != 3 && preshift > 2 ) |
||||
preshift = 2; |
||||
|
||||
return fr->firstframe - preshift; |
||||
} |
||||
|
||||
// the frame seek... this is not simply the seek to fe * fr->spf samples in output because we think of _input_ frames here.
|
||||
// seek to frame offset 1 may be just seek to 200 samples offset in output since the beginning of first frame is delay/padding.
|
||||
// hm, is that right? OK for the padding stuff, but actually, should the decoder delay be better totally hidden or not?
|
||||
// with gapless, even the whole frame position could be advanced further than requested (since Homey don't play dat).
|
||||
void frame_set_frameseek( mpg123_handle_t *fr, mpg_off_t fe ) |
||||
{ |
||||
fr->firstframe = fe; |
||||
|
||||
if( fr->p.flags & MPG123_GAPLESS && fr->gapless_frames > 0 ) |
||||
{ |
||||
// take care of the beginning...
|
||||
mpg_off_t beg_f = frame_offset( fr, fr->begin_os ); |
||||
|
||||
if( fe <= beg_f ) |
||||
{ |
||||
fr->firstframe = beg_f; |
||||
fr->firstoff = fr->begin_os - frame_outs( fr, beg_f ); |
||||
} |
||||
else |
||||
{ |
||||
fr->firstoff = 0; |
||||
} |
||||
|
||||
// the end is set once for a track at least, on the frame_set_frameseek called in get_next_frame()
|
||||
if( fr->end_os > 0 ) |
||||
{ |
||||
fr->lastframe = frame_offset( fr, fr->end_os ); |
||||
fr->lastoff = fr->end_os - frame_outs( fr, fr->lastframe ); |
||||
} |
||||
else |
||||
{ |
||||
fr->lastframe = -1; |
||||
fr->lastoff = 0; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
fr->firstoff = fr->lastoff = 0; |
||||
fr->lastframe = -1; |
||||
} |
||||
|
||||
fr->ignoreframe = ignoreframe( fr ); |
||||
} |
||||
|
||||
void frame_skip( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->lay == 3 ) |
||||
set_pointer( fr, 512 ); |
||||
} |
||||
|
||||
// sample accurate seek prepare for decoder.
|
||||
// this gets unadjusted output samples and takes resampling into account
|
||||
void frame_set_seek( mpg123_handle_t *fr, mpg_off_t sp ) |
||||
{ |
||||
fr->firstframe = frame_offset( fr, sp ); |
||||
fr->ignoreframe = ignoreframe( fr ); |
||||
fr->firstoff = sp - frame_outs( fr, fr->firstframe ); |
||||
} |
||||
|
||||
static int get_rva( mpg123_handle_t *fr, double *peak, double *gain ) |
||||
{ |
||||
double p = -1; |
||||
double g = 0; |
||||
int ret = 0; |
||||
|
||||
if( fr->p.rva ) |
||||
{ |
||||
int rt = 0; |
||||
|
||||
// should one assume a zero RVA as no RVA?
|
||||
if( fr->p.rva == 2 && fr->rva.level[1] != -1 ) |
||||
rt = 1; |
||||
|
||||
if( fr->rva.level[rt] != -1 ) |
||||
{ |
||||
p = fr->rva.peak[rt]; |
||||
g = fr->rva.gain[rt]; |
||||
ret = 1; // success.
|
||||
} |
||||
} |
||||
|
||||
if( peak != NULL ) *peak = p; |
||||
if( gain != NULL ) *gain = g; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
// adjust the volume, taking both fr->outscale and rva values into account
|
||||
void do_rva( mpg123_handle_t *fr ) |
||||
{ |
||||
double peak = 0; |
||||
double gain = 0; |
||||
double newscale; |
||||
double rvafact = 1; |
||||
|
||||
if( get_rva( fr, &peak, &gain )) |
||||
rvafact = pow( 10, gain / 20 ); |
||||
|
||||
newscale = fr->p.outscale * rvafact; |
||||
|
||||
// if peak is unknown (== 0) this check won't hurt
|
||||
if(( peak * newscale ) > 1.0 ) |
||||
newscale = 1.0 / peak; |
||||
|
||||
// first rva setting is forced with fr->lastscale < 0
|
||||
if( newscale != fr->lastscale || fr->decoder_change ) |
||||
{ |
||||
fr->lastscale = newscale; |
||||
// it may be too early, actually.
|
||||
if( fr->make_decode_tables != NULL ) |
||||
fr->make_decode_tables( fr ); // the actual work
|
||||
} |
||||
} |
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
frame.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef FRAME_H |
||||
#define FRAME_H |
||||
|
||||
#define MAXFRAMESIZE 3456 // max = 1728
|
||||
#define NUM_CHANNELS 2 |
||||
|
||||
typedef struct |
||||
{ |
||||
short bits; |
||||
short d; |
||||
} al_table_t; |
||||
|
||||
// the output buffer, used to be pcm_sample, pcm_point and audiobufsize
|
||||
typedef struct outbuffer_s |
||||
{ |
||||
byte *data; // main data pointer, aligned
|
||||
byte *p; // read pointer
|
||||
size_t fill; // fill from read pointer
|
||||
size_t size; |
||||
byte *rdata; // unaligned base pointer
|
||||
} outbuffer_t; |
||||
|
||||
typedef struct audioformat_s |
||||
{ |
||||
int encoding; // final encoding, after post-processing.
|
||||
int encsize; // size of one sample in bytes, plain int should be fine here...
|
||||
int dec_enc; // encoding of decoder synth.
|
||||
int dec_encsize; // size of one decoder sample.
|
||||
int channels; |
||||
int rate; |
||||
} audioformat_t; |
||||
|
||||
typedef struct mpg123_parm_s |
||||
{ |
||||
int verbose; // verbose level
|
||||
long flags; // combination of above
|
||||
int down_sample; |
||||
int rva; // (which) rva to do: 0: nothing, 1: radio/mix/track 2: album/audiophile
|
||||
long halfspeed; |
||||
long doublespeed; |
||||
long timeout; |
||||
|
||||
char audio_caps[NUM_CHANNELS][MPG123_RATES+1][MPG123_ENCODINGS]; |
||||
double outscale; |
||||
long resync_limit; |
||||
long index_size; // Long, because: negative values have a meaning.
|
||||
long preframes; |
||||
long feedpool; |
||||
long feedbuffer; |
||||
} mpg123_parm_t; |
||||
|
||||
// generic init, does not include dynamic buffers
|
||||
void frame_init( mpg123_handle_t *fr ); |
||||
void frame_init_par( mpg123_handle_t *fr, mpg123_parm_t *mp ); |
||||
int frame_outbuffer( mpg123_handle_t *fr ); |
||||
int frame_output_format( mpg123_handle_t *fr ); |
||||
int frame_buffers( mpg123_handle_t *fr ); |
||||
int frame_reset( mpg123_handle_t *fr ); |
||||
int frame_buffers_reset( mpg123_handle_t *fr ); |
||||
void frame_exit( mpg123_handle_t *fr ); |
||||
int frame_index_setup( mpg123_handle_t *fr ); |
||||
mpg_off_t frame_expect_outsamples( mpg123_handle_t *fr ); |
||||
mpg_off_t frame_offset( mpg123_handle_t *fr, mpg_off_t outs ); |
||||
void frame_gapless_init( mpg123_handle_t *fr, mpg_off_t framecount, mpg_off_t bskip, mpg_off_t eskip ); |
||||
void frame_gapless_realinit( mpg123_handle_t *fr ); |
||||
void frame_gapless_update( mpg123_handle_t *fr, mpg_off_t total_samples ); |
||||
mpg_off_t frame_index_find( mpg123_handle_t *fr, mpg_off_t want_frame, mpg_off_t* get_frame ); |
||||
mpg_off_t frame_outs( mpg123_handle_t *fr, mpg_off_t num ); |
||||
void frame_set_seek( mpg123_handle_t *fr, mpg_off_t sp ); |
||||
void frame_set_frameseek( mpg123_handle_t *fr, mpg_off_t fe ); |
||||
int frame_fill_toc( mpg123_handle_t *fr, byte *in ); |
||||
void frame_skip( mpg123_handle_t *fr ); |
||||
void do_rva( mpg123_handle_t *fr ); |
||||
|
||||
#endif//FRAME_H
|
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
getbits.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef GETBITS_H |
||||
#define GETBITS_H |
||||
|
||||
#define backbits( fr, nob ) ((void)( \ |
||||
fr->bitindex -= nob, \ |
||||
fr->wordpointer += (fr->bitindex>>3), \ |
||||
fr->bitindex &= 0x7 )) |
||||
|
||||
#define getbitoffset( fr ) ((-fr->bitindex) & 0x7) |
||||
#define getbyte( fr ) (*fr->wordpointer++) |
||||
|
||||
#define skipbits( fr, nob ) fr->ultmp = ( \ |
||||
fr->ultmp = fr->wordpointer[0], fr->ultmp <<= 8, fr->ultmp |= fr->wordpointer[1], \ |
||||
fr->ultmp <<= 8, fr->ultmp |= fr->wordpointer[2], fr->ultmp <<= fr->bitindex, \ |
||||
fr->ultmp &= 0xffffff, fr->bitindex += nob, \ |
||||
fr->ultmp >>= (24-nob), fr->wordpointer += (fr->bitindex>>3), \ |
||||
fr->bitindex &= 7 ) |
||||
|
||||
#define getbits_fast( fr, nob )( \ |
||||
fr->ultmp = (byte) (fr->wordpointer[0] << fr->bitindex), \ |
||||
fr->ultmp |= ((ulong)fr->wordpointer[1] << fr->bitindex) >> 8, \ |
||||
fr->ultmp <<= nob, fr->ultmp >>= 8, \ |
||||
fr->bitindex += nob, fr->wordpointer += (fr->bitindex >> 3), \ |
||||
fr->bitindex &= 7, fr->ultmp ) |
||||
|
||||
#define get1bit( fr ) ( \ |
||||
fr->uctmp = *fr->wordpointer << fr->bitindex, fr->bitindex++, \ |
||||
fr->wordpointer += (fr->bitindex >> 3), fr->bitindex &= 7, fr->uctmp >> 7 ) |
||||
|
||||
// 24 is enough because tab13 has max. a 19 bit huffvector
|
||||
#define BITSHIFT ((sizeof(long) - 1) * 8) |
||||
|
||||
#define REFRESH_MASK \ |
||||
while( num < BITSHIFT ) { \ |
||||
mask |= ((ulong)getbyte( fr )) << (BITSHIFT - num); \ |
||||
num += 8; \ |
||||
part2remain -= 8; } |
||||
|
||||
static uint getbits( mpg123_handle_t *fr, int number_of_bits ) |
||||
{ |
||||
ulong rval; |
||||
|
||||
rval = fr->wordpointer[0]; |
||||
rval <<= 8; |
||||
rval |= fr->wordpointer[1]; |
||||
rval <<= 8; |
||||
rval |= fr->wordpointer[2]; |
||||
|
||||
rval <<= fr->bitindex; |
||||
rval &= 0xffffff; |
||||
|
||||
fr->bitindex += number_of_bits; |
||||
rval >>= (24-number_of_bits); |
||||
|
||||
fr->wordpointer += (fr->bitindex>>3); |
||||
fr->bitindex &= 7; |
||||
|
||||
return rval; |
||||
} |
||||
|
||||
#endif//GETBITS_H
|
@ -0,0 +1,337 @@
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
huffman.h: huffman tables ... recalcualted to work with optimized decoder scheme (MH) |
||||
|
||||
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1 |
||||
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
||||
initially written by Michael Hipp |
||||
|
||||
probably we could save a few bytes of memory, because the |
||||
smaller tables are often the part of a bigger table |
||||
*/ |
||||
|
||||
|
||||
#ifndef HUFFMAN_H_ |
||||
#define HUFFMAN_H_ |
||||
|
||||
struct newhuff |
||||
{ |
||||
uint linbits; |
||||
const short *table; |
||||
}; |
||||
|
||||
static const short tab0[] = |
||||
{ |
||||
0 |
||||
}; |
||||
|
||||
static const short tab1[] = |
||||
{ |
||||
-5, -3, -1, 17, 1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab2[] = |
||||
{ |
||||
-15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1, |
||||
16, 0 |
||||
}; |
||||
|
||||
static const short tab3[] = |
||||
{ |
||||
-13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1, |
||||
1, 0 |
||||
}; |
||||
|
||||
static const short tab5[] = |
||||
{ |
||||
-29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19, |
||||
3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab6[] = |
||||
{ |
||||
-25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19, |
||||
49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab7[] = |
||||
{ |
||||
-69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83, |
||||
-1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1, |
||||
80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7, |
||||
-3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18, |
||||
-5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab8[] = |
||||
{ |
||||
-65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83, |
||||
-3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52, |
||||
67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4, |
||||
64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1, |
||||
2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab9[] = |
||||
{ |
||||
-63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1, |
||||
84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67, |
||||
-1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5, |
||||
-3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2, |
||||
18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab10[] = |
||||
{ |
||||
-125,-121,-111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118, |
||||
87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3, |
||||
-1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1, |
||||
100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23, |
||||
-17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81, |
||||
-1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7, |
||||
-3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1, |
||||
50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1, |
||||
2, 32, 17, -1, 1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab11[] = |
||||
{ |
||||
-121,-113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117, |
||||
-3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55, |
||||
-1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114, |
||||
-1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96, |
||||
-1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38, |
||||
6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1, |
||||
36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50, |
||||
-1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2, |
||||
32, 17, -3, -1, 1, 16, 0 |
||||
}; |
||||
|
||||
static const short tab12[] = |
||||
{ |
||||
-115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87, |
||||
117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115, |
||||
85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7, |
||||
112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5, |
||||
-1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37, |
||||
82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4, |
||||
36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3, |
||||
-1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1, |
||||
2, 32, 0, 17, -1, 1, 16 |
||||
}; |
||||
|
||||
static const short tab13[] = |
||||
{ |
||||
-509,-503,-475,-405,-333,-265,-205,-153,-115, -83, -53, -35, -21, -13, -9, |
||||
-7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238, |
||||
207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1, |
||||
236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249, |
||||
234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158, |
||||
-5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1, |
||||
203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245, |
||||
231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1, |
||||
63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15, |
||||
-5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1, |
||||
200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1, |
||||
240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1, |
||||
46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3, |
||||
-1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1, |
||||
198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5, |
||||
-1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167, |
||||
151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76, |
||||
196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137, |
||||
28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106, |
||||
-5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43, |
||||
-1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178, |
||||
-11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1, |
||||
58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161, |
||||
-3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88, |
||||
-1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1, |
||||
131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25, |
||||
145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100, |
||||
40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113, |
||||
-1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38, |
||||
-1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6, |
||||
96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81, |
||||
-7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11, |
||||
-5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3, |
||||
-1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab15[] = |
||||
{ |
||||
-495,-445,-355,-263,-183,-115, -77, -43, -27, -13, -7, -3, -1, 255, 239, |
||||
-1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237, |
||||
191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3, |
||||
-1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219, |
||||
-3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173, |
||||
-3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246, |
||||
-3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9, |
||||
-3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243, |
||||
216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1, |
||||
31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1, |
||||
125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3, |
||||
-1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5, |
||||
-1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124, |
||||
199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1, |
||||
198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183, |
||||
-5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76, |
||||
-1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1, |
||||
122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15, |
||||
-7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106, |
||||
-5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5, |
||||
-1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74, |
||||
-1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1, |
||||
42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134, |
||||
73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29, |
||||
-13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7, |
||||
-3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7, |
||||
-3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86, |
||||
-3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100, |
||||
23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69, |
||||
-1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9, |
||||
-5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1, |
||||
5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20, |
||||
4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48, |
||||
34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab16[] = |
||||
{ |
||||
-509,-503,-461,-323,-103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223, |
||||
253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3, |
||||
-1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5, |
||||
-3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19, |
||||
-13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1, |
||||
238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5, |
||||
-1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125, |
||||
94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13, |
||||
-5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3, |
||||
-1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186, |
||||
-1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1, |
||||
214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169, |
||||
-5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213, |
||||
-3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154, |
||||
108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1, |
||||
153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1, |
||||
192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45, |
||||
-1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107, |
||||
-1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12, |
||||
-1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1, |
||||
178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74, |
||||
164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33, |
||||
-19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3, |
||||
-1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147, |
||||
-1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1, |
||||
145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3, |
||||
-1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1, |
||||
8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3, |
||||
-1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1, |
||||
99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9, |
||||
-5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33, |
||||
-23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, |
||||
-5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1, |
||||
3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab24[] = |
||||
{ |
||||
-451,-117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1, |
||||
207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9, |
||||
-5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79, |
||||
244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31, |
||||
240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1, |
||||
236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3, |
||||
-1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3, |
||||
-1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255,-235, |
||||
-143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3, |
||||
-1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9, |
||||
-5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1, |
||||
78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185, |
||||
170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199, |
||||
77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3, |
||||
-1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3, |
||||
-1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196, |
||||
-3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1, |
||||
167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1, |
||||
137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10, |
||||
26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9, |
||||
144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165, |
||||
27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135, |
||||
-1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104, |
||||
-1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3, |
||||
-1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3, |
||||
-1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7, |
||||
-3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86, |
||||
101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15, |
||||
-7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84, |
||||
-7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1, |
||||
83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5, |
||||
80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5, |
||||
-1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1, |
||||
3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab_c0[] = |
||||
{ |
||||
-29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5, |
||||
9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8, |
||||
0 |
||||
}; |
||||
|
||||
static const short tab_c1[] = |
||||
{ |
||||
-15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9, |
||||
8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1, |
||||
0 |
||||
}; |
||||
|
||||
static const struct newhuff ht[] = |
||||
{ |
||||
{ 0 , tab0 }, |
||||
{ 0 , tab1 }, |
||||
{ 0 , tab2 }, |
||||
{ 0 , tab3 }, |
||||
{ 0 , tab0 }, |
||||
{ 0 , tab5 }, |
||||
{ 0 , tab6 }, |
||||
{ 0 , tab7 }, |
||||
{ 0 , tab8 }, |
||||
{ 0 , tab9 }, |
||||
{ 0 , tab10 }, |
||||
{ 0 , tab11 }, |
||||
{ 0 , tab12 }, |
||||
{ 0 , tab13 }, |
||||
{ 0 , tab0 }, |
||||
{ 0 , tab15 }, |
||||
|
||||
{ 1 , tab16 }, |
||||
{ 2 , tab16 }, |
||||
{ 3 , tab16 }, |
||||
{ 4 , tab16 }, |
||||
{ 6 , tab16 }, |
||||
{ 8 , tab16 }, |
||||
{ 10, tab16 }, |
||||
{ 13, tab16 }, |
||||
{ 4 , tab24 }, |
||||
{ 5 , tab24 }, |
||||
{ 6 , tab24 }, |
||||
{ 7 , tab24 }, |
||||
{ 8 , tab24 }, |
||||
{ 9 , tab24 }, |
||||
{ 11, tab24 }, |
||||
{ 13, tab24 } |
||||
}; |
||||
|
||||
static const struct newhuff htc[] = |
||||
{ |
||||
{ 0 , tab_c0 }, |
||||
{ 0 , tab_c1 } |
||||
}; |
||||
|
||||
#endif//HUFFMAN_H
|
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
index.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include "index.h" |
||||
|
||||
// the next expected frame offset, one step ahead.
|
||||
static mpg_off_t fi_next( frame_index_t *fi ) |
||||
{ |
||||
return (mpg_off_t)fi->fill*fi->step; |
||||
} |
||||
|
||||
// shrink down the used index to the half.
|
||||
// be careful with size = 1 ... there's no shrinking possible there.
|
||||
static void fi_shrink( frame_index_t *fi ) |
||||
{ |
||||
if( fi->fill < 2 ) |
||||
{ |
||||
return; // won't shrink below 1.
|
||||
} |
||||
else |
||||
{ |
||||
size_t c; |
||||
|
||||
// double the step, half the fill. Should work as well for fill%2 = 1
|
||||
fi->step *= 2; |
||||
fi->fill /= 2; |
||||
|
||||
// move the data down.
|
||||
for( c = 0; c < fi->fill; ++c ) |
||||
fi->data[c] = fi->data[2*c]; |
||||
} |
||||
|
||||
fi->next = fi_next( fi ); |
||||
} |
||||
|
||||
void fi_init( frame_index_t *fi ) |
||||
{ |
||||
fi->data = NULL; |
||||
fi->step = 1; |
||||
fi->fill = 0; |
||||
fi->size = 0; |
||||
fi->grow_size = 0; |
||||
fi->next = fi_next( fi ); |
||||
} |
||||
|
||||
void fi_exit( frame_index_t *fi ) |
||||
{ |
||||
if( fi->size && fi->data != NULL ) |
||||
free( fi->data ); |
||||
|
||||
fi_init( fi ); // be prepared for further fun, still.
|
||||
} |
||||
|
||||
int fi_resize( frame_index_t *fi, size_t newsize ) |
||||
{ |
||||
mpg_off_t *newdata = NULL; |
||||
|
||||
if( newsize == fi->size ) |
||||
return 0; |
||||
|
||||
if( newsize > 0 && newsize < fi->size ) |
||||
{ |
||||
// when we reduce buffer size a bit, shrink stuff.
|
||||
while( fi->fill > newsize ) |
||||
fi_shrink( fi ); |
||||
} |
||||
|
||||
newdata = realloc( fi->data, newsize * sizeof( mpg_off_t )); |
||||
if( newsize == 0 || newdata != NULL ) |
||||
{ |
||||
fi->data = newdata; |
||||
fi->size = newsize; |
||||
|
||||
if( fi->fill > fi->size ) |
||||
fi->fill = fi->size; |
||||
|
||||
fi->next = fi_next( fi ); |
||||
|
||||
return 0; |
||||
} |
||||
else |
||||
{ |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
void fi_add( frame_index_t *fi, mpg_off_t pos ) |
||||
{ |
||||
if( fi->fill == fi->size ) |
||||
{ |
||||
mpg_off_t framenum = fi->fill*fi->step; |
||||
|
||||
// index is full, we need to shrink... or grow.
|
||||
// store the current frame number to check later if we still want it.
|
||||
|
||||
// if we want not / cannot grow, we shrink.
|
||||
if( !( fi->grow_size && fi_resize( fi, fi->size+fi->grow_size ) == 0 )) |
||||
fi_shrink( fi ); |
||||
|
||||
// now check if we still want to add this frame (could be that not, because of changed step).
|
||||
if( fi->next != framenum ) |
||||
return; |
||||
} |
||||
|
||||
// when we are here, we want that frame.
|
||||
if( fi->fill < fi->size ) // safeguard for size = 1, or just generally
|
||||
{ |
||||
fi->data[fi->fill] = pos; |
||||
fi->fill++; |
||||
fi->next = fi_next( fi ); |
||||
} |
||||
} |
||||
|
||||
int fi_set( frame_index_t *fi, mpg_off_t *offsets, mpg_off_t step, size_t fill ) |
||||
{ |
||||
if( fi_resize( fi, fill ) == -1 ) |
||||
return -1; |
||||
|
||||
fi->step = step; |
||||
|
||||
if( offsets != NULL ) |
||||
{ |
||||
memcpy( fi->data, offsets, fill * sizeof( mpg_off_t )); |
||||
fi->fill = fill; |
||||
} |
||||
else |
||||
{ |
||||
// allocation only, no entries in index yet
|
||||
fi->fill = 0; |
||||
} |
||||
|
||||
fi->next = fi_next( fi ); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void fi_reset( frame_index_t *fi ) |
||||
{ |
||||
fi->fill = 0; |
||||
fi->step = 1; |
||||
fi->next = fi_next( fi ); |
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
index.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef INDEX_H |
||||
#define INDEX_H |
||||
|
||||
typedef struct frame_index_s |
||||
{ |
||||
mpg_off_t *data; // actual data, the frame positions
|
||||
mpg_off_t step; // advancement in frame number per index point
|
||||
mpg_off_t next; // frame offset supposed to come next into the index
|
||||
size_t size; // total number of possible entries
|
||||
size_t fill; // number of used entries
|
||||
size_t grow_size;// if > 0: index allowed to grow on need with these steps, instead of lowering resolution
|
||||
} frame_index_t; |
||||
|
||||
// the condition for a framenum to be appended to the index.
|
||||
#define FI_NEXT( fi, framenum ) ((fi).size && framenum == (fi).next) |
||||
|
||||
// initialize stuff, set things to zero and NULL...
|
||||
void fi_init( frame_index_t *fi ); |
||||
// deallocate/zero things.
|
||||
void fi_exit( frame_index_t *fi ); |
||||
// prepare a given size, preserving current fill, if possible.
|
||||
int fi_resize( frame_index_t *fi, size_t newsize ); |
||||
// append a frame position, reducing index density if needed.
|
||||
void fi_add( frame_index_t *fi, mpg_off_t pos ); |
||||
// replace the frame index
|
||||
int fi_set( frame_index_t *fi, mpg_off_t *offsets, mpg_off_t step, size_t fill ); |
||||
// empty the index (setting fill=0 and step=1), but keep current size.
|
||||
void fi_reset( frame_index_t *fi ); |
||||
|
||||
#endif//INDEX_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
libmpg.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include "libmpg.h" |
||||
|
||||
void *create_decoder( int *error ) |
||||
{ |
||||
void *mpg; |
||||
int ret; |
||||
|
||||
if( error ) *error = 0; |
||||
mpg123_init(); |
||||
|
||||
mpg = mpg123_new( &ret ); |
||||
if( !mpg ) return NULL; |
||||
|
||||
ret = mpg123_param( mpg, MPG123_FLAGS, MPG123_FUZZY|MPG123_SEEKBUFFER|MPG123_GAPLESS ); |
||||
if( ret != MPG123_OK && error ) |
||||
*error = 1; |
||||
|
||||
// let the seek index auto-grow and contain an entry for every frame
|
||||
ret = mpg123_param( mpg, MPG123_INDEX_SIZE, -1 ); |
||||
if( ret != MPG123_OK && error ) |
||||
*error = 1; |
||||
|
||||
return mpg; |
||||
} |
||||
|
||||
int feed_mpeg_header( void *mpg, const char *data, long bufsize, long streamsize, wavinfo_t *sc ) |
||||
{ |
||||
mpg123_handle_t *mh = (mpg123_handle_t *)mpg; |
||||
int ret, no; |
||||
|
||||
if( !mh || !sc ) return 0; |
||||
|
||||
ret = mpg123_open_feed( mh ); |
||||
if( ret != MPG123_OK ) |
||||
return 0; |
||||
|
||||
// feed input chunk and get first chunk of decoded audio.
|
||||
ret = mpg123_decode( mh, data, bufsize, NULL, 0, NULL ); |
||||
|
||||
if( ret != MPG123_NEW_FORMAT ) |
||||
return 0; // there were errors
|
||||
|
||||
mpg123_getformat( mh, &sc->rate, &sc->channels, &no ); |
||||
mpg123_format_none( mh ); |
||||
mpg123_format( mh, sc->rate, sc->channels, MPG123_ENC_SIGNED_16 ); |
||||
|
||||
// some hacking to get function get_songlen to working properly
|
||||
mh->rdat.filelen = streamsize; |
||||
sc->playtime = get_songlen( mh, -1 ) * 1000; |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
int feed_mpeg_stream( void *mpg, const char *data, long bufsize, char *outbuf, size_t *outsize ) |
||||
{ |
||||
switch( mpg123_decode( mpg, data, bufsize, outbuf, OUTBUF_SIZE, outsize )) |
||||
{ |
||||
case MPG123_NEED_MORE: |
||||
return MP3_NEED_MORE; |
||||
case MPG123_OK: |
||||
return MP3_OK; |
||||
default: |
||||
return MP3_ERR; |
||||
} |
||||
} |
||||
|
||||
int open_mpeg_stream( void *mpg, void *file, pfread f_read, pfseek f_seek, wavinfo_t *sc ) |
||||
{ |
||||
mpg123_handle_t *mh = (mpg123_handle_t *)mpg; |
||||
int ret, no; |
||||
|
||||
if( !mh || !sc ) return 0; |
||||
|
||||
ret = mpg123_replace_reader_handle( mh, f_read, f_seek, NULL ); |
||||
if( ret != MPG123_OK ) |
||||
return 0; |
||||
|
||||
ret = mpg123_open_handle( mh, file ); |
||||
if( ret != MPG123_OK ) |
||||
return 0; |
||||
|
||||
ret = mpg123_getformat( mh, &sc->rate, &sc->channels, &no ); |
||||
if( ret != MPG123_OK ) |
||||
return 0; |
||||
|
||||
mpg123_format_none( mh ); |
||||
mpg123_format( mh, sc->rate, sc->channels, MPG123_ENC_SIGNED_16 ); |
||||
sc->playtime = get_songlen( mh, -1 ) * 1000; |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
int read_mpeg_stream( void *mpg, char *outbuf, size_t *outsize ) |
||||
{ |
||||
switch( mpg123_read( mpg, outbuf, OUTBUF_SIZE, outsize )) |
||||
{ |
||||
case MPG123_OK: |
||||
return MP3_OK; |
||||
default: |
||||
return MP3_ERR; |
||||
} |
||||
} |
||||
|
||||
int get_stream_pos( void *mpg ) |
||||
{ |
||||
return mpg123_tell( mpg ); |
||||
} |
||||
|
||||
int set_stream_pos( void *mpg, int curpos ) |
||||
{ |
||||
return mpg123_seek( mpg, curpos, SEEK_SET ); |
||||
} |
||||
|
||||
void close_decoder( void *mpg ) |
||||
{ |
||||
mpg123_delete( mpg ); |
||||
mpg123_exit(); |
||||
} |
@ -0,0 +1,182 @@
@@ -0,0 +1,182 @@
|
||||
# Microsoft Developer Studio Project File - Name="libmpg" - Package Owner=<4> |
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00 |
||||
# ** DO NOT EDIT ** |
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104 |
||||
|
||||
CFG=libmpg - Win32 Release |
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE, |
||||
!MESSAGE use the Export Makefile command and run |
||||
!MESSAGE |
||||
!MESSAGE NMAKE /f "libmpg.mak". |
||||
!MESSAGE |
||||
!MESSAGE You can specify a configuration when running NMAKE |
||||
!MESSAGE by defining the macro CFG on the command line. For example: |
||||
!MESSAGE |
||||
!MESSAGE NMAKE /f "libmpg.mak" CFG="libmpg - Win32 Release" |
||||
!MESSAGE |
||||
!MESSAGE Possible choices for configuration are: |
||||
!MESSAGE |
||||
!MESSAGE "libmpg - Win32 Release" (based on "Win32 (x86) Static Library") |
||||
!MESSAGE "libmpg - Win32 Debug" (based on "Win32 (x86) Static Library") |
||||
!MESSAGE |
||||
|
||||
# Begin Project |
||||
# PROP AllowPerConfigDependencies 0 |
||||
# PROP Scc_ProjName "" |
||||
# PROP Scc_LocalPath "" |
||||
CPP=cl.exe |
||||
RSC=rc.exe |
||||
|
||||
!IF "$(CFG)" == "libmpg - Win32 Release" |
||||
|
||||
# PROP BASE Use_MFC 0 |
||||
# PROP BASE Use_Debug_Libraries 0 |
||||
# PROP BASE Output_Dir "Release" |
||||
# PROP BASE Intermediate_Dir "Release" |
||||
# PROP BASE Target_Dir "" |
||||
# PROP Use_MFC 0 |
||||
# PROP Use_Debug_Libraries 0 |
||||
# PROP Output_Dir "..\temp\libmpg\!release" |
||||
# PROP Intermediate_Dir "..\temp\libmpg\!release" |
||||
# PROP Target_Dir "" |
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c |
||||
# ADD CPP /nologo /MD /W3 /GX /O1 /I "./" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c |
||||
# SUBTRACT CPP /YX /Yc /Yu |
||||
# ADD BASE RSC /l 0x419 /d "NDEBUG" |
||||
# ADD RSC /l 0x419 /d "NDEBUG" |
||||
BSC32=bscmake.exe |
||||
# ADD BASE BSC32 /nologo |
||||
# ADD BSC32 /nologo |
||||
LIB32=link.exe -lib |
||||
# ADD BASE LIB32 /nologo |
||||
# ADD LIB32 /nologo /out:"..\mpeg.lib" |
||||
|
||||
!ELSEIF "$(CFG)" == "libmpg - Win32 Debug" |
||||
|
||||
# PROP BASE Use_MFC 0 |
||||
# PROP BASE Use_Debug_Libraries 1 |
||||
# PROP BASE Output_Dir "Debug" |
||||
# PROP BASE Intermediate_Dir "Debug" |
||||
# PROP BASE Target_Dir "" |
||||
# PROP Use_MFC 0 |
||||
# PROP Use_Debug_Libraries 1 |
||||
# PROP Output_Dir "..\temp\libmpg\!debug" |
||||
# PROP Intermediate_Dir "..\temp\libmpg\!debug" |
||||
# PROP Target_Dir "" |
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c |
||||
# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "./" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FAs /FR /FD /GZ /c |
||||
# SUBTRACT CPP /YX /Yc /Yu |
||||
# ADD BASE RSC /l 0x419 /d "_DEBUG" |
||||
# ADD RSC /l 0x419 /d "_DEBUG" |
||||
BSC32=bscmake.exe |
||||
# ADD BASE BSC32 /nologo |
||||
# ADD BSC32 /nologo |
||||
LIB32=link.exe -lib |
||||
# ADD BASE LIB32 /nologo |
||||
# ADD LIB32 /nologo /out:"..\mpeg_dbg.lib" |
||||
|
||||
!ENDIF |
||||
|
||||
# Begin Target |
||||
|
||||
# Name "libmpg - Win32 Release" |
||||
# Name "libmpg - Win32 Debug" |
||||
# Begin Group "Source Files" |
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\dct36.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\dct64.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\format.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\frame.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\index.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\layer3.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\libmpg.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\mpg123.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\parse.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\reader.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\synth.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\tabinit.c |
||||
# End Source File |
||||
# End Group |
||||
# Begin Group "Header Files" |
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl" |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\fmt123.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\frame.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\getbits.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\huffman.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\index.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\libmpg.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\mpg123.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\reader.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\sample.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\synth.h |
||||
# End Source File |
||||
# End Group |
||||
# End Target |
||||
# End Project |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
libmpg.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef LIBMPG_H |
||||
#define LIBMPG_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
// error codes
|
||||
#define MP3_ERR -1 |
||||
#define MP3_OK 0 |
||||
#define MP3_NEED_MORE 1 |
||||
|
||||
#define OUTBUF_SIZE 8192 // don't change!
|
||||
|
||||
typedef struct |
||||
{ |
||||
int rate; // num samples per second (e.g. 11025 - 11 khz)
|
||||
int channels; // num channels (1 - mono, 2 - stereo)
|
||||
int playtime; // stream size in milliseconds
|
||||
} wavinfo_t; |
||||
|
||||
// custom stdio
|
||||
typedef long (*pfread)( void *handle, void *buf, size_t count ); |
||||
typedef long (*pfseek)( void *handle, long offset, int whence ); |
||||
|
||||
extern void *create_decoder( int *error ); |
||||
extern int feed_mpeg_header( void *mpg, const char *data, long bufsize, long streamsize, wavinfo_t *sc ); |
||||
extern int feed_mpeg_stream( void *mpg, const char *data, long bufsize, char *outbuf, size_t *outsize ); |
||||
extern int open_mpeg_stream( void *mpg, void *file, pfread f_read, pfseek f_seek, wavinfo_t *sc ); |
||||
extern int read_mpeg_stream( void *mpg, char *outbuf, size_t *outsize ); |
||||
extern int get_stream_pos( void *mpg ); |
||||
extern int set_stream_pos( void *mpg, int curpos ); |
||||
extern void close_decoder( void *mpg ); |
||||
const char *get_error( void *mpeg ); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif//LIBMPG_H
|
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
mpeghead.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef MPEGHEAD_H |
||||
#define MPEGHEAD_H |
||||
|
||||
#define HDR_SYNC 0xffe00000 |
||||
#define HDR_SYNC_VAL(h) (((h) & HDR_SYNC) >> 21) |
||||
#define HDR_VERSION 0x00180000 |
||||
#define HDR_VERSION_VAL(h) (((h) & HDR_VERSION) >> 19) |
||||
#define HDR_LAYER 0x00060000 |
||||
#define HDR_LAYER_VAL(h) (((h) & HDR_LAYER) >> 17) |
||||
#define HDR_CRC 0x00010000 |
||||
#define HDR_CRC_VAL(h) (((h) & HDR_CRC) >> 16) |
||||
#define HDR_BITRATE 0x0000f000 |
||||
#define HDR_BITRATE_VAL(h) (((h) & HDR_BITRATE) >> 12) |
||||
#define HDR_SAMPLERATE 0x00000c00 |
||||
#define HDR_SAMPLERATE_VAL(h) (((h) & HDR_SAMPLERATE) >> 10) |
||||
#define HDR_PADDING 0x00000200 |
||||
#define HDR_PADDING_VAL(h) (((h) & HDR_PADDING) >> 9) |
||||
#define HDR_PRIVATE 0x00000100 |
||||
#define HDR_PRIVATE_VAL(h) (((h) & HDR_PRIVATE) >> 8) |
||||
#define HDR_CHANNEL 0x000000c0 |
||||
#define HDR_CHANNEL_VAL(h) (((h) & HDR_CHANNEL) >> 6) |
||||
#define HDR_CHANEX 0x00000030 |
||||
#define HDR_CHANEX_VAL(h) (((h) & HDR_CHANEX) >> 4) |
||||
#define HDR_COPYRIGHT 0x00000008 |
||||
#define HDR_COPYRIGHT_VAL(h) (((h) & HDR_COPYRIGHT) >> 3) |
||||
#define HDR_ORIGINAL 0x00000004 |
||||
#define HDR_ORIGINAL_VAL(h) (((h) & HDR_ORIGINAL) >> 2) |
||||
#define HDR_EMPHASIS 0x00000003 |
||||
#define HDR_EMPHASIS_VAL(h) (((h) & HDR_EMPHASIS) >> 0) |
||||
|
||||
|
||||
// a generic mask for telling if a header is somewhat valid for the current stream.
|
||||
// meaning: Most basic info is not allowed to change.
|
||||
// checking of channel count needs to be done, too, though. So,
|
||||
// if channel count matches, frames are decoded the same way: frame buffers and decoding
|
||||
// routines can stay the same, especially frame buffers (think spf * channels!).
|
||||
#define HDR_CMPMASK (HDR_SYNC|HDR_VERSION|HDR_LAYER|HDR_SAMPLERATE) |
||||
|
||||
// A stricter mask, for matching free format headers.
|
||||
#define HDR_SAMEMASK (HDR_SYNC|HDR_VERSION|HDR_LAYER|HDR_BITRATE|HDR_SAMPLERATE|HDR_CHANNEL|HDR_CHANEX) |
||||
|
||||
// free format headers have zero bitrate value.
|
||||
#define HDR_FREE_FORMAT(head) (!(head & HDR_BITRATE)) |
||||
|
||||
// a mask for changed sampling rate (version or rate bits).
|
||||
#define HDR_SAMPMASK (HDR_VERSION|HDR_SAMPLERATE) |
||||
|
||||
#endif//MPEGHEAD_H
|
@ -0,0 +1,970 @@
@@ -0,0 +1,970 @@
|
||||
/*
|
||||
mpg123.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include "sample.h" |
||||
|
||||
static int initialized = 0; |
||||
|
||||
int mpg123_init( void ) |
||||
{ |
||||
if(( sizeof( short ) != 2 ) || ( sizeof( long ) < 4 )) |
||||
return MPG123_BAD_TYPES; |
||||
|
||||
if( initialized ) |
||||
return MPG123_OK; // no need to initialize twice
|
||||
|
||||
init_layer3(); |
||||
prepare_decode_tables(); |
||||
initialized = 1; |
||||
|
||||
#ifdef IEEE_FLOAT |
||||
// this is rather pointless but it eases my mind to check that we did
|
||||
// not enable the special rounding on a VAX or something.
|
||||
if( REAL_TO_SHORT_ACCURATE( 12345.67f ) != 12346 ) |
||||
{ |
||||
return MPG123_ERR; |
||||
} |
||||
#endif |
||||
return MPG123_OK; |
||||
} |
||||
|
||||
void mpg123_exit( void ) |
||||
{ |
||||
// nothing yet, but something later perhaps
|
||||
} |
||||
|
||||
// create a new handle with specified decoder, decoder can be "", "auto" or NULL for auto-detection
|
||||
mpg123_handle_t *mpg123_new( int *error ) |
||||
{ |
||||
return mpg123_parnew( NULL, error ); |
||||
} |
||||
|
||||
// ...the full routine with optional initial parameters to override defaults.
|
||||
mpg123_handle_t *mpg123_parnew( mpg123_parm_t *mp, int *error ) |
||||
{ |
||||
mpg123_handle_t *fr = NULL; |
||||
int err = MPG123_OK; |
||||
|
||||
if( initialized ) |
||||
fr = (mpg123_handle_t *)malloc( sizeof( mpg123_handle_t )); |
||||
else err = MPG123_NOT_INITIALIZED; |
||||
|
||||
if( fr != NULL ) |
||||
{ |
||||
frame_init_par( fr, mp ); |
||||
init_synth( fr ); |
||||
} |
||||
|
||||
if( fr != NULL ) |
||||
{ |
||||
fr->decoder_change = 1; |
||||
} |
||||
else if( err == MPG123_OK ) |
||||
{ |
||||
err = MPG123_OUT_OF_MEM; |
||||
} |
||||
|
||||
if( error != NULL ) |
||||
*error = err; |
||||
|
||||
return fr; |
||||
} |
||||
|
||||
int mpg123_par( mpg123_parm_t *mp, enum mpg123_parms key, long val ) |
||||
{ |
||||
int ret = MPG123_OK; |
||||
|
||||
if( mp == NULL ) |
||||
return MPG123_BAD_PARS; |
||||
|
||||
switch( key ) |
||||
{ |
||||
case MPG123_VERBOSE: |
||||
mp->verbose = val; |
||||
break; |
||||
case MPG123_FLAGS: |
||||
if( ret == MPG123_OK ) |
||||
mp->flags = val; |
||||
break; |
||||
case MPG123_ADD_FLAGS: |
||||
mp->flags |= val; |
||||
break; |
||||
case MPG123_REMOVE_FLAGS: |
||||
mp->flags &= ~val; |
||||
break; |
||||
case MPG123_FORCE_RATE: // should this trigger something
|
||||
if( val > 0 ) |
||||
ret = MPG123_BAD_RATE; |
||||
break; |
||||
case MPG123_DOWN_SAMPLE: |
||||
if( val != 0 ) |
||||
ret = MPG123_BAD_RATE; |
||||
break; |
||||
case MPG123_RVA: |
||||
if( val < 0 || val > MPG123_RVA_MAX ) |
||||
ret = MPG123_BAD_RVA; |
||||
else mp->rva = (int)val; |
||||
break; |
||||
case MPG123_DOWNSPEED: |
||||
mp->halfspeed = val < 0 ? 0 : val; |
||||
break; |
||||
case MPG123_UPSPEED: |
||||
mp->doublespeed = val < 0 ? 0 : val; |
||||
break; |
||||
case MPG123_OUTSCALE: |
||||
// choose the value that is non-zero, if any.
|
||||
// downscaling integers to 1.0.
|
||||
mp->outscale = (double)val / SHORT_SCALE; |
||||
break; |
||||
case MPG123_TIMEOUT: |
||||
if( val > 0 ) ret = MPG123_NO_TIMEOUT; |
||||
break; |
||||
case MPG123_RESYNC_LIMIT: |
||||
mp->resync_limit = val; |
||||
break; |
||||
case MPG123_INDEX_SIZE: |
||||
mp->index_size = val; |
||||
break; |
||||
case MPG123_PREFRAMES: |
||||
if( val >= 0 ) mp->preframes = val; |
||||
else ret = MPG123_BAD_VALUE; |
||||
break; |
||||
case MPG123_FEEDPOOL: |
||||
if( val >= 0 ) mp->feedpool = val; |
||||
else ret = MPG123_BAD_VALUE; |
||||
break; |
||||
case MPG123_FEEDBUFFER: |
||||
if( val > 0 ) mp->feedbuffer = val; |
||||
else ret = MPG123_BAD_VALUE; |
||||
break; |
||||
default: |
||||
ret = MPG123_BAD_PARAM; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int mpg123_param( mpg123_handle_t *mh, enum mpg123_parms key, long val ) |
||||
{ |
||||
int r; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
r = mpg123_par(&mh->p, key, val ); |
||||
if( r != MPG123_OK ) |
||||
{ |
||||
mh->err = r; |
||||
return MPG123_ERR; |
||||
} |
||||
else |
||||
{ |
||||
// special treatment for some settings.
|
||||
if( key == MPG123_INDEX_SIZE ) |
||||
{ |
||||
// apply frame index size and grow property on the fly.
|
||||
r = frame_index_setup( mh ); |
||||
if( r != MPG123_OK ) |
||||
mh->err = MPG123_INDEX_FAIL; |
||||
} |
||||
|
||||
// feeder pool size is applied right away, reader will react to that.
|
||||
if( key == MPG123_FEEDPOOL || key == MPG123_FEEDBUFFER ) |
||||
bc_poolsize( &mh->rdat.buffer, mh->p.feedpool, mh->p.feedbuffer ); |
||||
|
||||
return r; |
||||
} |
||||
} |
||||
|
||||
int mpg123_close( mpg123_handle_t *mh ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
// mh->rd is never NULL!
|
||||
if( mh->rd->close != NULL ) |
||||
mh->rd->close( mh ); |
||||
|
||||
if( mh->new_format ) |
||||
{ |
||||
invalidate_format( &mh->af ); |
||||
mh->new_format = 0; |
||||
} |
||||
|
||||
// always reset the frame buffers on close, so we cannot forget it in funky opening routines (wrappers, even).
|
||||
frame_reset( mh ); |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
void mpg123_delete( mpg123_handle_t *mh ) |
||||
{ |
||||
if( mh != NULL ) |
||||
{ |
||||
mpg123_close( mh ); |
||||
frame_exit( mh ); // free buffers in frame
|
||||
free( mh ); // free struct; cast?
|
||||
} |
||||
} |
||||
|
||||
int mpg123_open_handle( mpg123_handle_t *mh, void *iohandle ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
mpg123_close( mh ); |
||||
|
||||
if( mh->rdat.r_read_handle == NULL ) |
||||
{ |
||||
mh->err = MPG123_BAD_CUSTOM_IO; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
return open_stream_handle( mh, iohandle ); |
||||
} |
||||
|
||||
int mpg123_open_feed( mpg123_handle_t *mh ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
mpg123_close( mh ); |
||||
|
||||
return open_feed( mh ); |
||||
} |
||||
|
||||
int mpg123_replace_reader_handle( mpg123_handle_t *mh, mpg_ssize_t (*fread)( void*, void*, size_t), mpg_off_t (*lseek)(void*, mpg_off_t, int), void(*fclose)(void*)) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
mpg123_close( mh ); |
||||
mh->rdat.r_read_handle = fread; |
||||
mh->rdat.r_lseek_handle = lseek; |
||||
mh->rdat.cleanup_handle = fclose; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
// update decoding engine for
|
||||
// a) a new choice of decoder
|
||||
// b) a changed native format of the MPEG stream
|
||||
// ... calls are only valid after parsing some MPEG frame!
|
||||
int decode_update( mpg123_handle_t *mh ) |
||||
{ |
||||
long native_rate; |
||||
int b; |
||||
|
||||
if( mh->num < 0 ) |
||||
{ |
||||
mh->err = MPG123_BAD_DECODER_SETUP; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
mh->state_flags |= FRAME_FRESH_DECODER; |
||||
native_rate = frame_freq( mh ); |
||||
|
||||
b = frame_output_format( mh ); // select the new output format based on given constraints.
|
||||
if( b < 0 ) return MPG123_ERR; |
||||
if( b == 1 ) mh->new_format = 1; // store for later...
|
||||
|
||||
if( mh->af.rate == native_rate ) |
||||
mh->down_sample = 0; |
||||
else if( mh->af.rate == native_rate >> 1 ) |
||||
mh->down_sample = 1; |
||||
else if( mh->af.rate == native_rate >> 2 ) |
||||
mh->down_sample = 2; |
||||
else mh->down_sample = 3; // flexible (fixed) rate
|
||||
|
||||
switch( mh->down_sample ) |
||||
{ |
||||
case 0: |
||||
case 1: |
||||
case 2: |
||||
mh->down_sample_sblimit = SBLIMIT >> ( mh->down_sample ); |
||||
// with downsampling I get less samples per frame
|
||||
mh->outblock = outblock_bytes( mh, ( mh->spf >> mh->down_sample )); |
||||
break; |
||||
} |
||||
|
||||
if(!( mh->p.flags & MPG123_FORCE_MONO )) |
||||
{ |
||||
if( mh->af.channels == 1 ) |
||||
mh->single = SINGLE_MIX; |
||||
else mh->single = SINGLE_STEREO; |
||||
} |
||||
else mh->single = ( mh->p.flags & MPG123_FORCE_MONO ) - 1; |
||||
|
||||
if( set_synth_functions( mh ) != 0 ) |
||||
return -1; |
||||
|
||||
// the needed size of output buffer may have changed.
|
||||
if( frame_outbuffer( mh ) != MPG123_OK ) |
||||
return -1; |
||||
|
||||
do_rva( mh ); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
size_t mpg123_safe_buffer( void ) |
||||
{ |
||||
// real is the largest possible output
|
||||
return sizeof( float ) * 2 * 1152; |
||||
} |
||||
|
||||
size_t mpg123_outblock( mpg123_handle_t *mh ) |
||||
{ |
||||
// try to be helpful and never return zero output block size.
|
||||
if( mh != NULL && mh->outblock > 0 ) |
||||
return mh->outblock; |
||||
return mpg123_safe_buffer(); |
||||
} |
||||
|
||||
// read in the next frame we actually want for decoding.
|
||||
// this includes skipping/ignoring frames, in additon to skipping junk in the parser.
|
||||
static int get_next_frame( mpg123_handle_t *mh ) |
||||
{ |
||||
int change = mh->decoder_change; |
||||
|
||||
// ensure we got proper decoder for ignoring frames.
|
||||
// header can be changed from seeking around. But be careful: Only after at
|
||||
// least one frame got read, decoder update makes sense.
|
||||
if( mh->header_change > 1 && mh->num >= 0 ) |
||||
{ |
||||
change = 1; |
||||
mh->header_change = 0; |
||||
|
||||
if( decode_update( mh ) < 0 ) |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
do |
||||
{ |
||||
int b; |
||||
|
||||
// decode & discard some frame(s) before beginning.
|
||||
if( mh->to_ignore && mh->num < mh->firstframe && mh->num >= mh->ignoreframe ) |
||||
{ |
||||
// decoder structure must be current! decode_update has been called before...
|
||||
(mh->do_layer)( mh ); |
||||
mh->buffer.fill = 0; |
||||
mh->to_ignore = mh->to_decode = FALSE; |
||||
} |
||||
|
||||
// read new frame data; possibly breaking out here for MPG123_NEED_MORE.
|
||||
mh->to_decode = FALSE; |
||||
b = read_frame( mh ); // that sets to_decode only if a full frame was read.
|
||||
|
||||
if( b == MPG123_NEED_MORE ) |
||||
{ |
||||
return MPG123_NEED_MORE; // need another call with data
|
||||
} |
||||
else if( b <= 0 ) |
||||
{ |
||||
// more sophisticated error control?
|
||||
if( b == 0 || ( mh->rdat.filelen >= 0 && mh->rdat.filepos == mh->rdat.filelen )) |
||||
{ |
||||
// we simply reached the end.
|
||||
mh->track_frames = mh->num + 1; |
||||
|
||||
return MPG123_DONE; |
||||
} |
||||
|
||||
return MPG123_ERR; // some real error.
|
||||
} |
||||
|
||||
// now, there should be new data to decode ... and also possibly new stream properties
|
||||
if( mh->header_change > 1 ) |
||||
{ |
||||
change = 1; |
||||
mh->header_change = 0; |
||||
|
||||
// need to update decoder structure right away since frame might need to
|
||||
// be decoded on next loop iteration for properly ignoring its output.
|
||||
if( decode_update( mh ) < 0 ) |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
// now some accounting: Look at the numbers and decide if we want this frame.
|
||||
mh->playnum++; |
||||
|
||||
// plain skipping without decoding, only when frame is not ignored on next cycle.
|
||||
if( mh->num < mh->firstframe || ( mh->p.doublespeed && ( mh->playnum % mh->p.doublespeed ))) |
||||
{ |
||||
if(!( mh->to_ignore && mh->num < mh->firstframe && mh->num >= mh->ignoreframe )) |
||||
frame_skip( mh ); |
||||
} |
||||
else |
||||
{ |
||||
// or, we are finally done and have a new frame.
|
||||
break; |
||||
} |
||||
} while( 1 ); |
||||
|
||||
// if we reach this point, we got a new frame ready to be decoded.
|
||||
// all other situations resulted in returns from the loop.
|
||||
if( change ) |
||||
{ |
||||
mh->decoder_change = 0; |
||||
|
||||
if( mh->fresh ) |
||||
{ |
||||
int b = 0; |
||||
// prepare offsets for gapless decoding.
|
||||
frame_gapless_realinit( mh ); |
||||
frame_set_frameseek( mh, mh->num ); |
||||
mh->fresh = 0; |
||||
|
||||
// could this possibly happen? With a real big gapless offset...
|
||||
if( mh->num < mh->firstframe ) b = get_next_frame( mh ); |
||||
if( b < 0 ) return b; // Could be error, need for more, new format...
|
||||
} |
||||
} |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
static int init_track( mpg123_handle_t *mh ) |
||||
{ |
||||
if( track_need_init( mh )) |
||||
{ |
||||
// fresh track, need first frame for basic info.
|
||||
int b = get_next_frame( mh ); |
||||
if( b < 0 ) return b; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// from internal sample number to external.
|
||||
static mpg_off_t sample_adjust( mpg123_handle_t *mh, mpg_off_t x ) |
||||
{ |
||||
mpg_off_t s; |
||||
|
||||
if( mh->p.flags & MPG123_GAPLESS ) |
||||
{ |
||||
// it's a bit tricky to do this computation for the padding samples.
|
||||
// they are not there on the outside.
|
||||
if( x > mh->end_os ) |
||||
{ |
||||
if( x < mh->fullend_os ) |
||||
s = mh->end_os - mh->begin_os; |
||||
else s = x - (mh->fullend_os - mh->end_os + mh->begin_os); |
||||
} |
||||
else s = x - mh->begin_os; |
||||
} |
||||
else |
||||
{ |
||||
s = x; |
||||
} |
||||
|
||||
return s; |
||||
} |
||||
|
||||
// from external samples to internal
|
||||
static mpg_off_t sample_unadjust( mpg123_handle_t *mh, mpg_off_t x ) |
||||
{ |
||||
mpg_off_t s; |
||||
|
||||
if( mh->p.flags & MPG123_GAPLESS ) |
||||
{ |
||||
s = x + mh->begin_os; |
||||
// there is a hole; we don't create sample positions in there.
|
||||
// jump from the end of the gapless track directly to after the padding.
|
||||
if( s >= mh->end_os ) s += mh->fullend_os - mh->end_os; |
||||
} |
||||
else |
||||
{ |
||||
s = x; |
||||
} |
||||
|
||||
return s; |
||||
} |
||||
|
||||
// take the buffer after a frame decode (strictly: it is the data from frame fr->num!) and cut samples out.
|
||||
// fr->buffer.fill may then be smaller than before...
|
||||
static void frame_buffercheck( mpg123_handle_t *fr ) |
||||
{ |
||||
// when we have no accurate position, gapless code does not make sense.
|
||||
if( !( fr->state_flags & FRAME_ACCURATE )) |
||||
return; |
||||
|
||||
// get a grip on dirty streams that start with a gapless header.
|
||||
// simply accept all data from frames that are too much,
|
||||
// they are supposedly attached to the stream after the fact.
|
||||
if( fr->gapless_frames > 0 && fr->num >= fr->gapless_frames ) |
||||
return; |
||||
|
||||
// important: We first cut samples from the end, then cut from beginning (including left-shift of the buffer).
|
||||
// this order works also for the case where firstframe == lastframe.
|
||||
|
||||
// the last interesting (planned) frame: Only use some leading samples.
|
||||
// note a difference from the below: The last frame and offset are unchanges by seeks.
|
||||
// the lastoff keeps being valid.
|
||||
if( fr->lastframe > -1 && fr->num >= fr->lastframe ) |
||||
{ |
||||
// there can be more than one frame of padding at the end, so we ignore the whole frame if we are beyond lastframe.
|
||||
mpg_off_t byteoff = ( fr->num == fr->lastframe ) ? samples_to_bytes( fr, fr->lastoff ) : 0; |
||||
|
||||
if((mpg_off_t)fr->buffer.fill > byteoff ) |
||||
fr->buffer.fill = byteoff; |
||||
} |
||||
|
||||
// the first interesting frame: Skip some leading samples.
|
||||
if( fr->firstoff && fr->num == fr->firstframe ) |
||||
{ |
||||
mpg_off_t byteoff = samples_to_bytes( fr, fr->firstoff ); |
||||
if((mpg_off_t)fr->buffer.fill > byteoff ) |
||||
{ |
||||
fr->buffer.fill -= byteoff; |
||||
|
||||
if( fr->own_buffer ) fr->buffer.p = fr->buffer.data + byteoff; |
||||
else memmove( fr->buffer.data, fr->buffer.data + byteoff, fr->buffer.fill ); |
||||
} |
||||
else fr->buffer.fill = 0; |
||||
|
||||
// we can only reach this frame again by seeking. And on seeking, firstoff will be recomputed.
|
||||
// so it is safe to null it here (and it makes the if() decision abort earlier).
|
||||
fr->firstoff = 0; |
||||
} |
||||
} |
||||
|
||||
// not part of the api. This just decodes the frame and fills missing bits with zeroes.
|
||||
// there can be frames that are broken and thus make do_layer() fail.
|
||||
static void decode_the_frame( mpg123_handle_t *fr ) |
||||
{ |
||||
size_t needed_bytes = decoder_synth_bytes( fr, frame_expect_outsamples( fr )); |
||||
fr->clip += (fr->do_layer)(fr); |
||||
|
||||
// there could be less data than promised.
|
||||
// also, then debugging, we look out for coding errors that could result in _more_ data than expected.
|
||||
if( fr->buffer.fill < needed_bytes ) |
||||
{ |
||||
// one could do a loop with individual samples instead... but zero is zero
|
||||
// actually, that is wrong: zero is mostly a series of null bytes,
|
||||
// but we have funny 8bit formats that have a different opinion on zero...
|
||||
// unsigned 16 or 32 bit formats are handled later.
|
||||
memset( fr->buffer.data + fr->buffer.fill, 0, needed_bytes - fr->buffer.fill ); |
||||
|
||||
fr->buffer.fill = needed_bytes; |
||||
} |
||||
|
||||
postprocess_buffer( fr ); |
||||
} |
||||
|
||||
int mpg123_read( mpg123_handle_t *mh, byte *out, size_t size, size_t *done ) |
||||
{ |
||||
return mpg123_decode( mh, NULL, 0, out, size, done ); |
||||
} |
||||
|
||||
int mpg123_feed( mpg123_handle_t *mh, const byte *in, size_t size ) |
||||
{ |
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
if( size > 0 ) |
||||
{ |
||||
if( in != NULL ) |
||||
{ |
||||
if( feed_more( mh, in, size ) != 0 ) |
||||
{ |
||||
return MPG123_ERR; |
||||
} |
||||
else |
||||
{ |
||||
// the need for more data might have triggered an error.
|
||||
// this one is outdated now with the new data.
|
||||
if( mh->err == MPG123_ERR_READER ) |
||||
mh->err = MPG123_OK; |
||||
return MPG123_OK; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
mh->err = MPG123_NULL_BUFFER; |
||||
return MPG123_ERR; |
||||
} |
||||
} |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
int mpg123_decode( mpg123_handle_t *mh, const byte *inmemory, size_t inmemsize, byte *outmemory, size_t outmemsize, size_t *done ) |
||||
{ |
||||
int ret = MPG123_OK; |
||||
size_t mdone = 0; |
||||
|
||||
if( done != NULL ) *done = 0; |
||||
if( mh == NULL ) return MPG123_BAD_HANDLE; |
||||
|
||||
if( inmemsize > 0 && mpg123_feed( mh, inmemory, inmemsize ) != MPG123_OK ) |
||||
{ |
||||
ret = MPG123_ERR; |
||||
goto decodeend; |
||||
} |
||||
|
||||
if( outmemory == NULL ) |
||||
outmemsize = 0; // not just give error, give chance to get a status message.
|
||||
|
||||
while( ret == MPG123_OK ) |
||||
{ |
||||
// decode a frame that has been read before.
|
||||
// this only happens when buffer is empty!
|
||||
if( mh->to_decode ) |
||||
{ |
||||
if( mh->new_format ) |
||||
{ |
||||
mh->new_format = 0; |
||||
ret = MPG123_NEW_FORMAT; |
||||
goto decodeend; |
||||
} |
||||
|
||||
if( mh->buffer.size - mh->buffer.fill < mh->outblock ) |
||||
{ |
||||
ret = MPG123_NO_SPACE; |
||||
goto decodeend; |
||||
} |
||||
|
||||
decode_the_frame( mh ); |
||||
mh->to_decode = mh->to_ignore = FALSE; |
||||
mh->buffer.p = mh->buffer.data; |
||||
frame_buffercheck( mh ); |
||||
} |
||||
|
||||
if( mh->buffer.fill ) |
||||
{ |
||||
int a = mh->buffer.fill > (outmemsize - mdone) ? outmemsize - mdone : mh->buffer.fill; |
||||
|
||||
// copy (part of) the decoded data to the caller's buffer.
|
||||
// get what is needed - or just what is there
|
||||
memcpy( outmemory, mh->buffer.p, a ); |
||||
|
||||
// less data in frame buffer, less needed, output pointer increase, more data given...
|
||||
mh->buffer.fill -= a; |
||||
outmemory += a; |
||||
mdone += a; |
||||
mh->buffer.p += a; |
||||
|
||||
if(!( outmemsize > mdone )) |
||||
goto decodeend; |
||||
} |
||||
else |
||||
{ |
||||
// if we didn't have data, get a new frame.
|
||||
int b = get_next_frame( mh ); |
||||
if (b < 0 ) |
||||
{ |
||||
ret = b; |
||||
goto decodeend; |
||||
} |
||||
} |
||||
} |
||||
decodeend: |
||||
if( done != NULL ) |
||||
*done = mdone; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int mpg123_getformat( mpg123_handle_t *mh, int *rate, int *channels, int *encoding ) |
||||
{ |
||||
int b; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
b = init_track( mh ); |
||||
if( b < 0 ) return b; |
||||
|
||||
if( rate != NULL ) *rate = mh->af.rate; |
||||
if( channels != NULL ) *channels = mh->af.channels; |
||||
if( encoding != NULL ) *encoding = mh->af.encoding; |
||||
mh->new_format = 0; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
int mpg123_scan( mpg123_handle_t *mh ) |
||||
{ |
||||
mpg_off_t track_frames = 0; |
||||
mpg_off_t track_samples = 0; |
||||
mpg_off_t oldpos; |
||||
int b; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_BAD_HANDLE; |
||||
|
||||
if(!( mh->rdat.flags & READER_SEEKABLE )) |
||||
{ |
||||
mh->err = MPG123_NO_SEEK; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
// scan through the _whole_ file, since the current position is no count but computed assuming constant samples per frame.
|
||||
// also, we can just keep the current buffer and seek settings. Just operate on input frames here.
|
||||
|
||||
b = init_track( mh ); // mh->num >= 0 !!
|
||||
|
||||
if( b < 0 ) |
||||
{ |
||||
if( b == MPG123_DONE ) |
||||
return MPG123_OK; |
||||
return MPG123_ERR; // must be error here, NEED_MORE is not for seekable streams.
|
||||
} |
||||
|
||||
oldpos = mpg123_tell( mh ); |
||||
b = mh->rd->seek_frame( mh, 0 ); |
||||
|
||||
if( b < 0 || mh->num != 0 ) |
||||
return MPG123_ERR; |
||||
|
||||
// one frame must be there now.
|
||||
track_frames = 1; |
||||
track_samples = mh->spf; // internal samples.
|
||||
|
||||
// do not increment mh->track_frames in the loop as tha would confuse Frankenstein detection.
|
||||
while( read_frame( mh ) == 1 ) |
||||
{ |
||||
track_samples += mh->spf; |
||||
track_frames++; |
||||
} |
||||
|
||||
mh->track_frames = track_frames; |
||||
mh->track_samples = track_samples; |
||||
|
||||
// also, think about usefulness of that extra value track_samples ...
|
||||
// it could be used for consistency checking.
|
||||
if( mh->p.flags & MPG123_GAPLESS ) |
||||
frame_gapless_update( mh, mh->track_samples ); |
||||
|
||||
return mpg123_seek( mh, oldpos, SEEK_SET ) >= 0 ? MPG123_OK : MPG123_ERR; |
||||
} |
||||
|
||||
// now, where are we? We need to know the last decoded frame... and what's left of it in buffer.
|
||||
// the current frame number can mean the last decoded frame or the to-be-decoded frame.
|
||||
// if mh->to_decode, then mh->num frames have been decoded, the frame mh->num now coming next.
|
||||
// if not, we have the possibility of mh->num+1 frames being decoded or nothing at all.
|
||||
// then, there is firstframe...when we didn't reach it yet, then the next data will come from there.
|
||||
// mh->num starts with -1
|
||||
mpg_off_t mpg123_tell( mpg123_handle_t *mh ) |
||||
{ |
||||
mpg_off_t pos = 0; |
||||
|
||||
if( mh == NULL ) |
||||
return MPG123_ERR; |
||||
|
||||
if( track_need_init( mh )) |
||||
return 0; |
||||
|
||||
// now we have all the info at hand.
|
||||
if(( mh->num < mh->firstframe ) || ( mh->num == mh->firstframe && mh->to_decode )) |
||||
{ |
||||
// we are at the beginning, expect output from firstframe on.
|
||||
pos = frame_outs( mh, mh->firstframe ); |
||||
pos += mh->firstoff; |
||||
} |
||||
else if( mh->to_decode ) |
||||
{ |
||||
// we start fresh with this frame. Buffer should be empty, but we make sure to count it in.
|
||||
pos = frame_outs(mh, mh->num) - bytes_to_samples( mh, mh->buffer.fill ); |
||||
} |
||||
else |
||||
{ |
||||
// we serve what we have in buffer and then the beginning of next frame...
|
||||
pos = frame_outs(mh, mh->num+1) - bytes_to_samples( mh, mh->buffer.fill ); |
||||
} |
||||
|
||||
// substract padding and delay from the beginning. */
|
||||
pos = sample_adjust( mh, pos ); |
||||
|
||||
// negative sample offsets are not right, less than nothing is still nothing.
|
||||
return pos > 0 ? pos : 0; |
||||
} |
||||
|
||||
static int do_the_seek( mpg123_handle_t *mh ) |
||||
{ |
||||
mpg_off_t fnum = SEEKFRAME( mh ); |
||||
int b; |
||||
|
||||
mh->buffer.fill = 0; |
||||
|
||||
// If we are inside the ignoreframe - firstframe window,
|
||||
// we may get away without actual seeking.
|
||||
if( mh->num < mh->firstframe ) |
||||
{ |
||||
mh->to_decode = FALSE; // In any case, don't decode the current frame, perhaps ignore instead.
|
||||
if( mh->num > fnum ) |
||||
return MPG123_OK; |
||||
} |
||||
|
||||
// if we are already there, we are fine either for decoding or for ignoring.
|
||||
if( mh->num == fnum && ( mh->to_decode || fnum < mh->firstframe )) |
||||
return MPG123_OK; |
||||
|
||||
// we have the frame before... just go ahead as normal.
|
||||
if( mh->num == fnum - 1 ) |
||||
{ |
||||
mh->to_decode = FALSE; |
||||
return MPG123_OK; |
||||
} |
||||
|
||||
// OK, real seeking follows... clear buffers and go for it.
|
||||
frame_buffers_reset( mh ); |
||||
|
||||
b = mh->rd->seek_frame( mh, fnum ); |
||||
if( mh->header_change > 1 ) |
||||
{ |
||||
if( decode_update( mh ) < 0 ) |
||||
return MPG123_ERR; |
||||
mh->header_change = 0; |
||||
} |
||||
|
||||
if( b < 0 ) return b; |
||||
|
||||
// Only mh->to_ignore is TRUE.
|
||||
if( mh->num < mh->firstframe ) |
||||
mh->to_decode = FALSE; |
||||
mh->playnum = mh->num; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
mpg_off_t mpg123_seek( mpg123_handle_t *mh, mpg_off_t sampleoff, int whence ) |
||||
{ |
||||
mpg_off_t pos; |
||||
int b; |
||||
|
||||
pos = mpg123_tell( mh ); // adjusted samples
|
||||
|
||||
// pos < 0 also can mean that simply a former seek failed at the lower levels.
|
||||
// in that case, we only allow absolute seeks.
|
||||
if( pos < 0 && whence != SEEK_SET ) |
||||
{ |
||||
// unless we got the obvious error of NULL handle,
|
||||
// this is a special seek failure.
|
||||
if( mh != NULL ) |
||||
mh->err = MPG123_NO_RELSEEK; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
if(( b = init_track( mh )) < 0 ) |
||||
return b; |
||||
|
||||
switch( whence ) |
||||
{ |
||||
case SEEK_CUR: pos += sampleoff; break; |
||||
case SEEK_SET: pos = sampleoff; break; |
||||
case SEEK_END: |
||||
// when we do not know the end already, we can try to find it.
|
||||
if( mh->track_frames < 1 && ( mh->rdat.flags & READER_SEEKABLE )) |
||||
mpg123_scan( mh ); |
||||
if( mh->track_frames > 0 ) |
||||
pos = sample_adjust( mh, frame_outs( mh, mh->track_frames )) - sampleoff; |
||||
else if( mh->end_os > 0 ) |
||||
pos = sample_adjust( mh, mh->end_os ) - sampleoff; |
||||
else |
||||
{ |
||||
mh->err = MPG123_NO_SEEK_FROM_END; |
||||
return MPG123_ERR; |
||||
} |
||||
break; |
||||
default: |
||||
mh->err = MPG123_BAD_WHENCE; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
if( pos < 0 ) pos = 0; |
||||
// pos now holds the wanted sample offset in adjusted samples
|
||||
frame_set_seek( mh, sample_unadjust( mh, pos )); |
||||
pos = do_the_seek( mh ); |
||||
if( pos < 0 ) return pos; |
||||
|
||||
return mpg123_tell( mh ); |
||||
} |
||||
|
||||
static const char *mpg123_error[] = |
||||
{ |
||||
"No error... (code 0)", |
||||
"Unable to set up output format! (code 1)", |
||||
"Invalid channel number specified. (code 2)", |
||||
"Invalid sample rate specified. (code 3)", |
||||
"Unable to allocate memory for 16 to 8 converter table! (code 4)", |
||||
"Bad parameter id! (code 5)", |
||||
"Bad buffer given -- invalid pointer or too small size. (code 6)", |
||||
"Out of memory -- some malloc() failed. (code 7)", |
||||
"You didn't initialize the library! (code 8)", |
||||
"Invalid decoder choice. (code 9)", |
||||
"Invalid mpg123 handle. (code 10)", |
||||
"Unable to initialize frame buffers (out of memory?)! (code 11)", |
||||
"Invalid RVA mode. (code 12)", |
||||
"This build doesn't support gapless decoding. (code 13)", |
||||
"Not enough buffer space. (code 14)", |
||||
"Incompatible numeric data types. (code 15)", |
||||
"Bad equalizer band. (code 16)", |
||||
"Null pointer given where valid storage address needed. (code 17)", |
||||
"Error reading the stream. (code 18)", |
||||
"Cannot seek from end (end is not known). (code 19)", |
||||
"Invalid 'whence' for seek function. (code 20)", |
||||
"Build does not support stream timeouts. (code 21)", |
||||
"File access error. (code 22)", |
||||
"Seek not supported by stream. (code 23)", |
||||
"No stream opened. (code 24)", |
||||
"Bad parameter handle. (code 25)", |
||||
"Invalid parameter addresses for index retrieval. (code 26)", |
||||
"Lost track in the bytestream and did not attempt resync. (code 27)", |
||||
"Failed to find valid MPEG data within limit on resync. (code 28)", |
||||
"No 8bit encoding possible. (code 29)", |
||||
"Stack alignment is not good. (code 30)", |
||||
"You gave me a NULL buffer? (code 31)", |
||||
"File position is screwed up, please do an absolute seek (code 32)", |
||||
"Inappropriate NULL-pointer provided.", |
||||
"Bad key value given.", |
||||
"There is no frame index (disabled in this build).", |
||||
"Frame index operation failed.", |
||||
"Decoder setup failed (invalid combination of settings?)", |
||||
"Feature not in this build.", |
||||
"Some bad value has been provided.", |
||||
"Low-level seeking has failed (call to lseek(), usually).", |
||||
"Custom I/O obviously not prepared.", |
||||
"Overflow in LFS (large file support) conversion.", |
||||
"Overflow in integer conversion.", |
||||
}; |
||||
|
||||
const char *mpg123_plain_strerror( int errcode ) |
||||
{ |
||||
if( errcode >= 0 && errcode < sizeof( mpg123_error ) / sizeof( char* )) |
||||
return mpg123_error[errcode]; |
||||
|
||||
switch( errcode ) |
||||
{ |
||||
case MPG123_ERR: |
||||
return "A generic mpg123 error."; |
||||
case MPG123_DONE: |
||||
return "Message: I am done with this track."; |
||||
case MPG123_NEED_MORE: |
||||
return "Message: Feed me more input data!"; |
||||
case MPG123_NEW_FORMAT: |
||||
return "Message: Prepare for a changed audio format (query the new one)!"; |
||||
default: |
||||
return "I have no idea - an unknown error code!"; |
||||
} |
||||
} |
||||
|
||||
const char *get_error( mpg123_handle_t *mh ) |
||||
{ |
||||
if( !mh ) return mpg123_plain_strerror( MPG123_BAD_HANDLE ); |
||||
return mpg123_plain_strerror( mh->err ); |
||||
} |
@ -0,0 +1,495 @@
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
mpg123.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef MPG123_H |
||||
#define MPG123_H |
||||
|
||||
typedef struct mpg123_handle_s mpg123_handle_t; |
||||
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(disable : 4115) // named type definition in parentheses
|
||||
#pragma warning(disable : 4057) // differs in indirection to slightly different base types
|
||||
#pragma warning(disable : 4244) // conversion possible loss of data
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#pragma warning(disable : 4706) // assignment within conditional expression
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#endif |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include "fmt123.h" |
||||
|
||||
#ifndef FALSE |
||||
#define FALSE 0 |
||||
#endif |
||||
|
||||
#ifndef TRUE |
||||
#define TRUE (!FALSE) |
||||
#endif |
||||
|
||||
// configure the lib
|
||||
#define ACCURATE_ROUNDING |
||||
//#define IEEE_FLOAT
|
||||
|
||||
// begin used typedefs
|
||||
typedef unsigned char byte; |
||||
typedef unsigned short word; |
||||
typedef unsigned long ulong; |
||||
typedef unsigned int uint; |
||||
typedef long mpg_off_t; |
||||
|
||||
#ifdef _MSC_VER // a1ba: MSVC6 don't have ssize_t
|
||||
typedef long mpg_ssize_t; |
||||
#else |
||||
typedef ssize_t mpg_ssize_t; |
||||
#endif |
||||
|
||||
typedef short int16_t; |
||||
typedef unsigned short uint16_t; |
||||
|
||||
#include "synth.h" |
||||
#include "index.h" |
||||
#include "reader.h" |
||||
#include "frame.h" |
||||
|
||||
#define SEEKFRAME( mh ) ((mh)->ignoreframe < 0 ? 0 : (mh)->ignoreframe) |
||||
#define track_need_init( mh ) ((mh)->num < 0) |
||||
#define INDEX_SIZE 1000 |
||||
#define SBLIMIT 32 |
||||
#define SSLIMIT 18 |
||||
#define GAPLESS_DELAY 529 |
||||
#define SHORT_SCALE 32768 |
||||
|
||||
#define MPG_MD_STEREO 0 |
||||
#define MPG_MD_JOINT_STEREO 1 |
||||
#define MPG_MD_DUAL_CHANNEL 2 |
||||
#define MPG_MD_MONO 3 |
||||
|
||||
#define SINGLE_STEREO -1 |
||||
#define SINGLE_LEFT 0 |
||||
#define SINGLE_RIGHT 1 |
||||
#define SINGLE_MIX 3 |
||||
|
||||
#define DOUBLE_TO_REAL( x ) (float)(x) |
||||
#define DOUBLE_TO_REAL_15( x ) (float)(x) |
||||
#define DOUBLE_TO_REAL_POW43( x ) (float)(x) |
||||
#define DOUBLE_TO_REAL_SCALE_LAYER12( x ) (float)(x) |
||||
#define DOUBLE_TO_REAL_SCALE_LAYER3( x, y ) (float)(x) |
||||
#define REAL_TO_DOUBLE( x ) (x) |
||||
|
||||
#define REAL_MUL( x, y ) ((x) * (y)) |
||||
#define REAL_MUL_SYNTH( x, y ) ((x) * (y)) |
||||
#define REAL_MUL_15( x, y ) ((x) * (y)) |
||||
#define REAL_MUL_SCALE_LAYER12( x, y ) ((x) * (y)) |
||||
#define REAL_MUL_SCALE_LAYER3( x, y ) ((x) * (y)) |
||||
#define REAL_SCALE_LAYER12( x ) (x) |
||||
#define REAL_SCALE_LAYER3( x ) (x) |
||||
#define REAL_SCALE_DCT64( x ) (x) |
||||
|
||||
#ifndef M_PI |
||||
#define M_PI 3.14159265358979323846 |
||||
#endif |
||||
|
||||
#ifndef M_SQRT2 |
||||
#define M_SQRT2 1.41421356237309504880 |
||||
#endif |
||||
|
||||
// enumeration of the message and error codes and returned by libmpg123 functions.
|
||||
enum mpg123_errors |
||||
{ |
||||
MPG123_DONE = -12, /**< Message: Track ended. Stop decoding. */ |
||||
MPG123_NEW_FORMAT = -11, /**< Message: Output format will be different on next call. Note that some libmpg123 versions between 1.4.3 and 1.8.0 insist on you calling mpg123_getformat() after getting this message code. Newer verisons behave like advertised: You have the chance to call mpg123_getformat(), but you can also just continue decoding and get your data. */ |
||||
MPG123_NEED_MORE = -10, /**< Message: For feed reader: "Feed me more!" (call mpg123_feed() or mpg123_decode() with some new input data). */ |
||||
MPG123_ERR = -1, /**< Generic Error */ |
||||
MPG123_OK = 0, /**< Success */ |
||||
MPG123_BAD_OUTFORMAT, /**< Unable to set up output format! */ |
||||
MPG123_BAD_CHANNEL, /**< Invalid channel number specified. */ |
||||
MPG123_BAD_RATE, /**< Invalid sample rate specified. */ |
||||
MPG123_ERR_16TO8TABLE, /**< Unable to allocate memory for 16 to 8 converter table! */ |
||||
MPG123_BAD_PARAM, /**< Bad parameter id! */ |
||||
MPG123_BAD_BUFFER, /**< Bad buffer given -- invalid pointer or too small size. */ |
||||
MPG123_OUT_OF_MEM, /**< Out of memory -- some malloc() failed. */ |
||||
MPG123_NOT_INITIALIZED, /**< You didn't initialize the library! */ |
||||
MPG123_BAD_DECODER, /**< Invalid decoder choice. */ |
||||
MPG123_BAD_HANDLE, /**< Invalid mpg123 handle. */ |
||||
MPG123_NO_BUFFERS, /**< Unable to initialize frame buffers (out of memory?). */ |
||||
MPG123_BAD_RVA, /**< Invalid RVA mode. */ |
||||
MPG123_NO_GAPLESS, /**< This build doesn't support gapless decoding. */ |
||||
MPG123_NO_SPACE, /**< Not enough buffer space. */ |
||||
MPG123_BAD_TYPES, /**< Incompatible numeric data types. */ |
||||
MPG123_BAD_BAND, /**< Bad equalizer band. */ |
||||
MPG123_ERR_NULL, /**< Null pointer given where valid storage address needed. */ |
||||
MPG123_ERR_READER, /**< Error reading the stream. */ |
||||
MPG123_NO_SEEK_FROM_END, /**< Cannot seek from end (end is not known). */ |
||||
MPG123_BAD_WHENCE, /**< Invalid 'whence' for seek function.*/ |
||||
MPG123_NO_TIMEOUT, /**< Build does not support stream timeouts. */ |
||||
MPG123_BAD_FILE, /**< File access error. */ |
||||
MPG123_NO_SEEK, /**< Seek not supported by stream. */ |
||||
MPG123_NO_READER, /**< No stream opened. */ |
||||
MPG123_BAD_PARS, /**< Bad parameter handle. */ |
||||
MPG123_BAD_INDEX_PAR, /**< Bad parameters to mpg123_index() and mpg123_set_index() */ |
||||
MPG123_OUT_OF_SYNC, /**< Lost track in bytestream and did not try to resync. */ |
||||
MPG123_RESYNC_FAIL, /**< Resync failed to find valid MPEG data. */ |
||||
MPG123_NO_8BIT, /**< No 8bit encoding possible. */ |
||||
MPG123_BAD_ALIGN, /**< Stack aligmnent error */ |
||||
MPG123_NULL_BUFFER, /**< NULL input buffer with non-zero size... */ |
||||
MPG123_NO_RELSEEK, /**< Relative seek not possible (screwed up file offset) */ |
||||
MPG123_NULL_POINTER, /**< You gave a null pointer somewhere where you shouldn't have. */ |
||||
MPG123_BAD_KEY, /**< Bad key value given. */ |
||||
MPG123_NO_INDEX, /**< No frame index in this build. */ |
||||
MPG123_INDEX_FAIL, /**< Something with frame index went wrong. */ |
||||
MPG123_BAD_DECODER_SETUP, /**< Something prevents a proper decoder setup */ |
||||
MPG123_MISSING_FEATURE, /**< This feature has not been built into libmpg123. */ |
||||
MPG123_BAD_VALUE, /**< A bad value has been given, somewhere. */ |
||||
MPG123_LSEEK_FAILED, /**< Low-level seek failed. */ |
||||
MPG123_BAD_CUSTOM_IO, /**< Custom I/O not prepared. */ |
||||
MPG123_LFS_OVERFLOW, /**< Offset value overflow during translation of large file API calls -- your client program cannot handle that large file. */ |
||||
MPG123_INT_OVERFLOW /**< Some integer overflow. */ |
||||
}; |
||||
|
||||
// enumeration of the parameters types that it is possible to set/get.
|
||||
enum mpg123_parms |
||||
{ |
||||
MPG123_VERBOSE = 0, /**< set verbosity value for enabling messages to stderr, >= 0 makes sense (integer) */ |
||||
MPG123_FLAGS, /**< set all flags, p.ex val = MPG123_GAPLESS|MPG123_MONO_MIX (integer) */ |
||||
MPG123_ADD_FLAGS, /**< add some flags (integer) */ |
||||
MPG123_FORCE_RATE, /**< when value > 0, force output rate to that value (integer) */ |
||||
MPG123_DOWN_SAMPLE, /**< 0=native rate, 1=half rate, 2=quarter rate (integer) */ |
||||
MPG123_RVA, /**< one of the RVA choices above (integer) */ |
||||
MPG123_DOWNSPEED, /**< play a frame N times (integer) */ |
||||
MPG123_UPSPEED, /**< play every Nth frame (integer) */ |
||||
MPG123_START_FRAME, /**< start with this frame (skip frames before that, integer) */ |
||||
MPG123_DECODE_FRAMES, /**< decode only this number of frames (integer) */ |
||||
MPG123_OUTSCALE, /**< the scale for output samples (amplitude - integer according to mpg123 output format) */ |
||||
MPG123_TIMEOUT, /**< timeout for reading from a stream (not supported on win32, integer) */ |
||||
MPG123_REMOVE_FLAGS, /**< remove some flags (inverse of MPG123_ADD_FLAGS, integer) */ |
||||
MPG123_RESYNC_LIMIT, /**< Try resync on frame parsing for that many bytes or until end of stream (<0 ... integer). This can enlarge the limit for skipping junk on beginning, too (but not reduce it). */ |
||||
MPG123_INDEX_SIZE, /**< Set the frame index size (if supported). Values <0 mean that the index is allowed to grow dynamically in these steps (in positive direction, of course) -- Use this when you really want a full index with every individual frame. */ |
||||
MPG123_PREFRAMES, /**< Decode/ignore that many frames in advance for layer 3. This is needed to fill bit reservoir after seeking, for example (but also at least one frame in advance is needed to have all "normal" data for layer 3). Give a positive integer value, please.*/ |
||||
MPG123_FEEDPOOL, /**< For feeder mode, keep that many buffers in a pool to avoid frequent malloc/free. The pool is allocated on mpg123_open_feed(). If you change this parameter afterwards, you can trigger growth and shrinkage during decoding. The default value could change any time. If you care about this, then set it. (integer) */ |
||||
MPG123_FEEDBUFFER, /**< Minimal size of one internal feeder buffer, again, the default value is subject to change. (integer) */ |
||||
}; |
||||
|
||||
// flag bits for MPG123_FLAGS, use the usual binary or to combine.
|
||||
enum mpg123_param_flags |
||||
{ |
||||
MPG123_FORCE_MONO = 0x7, /**< 0111 Force some mono mode: This is a test bitmask for seeing if any mono forcing is active. */ |
||||
MPG123_MONO_LEFT = 0x1, /**< 0001 Force playback of left channel only. */ |
||||
MPG123_MONO_RIGHT = 0x2, /**< 0010 Force playback of right channel only. */ |
||||
MPG123_MONO_MIX = 0x4, /**< 0100 Force playback of mixed mono. */ |
||||
MPG123_FORCE_STEREO = 0x8, /**< 1000 Force stereo output. */ |
||||
MPG123_QUIET = 0x20, /**< 00100000 Suppress any printouts (overrules verbose). */ |
||||
MPG123_GAPLESS = 0x40, /**< 01000000 Enable gapless decoding (default on if libmpg123 has support). */ |
||||
MPG123_NO_RESYNC = 0x80, /**< 10000000 Disable resync stream after error. */ |
||||
MPG123_SEEKBUFFER = 0x100, /**< 000100000000 Enable small buffer on non-seekable streams to allow some peek-ahead (for better MPEG sync). */ |
||||
MPG123_FUZZY = 0x200, /**< 001000000000 Enable fuzzy seeks (guessing byte offsets or using approximate seek points from Xing TOC) */ |
||||
MPG123_IGNORE_STREAMLENGTH = 0x1000, /**< 1000000000000 Ignore any stream length information contained in the stream, which can be contained in a 'TLEN' frame of an ID3v2 tag or a Xing tag */ |
||||
MPG123_IGNORE_INFOFRAME = 0x4000, /**< 100 0000 0000 0000 Do not parse the LAME/Xing info frame, treat it as normal MPEG data. */ |
||||
MPG123_AUTO_RESAMPLE = 0x8000, /**< 1000 0000 0000 0000 Allow automatic internal resampling of any kind (default on if supported). Especially when going lowlevel with replacing output buffer, you might want to unset this flag. Setting MPG123_DOWNSAMPLE or MPG123_FORCE_RATE will override this. */ |
||||
}; |
||||
|
||||
// choices for MPG123_RVA
|
||||
enum mpg123_param_rva |
||||
{ |
||||
MPG123_RVA_OFF = 0, /**< RVA disabled (default). */ |
||||
MPG123_RVA_MIX = 1, /**< Use mix/track/radio gain. */ |
||||
MPG123_RVA_ALBUM = 2, /**< Use album/audiophile gain */ |
||||
MPG123_RVA_MAX = MPG123_RVA_ALBUM, /**< The maximum RVA code, may increase in future. */ |
||||
}; |
||||
|
||||
enum frame_state_flags |
||||
{ |
||||
FRAME_ACCURATE = 0x1, /**< 0001 Positions are considered accurate. */ |
||||
FRAME_FRANKENSTEIN = 0x2, /**< 0010 This stream is concatenated. */ |
||||
FRAME_FRESH_DECODER = 0x4, /**< 0100 Decoder is fleshly initialized. */ |
||||
}; |
||||
|
||||
// enumeration of the mode types of Variable Bitrate
|
||||
enum mpg123_vbr |
||||
{ |
||||
MPG123_CBR = 0, /**< Constant Bitrate Mode (default) */ |
||||
MPG123_VBR, /**< Variable Bitrate Mode */ |
||||
MPG123_ABR /**< Average Bitrate Mode */ |
||||
}; |
||||
|
||||
// Data structure for ID3v1 tags (the last 128 bytes of a file).
|
||||
// Don't take anything for granted (like string termination)!
|
||||
// Also note the change ID3v1.1 did: comment[28] = 0; comment[29] = track_number
|
||||
// It is your task to support ID3v1 only or ID3v1.1 ...
|
||||
typedef struct |
||||
{ |
||||
char tag[3]; /**< Always the string "TAG", the classic intro. */ |
||||
char title[30]; /**< Title string. */ |
||||
char artist[30]; /**< Artist string. */ |
||||
char album[30]; /**< Album string. */ |
||||
char year[4]; /**< Year string. */ |
||||
char comment[30]; /**< Comment string. */ |
||||
byte genre; /**< Genre index. */ |
||||
} mpg123_id3v1; |
||||
|
||||
#define MPG123_ID3 0x3 /**< 0011 There is some ID3 info. Also matches 0010 or NEW_ID3. */ |
||||
#define MPG123_NEW_ID3 0x1 /**< 0001 There is ID3 info that changed since last call to mpg123_id3. */ |
||||
|
||||
struct mpg123_handle_s |
||||
{ |
||||
int fresh; // to be moved into flags
|
||||
int new_format; |
||||
float hybrid_block[2][2][SBLIMIT*SSLIMIT]; |
||||
int hybrid_blc[2]; |
||||
|
||||
// the scratch vars for the decoders, sometimes float, sometimes short... sometimes int/long
|
||||
short *short_buffs[2][2]; |
||||
float *float_buffs[2][2]; |
||||
byte *rawbuffs; |
||||
int rawbuffss; |
||||
int bo; // just have it always here.
|
||||
byte *rawdecwin; // the block with all decwins
|
||||
|
||||
int rawdecwins; // size of rawdecwin memory
|
||||
float *decwin; // _the_ decode table
|
||||
|
||||
// for halfspeed mode
|
||||
byte ssave[34]; |
||||
int halfphase; |
||||
|
||||
// layer3
|
||||
int longLimit[9][23]; |
||||
int shortLimit[9][14]; |
||||
float gainpow2[256+118+4];// not floatly dynamic, just different for mmx
|
||||
|
||||
synth_t synths; |
||||
int verbose; // 0: nothing, 1: just print chosen decoder, 2: be verbose
|
||||
|
||||
const al_table_t *alloc; |
||||
|
||||
// the runtime-chosen decoding, based on input and output format
|
||||
func_synth synth; |
||||
func_synth_stereo synth_stereo; |
||||
func_synth_mono synth_mono; |
||||
|
||||
// yes, this function is runtime-switched, too.
|
||||
void (*make_decode_tables)( mpg123_handle_t *fr ); // that is the volume control.
|
||||
|
||||
int stereo; // I _think_ 1 for mono and 2 for stereo
|
||||
int jsbound; |
||||
|
||||
int single; |
||||
int II_sblimit; |
||||
int down_sample_sblimit; |
||||
int lsf; // 0: MPEG 1.0; 1: MPEG 2.0/2.5 -- both used as bool and array index!
|
||||
|
||||
// many flags in disguise as integers... wasting bytes.
|
||||
int mpeg25; |
||||
int down_sample; |
||||
int header_change; |
||||
int lay; |
||||
long spf; // cached count of samples per frame
|
||||
|
||||
int (*do_layer)( mpg123_handle_t* ); |
||||
|
||||
int error_protection; |
||||
int bitrate_index; |
||||
int sampling_frequency; |
||||
int padding; |
||||
int extension; |
||||
int mode; |
||||
int mode_ext; |
||||
int copyright; |
||||
int original; |
||||
int emphasis; |
||||
int framesize; // computed framesize
|
||||
int freesize; // free format frame size
|
||||
int vbr; // 1 if variable bitrate was detected
|
||||
mpg_off_t num; // frame offset ...
|
||||
mpg_off_t input_offset; // byte offset of this frame in input stream
|
||||
mpg_off_t playnum; // playback offset... includes repetitions, reset at seeks
|
||||
mpg_off_t audio_start; // The byte offset in the file where audio data begins.
|
||||
int state_flags; |
||||
char silent_resync; // Do not complain for the next n resyncs.
|
||||
byte *xing_toc; // The seek TOC from Xing header.
|
||||
int freeformat; |
||||
long freeformat_framesize; |
||||
|
||||
// bitstream info; bsi
|
||||
int bitindex; |
||||
byte *wordpointer; |
||||
|
||||
// temporary storage for getbits stuff
|
||||
ulong ultmp; |
||||
byte uctmp; |
||||
|
||||
// rva data
|
||||
double maxoutburst; // the maximum amplitude in current sample represenation.
|
||||
double lastscale; |
||||
|
||||
struct |
||||
{ |
||||
int level[2]; |
||||
float gain[2]; |
||||
float peak[2]; |
||||
} rva; |
||||
|
||||
// input data
|
||||
mpg_off_t track_frames; |
||||
mpg_off_t track_samples; |
||||
double mean_framesize; |
||||
mpg_off_t mean_frames; |
||||
int fsizeold; |
||||
int ssize; |
||||
|
||||
uint bitreservoir; |
||||
byte bsspace[2][MAXFRAMESIZE+512]; |
||||
byte *bsbuf; |
||||
byte *bsbufold; |
||||
int bsnum; |
||||
|
||||
// that is the header matching the last read frame body.
|
||||
ulong oldhead; |
||||
|
||||
// that is the header that is supposedly the first of the stream.
|
||||
ulong firsthead; |
||||
int abr_rate; |
||||
|
||||
frame_index_t index; |
||||
|
||||
// output data
|
||||
outbuffer_t buffer; |
||||
audioformat_t af; |
||||
|
||||
int own_buffer; |
||||
size_t outblock; // number of bytes that this frame produces (upper bound)
|
||||
int to_decode; // this frame holds data to be decoded
|
||||
int to_ignore; // the same, somehow
|
||||
mpg_off_t firstframe; // start decoding from here
|
||||
mpg_off_t lastframe; // last frame to decode (for gapless or num_frames limit)
|
||||
mpg_off_t ignoreframe; // frames to decode but discard before firstframe
|
||||
|
||||
mpg_off_t gapless_frames; // frame count for the gapless part
|
||||
mpg_off_t firstoff; // number of samples to ignore from firstframe
|
||||
mpg_off_t lastoff; // number of samples to use from lastframe
|
||||
mpg_off_t begin_s; // overall begin offset in samples
|
||||
mpg_off_t begin_os; |
||||
mpg_off_t end_s; // overall end offset in samples
|
||||
mpg_off_t end_os; |
||||
mpg_off_t fullend_os; // gapless_frames translated to output samples
|
||||
|
||||
uint crc; // well, I need a safe 16bit type, actually. But wider doesn't hurt.
|
||||
|
||||
reader_t *rd; // pointer to the reading functions
|
||||
reader_data_t rdat; // reader data and state info
|
||||
mpg123_parm_t p; |
||||
|
||||
int err; |
||||
int decoder_change; |
||||
int delayed_change; |
||||
long clip; |
||||
|
||||
// the meta crap
|
||||
int metaflags; |
||||
byte id3buf[128]; |
||||
|
||||
float *layerscratch; |
||||
|
||||
// these are significant chunks of memory already...
|
||||
struct |
||||
{ |
||||
float (*hybrid_in)[SBLIMIT][SSLIMIT]; // ALIGNED(16) float hybridIn[2][SBLIMIT][SSLIMIT];
|
||||
float (*hybrid_out)[SSLIMIT][SBLIMIT]; // ALIGNED(16) float hybridOut[2][SSLIMIT][SBLIMIT];
|
||||
} layer3; |
||||
|
||||
// a place for storing additional data for the large file wrapper. this is cruft!
|
||||
void *wrapperdata; |
||||
|
||||
// a callback used to properly destruct the wrapper data.
|
||||
void (*wrapperclean)( void* ); |
||||
}; |
||||
|
||||
//
|
||||
// parse.c
|
||||
//
|
||||
void set_pointer( mpg123_handle_t *fr, long backstep ); |
||||
int get_songlen( mpg123_handle_t *fr, int no ); |
||||
double compute_bpf( mpg123_handle_t *fr ); |
||||
long frame_freq( mpg123_handle_t *fr ); |
||||
double mpg123_tpf( mpg123_handle_t *fr ); |
||||
int mpg123_spf( mpg123_handle_t *mh ); |
||||
int read_frame( mpg123_handle_t *fr ); |
||||
|
||||
//
|
||||
// format.c
|
||||
//
|
||||
void invalidate_format( audioformat_t *af ); |
||||
void postprocess_buffer( mpg123_handle_t *fr ); |
||||
int frame_output_format( mpg123_handle_t *fr ); |
||||
int mpg123_fmt_all( mpg123_parm_t *mp ); |
||||
int mpg123_format_none( mpg123_handle_t *mh ); |
||||
int mpg123_format_all( mpg123_handle_t *mh ); |
||||
int mpg123_format( mpg123_handle_t *mh, long rate, int channels, int encodings ); |
||||
mpg_off_t decoder_synth_bytes( mpg123_handle_t *fr, mpg_off_t s ); |
||||
mpg_off_t bytes_to_samples( mpg123_handle_t *fr, mpg_off_t b ); |
||||
mpg_off_t samples_to_bytes( mpg123_handle_t *fr, mpg_off_t s ); |
||||
mpg_off_t outblock_bytes( mpg123_handle_t *fr, mpg_off_t s ); |
||||
|
||||
//
|
||||
// layer3.c
|
||||
//
|
||||
extern float COS6_1; |
||||
extern float COS6_2; |
||||
extern float cos9[3]; |
||||
extern float cos18[3]; |
||||
extern float tfcos12[3]; |
||||
extern float tfcos36[9]; |
||||
void init_layer3( void ); |
||||
void init_layer3_stuff( mpg123_handle_t *fr ); |
||||
int do_layer3( mpg123_handle_t *fr ); |
||||
|
||||
//
|
||||
// dct36.c
|
||||
//
|
||||
void dct36( float *inbuf, float *o1, float *o2, float *wintab, float *tsbuf ); |
||||
void dct12( float *in, float *rawout1, float *rawout2, register float *wi, register float *ts ); |
||||
|
||||
//
|
||||
// dct64.c
|
||||
//
|
||||
void dct64( float *out0, float *out1, float *samples ); |
||||
|
||||
//
|
||||
// tabinit.c
|
||||
//
|
||||
extern float *pnts[]; |
||||
void prepare_decode_tables( void ); |
||||
void make_decode_tables( mpg123_handle_t *fr ); |
||||
|
||||
// begin prototypes
|
||||
mpg123_handle_t *mpg123_new( int *error ); |
||||
mpg123_handle_t *mpg123_parnew( mpg123_parm_t *mp, int *error ); |
||||
int mpg123_param( mpg123_handle_t *mh, enum mpg123_parms key, long val ); |
||||
int mpg123_open_handle( mpg123_handle_t *mh, void *iohandle ); |
||||
int mpg123_replace_reader_handle( mpg123_handle_t *mh, mpg_ssize_t (*fread)(void*, void*, size_t), mpg_off_t (*lseek)(void*, mpg_off_t, int), void(*fclose)(void*)); |
||||
int mpg123_decode( mpg123_handle_t *mh, const byte *inmemory, size_t inmemsize, byte *outmemory, size_t outmemsize, size_t *done ); |
||||
int mpg123_getformat( mpg123_handle_t *mh, int *rate, int *channels, int *encoding ); |
||||
int mpg123_read( mpg123_handle_t *mh, byte *out, size_t size, size_t *done ); |
||||
mpg_off_t mpg123_seek( mpg123_handle_t *mh, mpg_off_t sampleoff, int whence ); |
||||
int mpg123_feed( mpg123_handle_t *mh, const byte *in, size_t size ); |
||||
const char *mpg123_plain_strerror( int errcode ); |
||||
int mpg123_open_feed( mpg123_handle_t *mh ); |
||||
void mpg123_delete( mpg123_handle_t *mh ); |
||||
mpg_off_t mpg123_tell( mpg123_handle_t *mh ); |
||||
int mpg123_init( void ); |
||||
void mpg123_exit( void ); |
||||
|
||||
#endif//MPG123_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,895 @@
@@ -0,0 +1,895 @@
|
||||
/*
|
||||
reader.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#ifdef _WIN32 |
||||
#include <io.h> |
||||
#else |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
#define READER_STREAM 0 |
||||
#define READER_FEED 1 |
||||
#define READER_BUF_STREAM 2 |
||||
|
||||
static int default_init( mpg123_handle_t *fr ); |
||||
static mpg_off_t get_fileinfo( mpg123_handle_t *fr ); |
||||
|
||||
// methods for the buffer chain, mainly used for feed reader, but not just that.
|
||||
static buffy_t* buffy_new( size_t size, size_t minsize ) |
||||
{ |
||||
buffy_t *newbuf = malloc( sizeof( buffy_t )); |
||||
|
||||
if( newbuf == NULL ) |
||||
return NULL; |
||||
|
||||
newbuf->realsize = size > minsize ? size : minsize; |
||||
newbuf->data = malloc( newbuf->realsize ); |
||||
|
||||
if( newbuf->data == NULL ) |
||||
{ |
||||
free( newbuf ); |
||||
return NULL; |
||||
} |
||||
|
||||
newbuf->size = 0; |
||||
newbuf->next = NULL; |
||||
|
||||
return newbuf; |
||||
} |
||||
|
||||
static void buffy_del( buffy_t *buf ) |
||||
{ |
||||
if( buf ) |
||||
{ |
||||
free( buf->data ); |
||||
free( buf ); |
||||
} |
||||
} |
||||
|
||||
// delete this buffy and all following buffies.
|
||||
static void buffy_del_chain( buffy_t *buf ) |
||||
{ |
||||
while( buf ) |
||||
{ |
||||
buffy_t *next = buf->next; |
||||
buffy_del( buf ); |
||||
buf = next; |
||||
} |
||||
} |
||||
|
||||
// fetch a buffer from the pool (if possible) or create one.
|
||||
static buffy_t* bc_alloc( bufferchain_t *bc, size_t size ) |
||||
{ |
||||
// Easy route: Just try the first available buffer.
|
||||
// size does not matter, it's only a hint for creation of new buffers.
|
||||
if( bc->pool ) |
||||
{ |
||||
buffy_t *buf = bc->pool; |
||||
|
||||
bc->pool = buf->next; |
||||
buf->next = NULL; // that shall be set to a sensible value later.
|
||||
buf->size = 0; |
||||
bc->pool_fill--; |
||||
|
||||
return buf; |
||||
} |
||||
|
||||
return buffy_new( size, bc->bufblock ); |
||||
} |
||||
|
||||
// either stuff the buffer back into the pool or free it for good.
|
||||
static void bc_free( bufferchain_t *bc, buffy_t* buf ) |
||||
{ |
||||
if( !buf ) return; |
||||
|
||||
if( bc->pool_fill < bc->pool_size ) |
||||
{ |
||||
buf->next = bc->pool; |
||||
bc->pool = buf; |
||||
bc->pool_fill++; |
||||
} |
||||
else buffy_del( buf ); |
||||
} |
||||
|
||||
// make the buffer count in the pool match the pool size.
|
||||
static int bc_fill_pool( bufferchain_t *bc ) |
||||
{ |
||||
// remove superfluous ones.
|
||||
while( bc->pool_fill > bc->pool_size ) |
||||
{ |
||||
// lazyness: Just work on the front.
|
||||
buffy_t *buf = bc->pool; |
||||
bc->pool = buf->next; |
||||
buffy_del( buf ); |
||||
bc->pool_fill--; |
||||
} |
||||
|
||||
// add missing ones.
|
||||
while( bc->pool_fill < bc->pool_size ) |
||||
{ |
||||
// again, just work on the front.
|
||||
buffy_t *buf = buffy_new( 0, bc->bufblock ); // use default block size.
|
||||
if( !buf ) return -1; |
||||
|
||||
buf->next = bc->pool; |
||||
bc->pool = buf; |
||||
bc->pool_fill++; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void bc_init( bufferchain_t *bc ) |
||||
{ |
||||
bc->first = NULL; |
||||
bc->last = bc->first; |
||||
bc->size = 0; |
||||
bc->pos = 0; |
||||
bc->firstpos = 0; |
||||
bc->fileoff = 0; |
||||
} |
||||
|
||||
static void bc_reset( bufferchain_t *bc ) |
||||
{ |
||||
// free current chain, possibly stuffing back into the pool.
|
||||
while( bc->first ) |
||||
{ |
||||
buffy_t *buf = bc->first; |
||||
bc->first = buf->next; |
||||
bc_free( bc, buf ); |
||||
} |
||||
|
||||
bc_fill_pool( bc ); // ignoring an error here...
|
||||
bc_init( bc ); |
||||
} |
||||
|
||||
// create a new buffy at the end to be filled.
|
||||
static int bc_append( bufferchain_t *bc, mpg_ssize_t size ) |
||||
{ |
||||
buffy_t *newbuf; |
||||
|
||||
if( size < 1 ) |
||||
return -1; |
||||
|
||||
newbuf = bc_alloc( bc, size ); |
||||
if( newbuf == NULL ) return -2; |
||||
|
||||
if( bc->last != NULL ) |
||||
bc->last->next = newbuf; |
||||
else if( bc->first == NULL ) |
||||
bc->first = newbuf; |
||||
|
||||
bc->last = newbuf; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void bc_prepare( bufferchain_t *bc, size_t pool_size, size_t bufblock ) |
||||
{ |
||||
bc_poolsize( bc, pool_size, bufblock ); |
||||
bc->pool = NULL; |
||||
bc->pool_fill = 0; |
||||
bc_init( bc ); // ensure that members are zeroed for read-only use.
|
||||
} |
||||
|
||||
size_t bc_fill( bufferchain_t *bc ) |
||||
{ |
||||
return (size_t)(bc->size - bc->pos); |
||||
} |
||||
|
||||
void bc_poolsize( bufferchain_t *bc, size_t pool_size, size_t bufblock ) |
||||
{ |
||||
bc->pool_size = pool_size; |
||||
bc->bufblock = bufblock; |
||||
} |
||||
|
||||
void bc_cleanup( bufferchain_t *bc ) |
||||
{ |
||||
buffy_del_chain( bc->pool ); |
||||
bc->pool_fill = 0; |
||||
bc->pool = NULL; |
||||
} |
||||
|
||||
// append a new buffer and copy content to it.
|
||||
static int bc_add( bufferchain_t *bc, const byte *data, mpg_ssize_t size ) |
||||
{ |
||||
int ret = 0; |
||||
mpg_ssize_t part = 0; |
||||
|
||||
while( size > 0 ) |
||||
{ |
||||
// try to fill up the last buffer block.
|
||||
if( bc->last != NULL && bc->last->size < bc->last->realsize ) |
||||
{ |
||||
part = bc->last->realsize - bc->last->size; |
||||
if( part > size ) part = size; |
||||
|
||||
memcpy( bc->last->data + bc->last->size, data, part ); |
||||
bc->last->size += part; |
||||
size -= part; |
||||
bc->size += part; |
||||
data += part; |
||||
} |
||||
|
||||
// if there is still data left, put it into a new buffer block.
|
||||
if( size > 0 && ( ret = bc_append( bc, size )) != 0 ) |
||||
break; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
// common handler for "You want more than I can give." situation.
|
||||
static mpg_ssize_t bc_need_more( bufferchain_t *bc ) |
||||
{ |
||||
// go back to firstpos, undo the previous reads
|
||||
bc->pos = bc->firstpos; |
||||
|
||||
return MPG123_NEED_MORE; |
||||
} |
||||
|
||||
// give some data, advancing position but not forgetting yet.
|
||||
static mpg_ssize_t bc_give( bufferchain_t *bc, byte *out, mpg_ssize_t size ) |
||||
{ |
||||
buffy_t *b = bc->first; |
||||
mpg_ssize_t gotcount = 0; |
||||
mpg_ssize_t offset = 0; |
||||
|
||||
if( bc->size - bc->pos < size ) |
||||
return bc_need_more( bc ); |
||||
|
||||
// find the current buffer
|
||||
while( b != NULL && ( offset + b->size ) <= bc->pos ) |
||||
{ |
||||
offset += b->size; |
||||
b = b->next; |
||||
} |
||||
|
||||
// now start copying from there
|
||||
while( gotcount < size && ( b != NULL )) |
||||
{ |
||||
mpg_ssize_t loff = bc->pos - offset; |
||||
mpg_ssize_t chunk = size - gotcount; // amount of bytes to get from here...
|
||||
|
||||
if( chunk > b->size - loff ) |
||||
chunk = b->size - loff; |
||||
|
||||
memcpy( out + gotcount, b->data + loff, chunk ); |
||||
gotcount += chunk; |
||||
bc->pos += chunk; |
||||
offset += b->size; |
||||
b = b->next; |
||||
} |
||||
|
||||
return gotcount; |
||||
} |
||||
|
||||
// skip some bytes and return the new position.
|
||||
// the buffers are still there, just the read pointer is moved!
|
||||
static mpg_ssize_t bc_skip( bufferchain_t *bc, mpg_ssize_t count ) |
||||
{ |
||||
if( count >= 0 ) |
||||
{ |
||||
if( bc->size - bc->pos < count ) |
||||
return bc_need_more( bc ); |
||||
return bc->pos += count; |
||||
} |
||||
|
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
static mpg_ssize_t bc_seekback( bufferchain_t *bc, mpg_ssize_t count ) |
||||
{ |
||||
if( count >= 0 && count <= bc->pos ) |
||||
return bc->pos -= count; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
// throw away buffies that we passed.
|
||||
static void bc_forget( bufferchain_t *bc ) |
||||
{ |
||||
buffy_t *b = bc->first; |
||||
|
||||
// free all buffers that are def'n'tly outdated
|
||||
// we have buffers until filepos... delete all buffers fully below it
|
||||
while( b != NULL && bc->pos >= b->size ) |
||||
{ |
||||
buffy_t *n = b->next; // != NULL or this is indeed the end and the last cycle anyway
|
||||
|
||||
if( n == NULL ) |
||||
bc->last = NULL; // Going to delete the last buffy...
|
||||
bc->fileoff += b->size; |
||||
bc->pos -= b->size; |
||||
bc->size -= b->size; |
||||
bc_free( bc, b ); |
||||
b = n; |
||||
} |
||||
|
||||
bc->first = b; |
||||
bc->firstpos = bc->pos; |
||||
} |
||||
|
||||
// reader for input via manually provided buffers
|
||||
static int feed_init( mpg123_handle_t *fr ) |
||||
{ |
||||
bc_init( &fr->rdat.buffer ); |
||||
bc_fill_pool( &fr->rdat.buffer ); |
||||
fr->rdat.filelen = 0; |
||||
fr->rdat.filepos = 0; |
||||
fr->rdat.flags |= READER_BUFFERED; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// externally called function, returns 0 on success, -1 on error
|
||||
int feed_more( mpg123_handle_t *fr, const byte *in, long count ) |
||||
{ |
||||
if( bc_add( &fr->rdat.buffer, in, count ) != 0 ) |
||||
return MPG123_ERR; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
static mpg_ssize_t feed_read( mpg123_handle_t *fr, byte *out, mpg_ssize_t count ) |
||||
{ |
||||
mpg_ssize_t gotcount = bc_give( &fr->rdat.buffer, out, count ); |
||||
|
||||
if( gotcount >= 0 && gotcount != count ) |
||||
return MPG123_ERR; |
||||
|
||||
return gotcount; |
||||
} |
||||
|
||||
// returns reached position... negative ones are bad...
|
||||
static mpg_off_t feed_skip_bytes( mpg123_handle_t *fr, mpg_off_t len ) |
||||
{ |
||||
// this is either the new buffer offset or some negative error value.
|
||||
mpg_off_t res = bc_skip( &fr->rdat.buffer, (mpg_ssize_t)len ); |
||||
if( res < 0 ) return res; |
||||
|
||||
return fr->rdat.buffer.fileoff + res; |
||||
} |
||||
|
||||
static int feed_back_bytes( mpg123_handle_t *fr, mpg_off_t bytes ) |
||||
{ |
||||
if( bytes >= 0 ) |
||||
return bc_seekback(&fr->rdat.buffer, (mpg_ssize_t)bytes) >= 0 ? 0 : MPG123_ERR; |
||||
return feed_skip_bytes( fr, -bytes ) >= 0 ? 0 : MPG123_ERR; |
||||
} |
||||
|
||||
static int feed_seek_frame( mpg123_handle_t *fr, mpg_off_t num ) |
||||
{ |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
// not just for feed reader, also for self-feeding buffered reader.
|
||||
static void buffered_forget( mpg123_handle_t *fr ) |
||||
{ |
||||
bc_forget( &fr->rdat.buffer ); |
||||
fr->rdat.filepos = fr->rdat.buffer.fileoff + fr->rdat.buffer.pos; |
||||
} |
||||
|
||||
mpg_off_t feed_set_pos( mpg123_handle_t *fr, mpg_off_t pos ) |
||||
{ |
||||
bufferchain_t *bc = &fr->rdat.buffer; |
||||
|
||||
if( pos >= bc->fileoff && pos - bc->fileoff < bc->size ) |
||||
{ |
||||
// we have the position!
|
||||
bc->pos = (mpg_ssize_t)(pos - bc->fileoff); |
||||
|
||||
// next input after end of buffer...
|
||||
return bc->fileoff + bc->size; |
||||
} |
||||
else |
||||
{ |
||||
// i expect to get the specific position on next feed. Forget what I have now.
|
||||
bc_reset( bc ); |
||||
bc->fileoff = pos; |
||||
|
||||
// next input from exactly that position.
|
||||
return pos; |
||||
} |
||||
} |
||||
|
||||
// the specific stuff for buffered stream reader.
|
||||
static mpg_ssize_t buffered_fullread( mpg123_handle_t *fr, byte *out, mpg_ssize_t count ) |
||||
{ |
||||
bufferchain_t *bc = &fr->rdat.buffer; |
||||
mpg_ssize_t gotcount; |
||||
|
||||
if( bc->size - bc->pos < count ) |
||||
{ |
||||
// add more stuff to buffer. If hitting end of file, adjust count.
|
||||
byte readbuf[4096]; |
||||
|
||||
mpg_ssize_t need = count - (bc->size - bc->pos); |
||||
|
||||
while( need > 0 ) |
||||
{ |
||||
mpg_ssize_t got = fr->rdat.fullread( fr, readbuf, sizeof( readbuf )); |
||||
int ret; |
||||
|
||||
if( got < 0 ) |
||||
return MPG123_ERR; |
||||
|
||||
if( got > 0 && ( ret = bc_add( bc, readbuf, got )) != 0 ) |
||||
return MPG123_ERR; |
||||
|
||||
need -= got; // may underflow here...
|
||||
|
||||
if( got < sizeof( readbuf )) // that naturally catches got == 0, too.
|
||||
break; // end.
|
||||
} |
||||
|
||||
if( bc->size - bc->pos < count ) |
||||
count = bc->size - bc->pos; // we want only what we got.
|
||||
} |
||||
|
||||
gotcount = bc_give( bc, out, count ); |
||||
|
||||
if( gotcount != count ) |
||||
return MPG123_ERR; |
||||
return gotcount; |
||||
} |
||||
|
||||
// stream based operation
|
||||
static mpg_ssize_t plain_fullread( mpg123_handle_t *fr, byte *buf, mpg_ssize_t count ) |
||||
{ |
||||
mpg_ssize_t ret, cnt=0; |
||||
|
||||
// there used to be a check for expected file end here (length value or ID3 flag).
|
||||
// this is not needed:
|
||||
// 1. EOF is indicated by fdread returning zero bytes anyway.
|
||||
// 2. We get false positives of EOF for either files that grew or
|
||||
// 3. ... files that have ID3v1 tags in between (stream with intro).
|
||||
while( cnt < count ) |
||||
{ |
||||
ret = fr->rdat.fdread( fr, buf + cnt, count - cnt ); |
||||
|
||||
if( ret < 0 ) return MPG123_ERR; |
||||
if( ret == 0 ) break; |
||||
|
||||
if(!( fr->rdat.flags & READER_BUFFERED )) |
||||
fr->rdat.filepos += ret; |
||||
cnt += ret; |
||||
} |
||||
|
||||
return cnt; |
||||
} |
||||
|
||||
// wrappers for actual reading/seeking... I'm full of wrappers here.
|
||||
static mpg_off_t io_seek( reader_data_t *rdat, mpg_off_t offset, int whence ) |
||||
{ |
||||
if( rdat->flags & READER_HANDLEIO ) |
||||
{ |
||||
if( rdat->r_lseek_handle != NULL ) |
||||
return rdat->r_lseek_handle( rdat->iohandle, offset, whence ); |
||||
return -1; |
||||
} |
||||
|
||||
return rdat->lseek( rdat->filept, offset, whence ); |
||||
} |
||||
|
||||
static mpg_ssize_t io_read( reader_data_t *rdat, void *buf, size_t count ) |
||||
{ |
||||
if( rdat->flags & READER_HANDLEIO ) |
||||
{ |
||||
if( rdat->r_read_handle != NULL ) |
||||
return rdat->r_read_handle( rdat->iohandle, buf, count ); |
||||
return -1; |
||||
} |
||||
|
||||
return rdat->read( rdat->filept, buf, count ); |
||||
} |
||||
|
||||
// A normal read and a read with timeout.
|
||||
static mpg_ssize_t plain_read( mpg123_handle_t *fr, void *buf, size_t count ) |
||||
{ |
||||
return io_read( &fr->rdat, buf, count ); |
||||
} |
||||
|
||||
static mpg_off_t stream_lseek( mpg123_handle_t *fr, mpg_off_t pos, int whence ) |
||||
{ |
||||
mpg_off_t ret; |
||||
|
||||
ret = io_seek( &fr->rdat, pos, whence ); |
||||
|
||||
if( ret >= 0 ) |
||||
{ |
||||
fr->rdat.filepos = ret; |
||||
} |
||||
else |
||||
{ |
||||
fr->err = MPG123_LSEEK_FAILED; |
||||
ret = MPG123_ERR; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static void stream_close( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->rdat.flags & READER_FD_OPENED ) |
||||
close( fr->rdat.filept ); |
||||
|
||||
fr->rdat.filept = 0; |
||||
|
||||
if( fr->rdat.flags & READER_BUFFERED ) |
||||
bc_reset( &fr->rdat.buffer ); |
||||
|
||||
if( fr->rdat.flags & READER_HANDLEIO ) |
||||
{ |
||||
if( fr->rdat.cleanup_handle != NULL ) |
||||
fr->rdat.cleanup_handle( fr->rdat.iohandle ); |
||||
fr->rdat.iohandle = NULL; |
||||
} |
||||
} |
||||
|
||||
static int stream_seek_frame( mpg123_handle_t *fr, mpg_off_t newframe ) |
||||
{ |
||||
// seekable streams can go backwards and jump forwards.
|
||||
// non-seekable streams still can go forward, just not jump.
|
||||
if(( fr->rdat.flags & READER_SEEKABLE ) || ( newframe >= fr->num )) |
||||
{ |
||||
mpg_off_t preframe; // a leading frame we jump to
|
||||
mpg_off_t seek_to; // the byte offset we want to reach
|
||||
mpg_off_t to_skip; // bytes to skip to get there (can be negative)
|
||||
|
||||
// now seek to nearest leading index position and read from there until newframe is reached.
|
||||
// we use skip_bytes, which handles seekable and non-seekable streams
|
||||
// (the latter only for positive offset, which we ensured before entering here).
|
||||
seek_to = frame_index_find( fr, newframe, &preframe ); |
||||
|
||||
// no need to seek to index position if we are closer already.
|
||||
// but I am picky about fr->num == newframe, play safe by reading the frame again.
|
||||
// if you think that's stupid, don't call a seek to the current frame.
|
||||
if( fr->num >= newframe || fr->num < preframe ) |
||||
{ |
||||
to_skip = seek_to - fr->rd->tell( fr ); |
||||
if( fr->rd->skip_bytes( fr, to_skip ) != seek_to ) |
||||
return MPG123_ERR; |
||||
|
||||
fr->num = preframe - 1; // watch out! I am going to read preframe... fr->num should indicate the frame before!
|
||||
} |
||||
|
||||
while( fr->num < newframe ) |
||||
{ |
||||
// try to be non-fatal now... frameNum only gets advanced on success anyway
|
||||
if( !read_frame( fr )) break; |
||||
} |
||||
|
||||
// now the wanted frame should be ready for decoding.
|
||||
return MPG123_OK; |
||||
} |
||||
else |
||||
{ |
||||
fr->err = MPG123_NO_SEEK; |
||||
return MPG123_ERR; // invalid, no seek happened
|
||||
} |
||||
} |
||||
|
||||
// return FALSE on error, TRUE on success, READER_MORE on occasion
|
||||
static int generic_head_read( mpg123_handle_t *fr, ulong *newhead ) |
||||
{ |
||||
byte hbuf[4]; |
||||
int ret = fr->rd->fullread( fr, hbuf, 4 ); |
||||
|
||||
if( ret == MPG123_NEED_MORE ) |
||||
return ret; |
||||
|
||||
if( ret != 4 ) return FALSE; |
||||
|
||||
*newhead = ((ulong) hbuf[0] << 24) | ((ulong) hbuf[1] << 16) | ((ulong) hbuf[2] << 8) | (ulong) hbuf[3]; |
||||
|
||||
return TRUE; |
||||
} |
||||
|
||||
// return FALSE on error, TRUE on success, READER_MORE on occasion
|
||||
static int generic_head_shift( mpg123_handle_t *fr, ulong *head ) |
||||
{ |
||||
byte hbuf; |
||||
int ret = fr->rd->fullread( fr, &hbuf, 1 ); |
||||
|
||||
if( ret == MPG123_NEED_MORE ) |
||||
return ret; |
||||
|
||||
if( ret != 1 ) return FALSE; |
||||
|
||||
*head <<= 8; |
||||
*head |= hbuf; |
||||
*head &= 0xffffffff; |
||||
|
||||
return TRUE; |
||||
} |
||||
|
||||
// returns reached position... negative ones are bad...
|
||||
static mpg_off_t stream_skip_bytes( mpg123_handle_t *fr, mpg_off_t len ) |
||||
{ |
||||
if( fr->rdat.flags & READER_SEEKABLE ) |
||||
{ |
||||
mpg_off_t ret = stream_lseek( fr, len, SEEK_CUR ); |
||||
return (ret < 0) ? MPG123_ERR : ret; |
||||
} |
||||
else if( len >= 0 ) |
||||
{ |
||||
byte buf[1024]; // ThOr: Compaq cxx complained and it makes sense to me... or should one do a cast? What for?
|
||||
mpg_ssize_t ret; |
||||
|
||||
while( len > 0 ) |
||||
{ |
||||
mpg_ssize_t num = len < (mpg_off_t)sizeof( buf ) ? (mpg_ssize_t)len : (mpg_ssize_t)sizeof( buf ); |
||||
ret = fr->rd->fullread( fr, buf, num ); |
||||
if( ret < 0 ) return ret; |
||||
else if( ret == 0 ) break; // EOF... an error? interface defined to tell the actual position...
|
||||
len -= ret; |
||||
} |
||||
|
||||
return fr->rd->tell( fr ); |
||||
} |
||||
else if( fr->rdat.flags & READER_BUFFERED ) |
||||
{ |
||||
// perhaps we _can_ go a bit back.
|
||||
if( fr->rdat.buffer.pos >= -len ) |
||||
{ |
||||
fr->rdat.buffer.pos += len; |
||||
return fr->rd->tell( fr ); |
||||
} |
||||
else |
||||
{ |
||||
fr->err = MPG123_NO_SEEK; |
||||
return MPG123_ERR; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
fr->err = MPG123_NO_SEEK; |
||||
return MPG123_ERR; |
||||
} |
||||
} |
||||
|
||||
// return 0 on success...
|
||||
static int stream_back_bytes( mpg123_handle_t *fr, mpg_off_t bytes ) |
||||
{ |
||||
mpg_off_t want = fr->rd->tell( fr ) - bytes; |
||||
|
||||
if( want < 0 ) return MPG123_ERR; |
||||
|
||||
if( stream_skip_bytes( fr, -bytes ) != want ) |
||||
return MPG123_ERR; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
// returns size on success...
|
||||
static int generic_read_frame_body( mpg123_handle_t *fr, byte *buf, int size ) |
||||
{ |
||||
long l; |
||||
|
||||
if(( l = fr->rd->fullread( fr, buf, size )) != size ) |
||||
return MPG123_ERR; |
||||
|
||||
return l; |
||||
} |
||||
|
||||
static mpg_off_t generic_tell( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->rdat.flags & READER_BUFFERED ) |
||||
fr->rdat.filepos = fr->rdat.buffer.fileoff + fr->rdat.buffer.pos; |
||||
|
||||
return fr->rdat.filepos; |
||||
} |
||||
|
||||
// this does not (fully) work for non-seekable streams... You have to check for that flag, pal!
|
||||
static void stream_rewind( mpg123_handle_t *fr ) |
||||
{ |
||||
if( fr->rdat.flags & READER_SEEKABLE ) |
||||
{ |
||||
fr->rdat.filepos = stream_lseek( fr, 0, SEEK_SET ); |
||||
fr->rdat.buffer.fileoff = fr->rdat.filepos; |
||||
} |
||||
|
||||
if( fr->rdat.flags & READER_BUFFERED ) |
||||
{ |
||||
fr->rdat.buffer.pos = 0; |
||||
fr->rdat.buffer.firstpos = 0; |
||||
fr->rdat.filepos = fr->rdat.buffer.fileoff; |
||||
} |
||||
} |
||||
|
||||
// returns length of a file (if filept points to a file)
|
||||
// reads the last 128 bytes information into buffer
|
||||
// ... that is not totally safe...
|
||||
static mpg_off_t get_fileinfo( mpg123_handle_t *fr ) |
||||
{ |
||||
mpg_off_t len; |
||||
|
||||
if(( len = io_seek( &fr->rdat, 0, SEEK_END )) < 0 ) |
||||
return -1; |
||||
|
||||
if( io_seek( &fr->rdat, -128, SEEK_END ) < 0 ) |
||||
return -1; |
||||
|
||||
if( fr->rd->fullread( fr, (byte *)fr->id3buf, 128 ) != 128 ) |
||||
return -1; |
||||
|
||||
if( !strncmp((char *)fr->id3buf, "TAG", 3 )) |
||||
len -= 128; |
||||
|
||||
if( io_seek( &fr->rdat, 0, SEEK_SET ) < 0 ) |
||||
return -1; |
||||
|
||||
if( len <= 0 ) |
||||
return -1; |
||||
|
||||
return len; |
||||
} |
||||
|
||||
static int bad_init( mpg123_handle_t *mh ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static mpg_ssize_t bad_fullread( mpg123_handle_t *mh, byte *data, mpg_ssize_t count ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static int bad_head_read( mpg123_handle_t *mh, ulong *newhead ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static int bad_head_shift( mpg123_handle_t *mh, ulong *head ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static mpg_off_t bad_skip_bytes( mpg123_handle_t *mh, mpg_off_t len ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static int bad_read_frame_body( mpg123_handle_t *mh, byte *data, int size ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static int bad_back_bytes( mpg123_handle_t *mh, mpg_off_t bytes ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static int bad_seek_frame( mpg123_handle_t *mh, mpg_off_t num ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static mpg_off_t bad_tell( mpg123_handle_t *mh ) { mh->err = MPG123_NO_READER; return MPG123_ERR; } |
||||
static void bad_rewind( mpg123_handle_t *mh ) { } |
||||
static void bad_close( mpg123_handle_t *mh ) { } |
||||
|
||||
static reader_t bad_reader = |
||||
{ |
||||
bad_init, |
||||
bad_close, |
||||
bad_fullread, |
||||
bad_head_read, |
||||
bad_head_shift, |
||||
bad_skip_bytes, |
||||
bad_read_frame_body, |
||||
bad_back_bytes, |
||||
bad_seek_frame, |
||||
bad_tell, |
||||
bad_rewind, |
||||
NULL |
||||
}; |
||||
|
||||
void open_bad( mpg123_handle_t *mh ) |
||||
{ |
||||
mh->rd = &bad_reader; |
||||
mh->rdat.flags = 0; |
||||
bc_init( &mh->rdat.buffer ); |
||||
mh->rdat.filelen = -1; |
||||
} |
||||
|
||||
static reader_t readers[] = |
||||
{ |
||||
{ // READER_STREAM
|
||||
default_init, |
||||
stream_close, |
||||
plain_fullread, |
||||
generic_head_read, |
||||
generic_head_shift, |
||||
stream_skip_bytes, |
||||
generic_read_frame_body, |
||||
stream_back_bytes, |
||||
stream_seek_frame, |
||||
generic_tell, |
||||
stream_rewind, |
||||
NULL |
||||
}, |
||||
{ // READER_FEED
|
||||
feed_init, |
||||
stream_close, |
||||
feed_read, |
||||
generic_head_read, |
||||
generic_head_shift, |
||||
feed_skip_bytes, |
||||
generic_read_frame_body, |
||||
feed_back_bytes, |
||||
feed_seek_frame, |
||||
generic_tell, |
||||
stream_rewind, |
||||
buffered_forget |
||||
}, |
||||
{ // READER_BUF_STREAM
|
||||
default_init, |
||||
stream_close, |
||||
buffered_fullread, |
||||
generic_head_read, |
||||
generic_head_shift, |
||||
stream_skip_bytes, |
||||
generic_read_frame_body, |
||||
stream_back_bytes, |
||||
stream_seek_frame, |
||||
generic_tell, |
||||
stream_rewind, |
||||
buffered_forget |
||||
} |
||||
}; |
||||
|
||||
// final code common to open_stream and open_stream_handle.
|
||||
static int open_finish( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->rd = &readers[READER_STREAM]; |
||||
if( fr->rd->init( fr ) < 0 ) |
||||
return -1; |
||||
|
||||
return MPG123_OK; |
||||
} |
||||
|
||||
int open_stream_handle( mpg123_handle_t *fr, void *iohandle ) |
||||
{ |
||||
fr->rdat.filelen = -1; |
||||
fr->rdat.filept = -1; |
||||
fr->rdat.iohandle = iohandle; |
||||
fr->rdat.flags = 0; |
||||
fr->rdat.flags |= READER_HANDLEIO; |
||||
|
||||
return open_finish( fr ); |
||||
} |
||||
|
||||
int open_feed( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->rd = &readers[READER_FEED]; |
||||
fr->rdat.flags = 0; |
||||
|
||||
if( fr->rd->init( fr ) < 0 ) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int default_init( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->rdat.fdread = plain_read; |
||||
fr->rdat.read = fr->rdat.r_read != NULL ? fr->rdat.r_read : read; |
||||
fr->rdat.lseek = fr->rdat.r_lseek != NULL ? fr->rdat.r_lseek : lseek; |
||||
fr->rdat.filelen = get_fileinfo( fr ); |
||||
fr->rdat.filepos = 0; |
||||
|
||||
// don't enable seeking on ICY streams, just plain normal files.
|
||||
// this check is necessary since the client can enforce ICY parsing on files that would otherwise be seekable.
|
||||
// it is a task for the future to make the ICY parsing safe with seeks ... or not.
|
||||
if( fr->rdat.filelen >= 0 ) |
||||
{ |
||||
fr->rdat.flags |= READER_SEEKABLE; |
||||
if( !strncmp((char *)fr->id3buf,"TAG", 3 )) |
||||
{ |
||||
fr->rdat.flags |= READER_ID3TAG; |
||||
fr->metaflags |= MPG123_NEW_ID3; |
||||
} |
||||
} |
||||
else if( fr->p.flags & MPG123_SEEKBUFFER ) |
||||
{ |
||||
// switch reader to a buffered one, if allowed.
|
||||
if( fr->rd == &readers[READER_STREAM] ) |
||||
{ |
||||
fr->rd = &readers[READER_BUF_STREAM]; |
||||
fr->rdat.fullread = plain_fullread; |
||||
} |
||||
else |
||||
{ |
||||
return -1; |
||||
} |
||||
|
||||
bc_init( &fr->rdat.buffer ); |
||||
fr->rdat.filelen = 0; // we carry the offset, but never know how big the stream is.
|
||||
fr->rdat.flags |= READER_BUFFERED; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
reader.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef READER_H |
||||
#define READER_H |
||||
|
||||
#define READER_FD_OPENED 0x1 |
||||
#define READER_ID3TAG 0x2 |
||||
#define READER_SEEKABLE 0x4 |
||||
#define READER_BUFFERED 0x8 |
||||
#define READER_NONBLOCK 0x20 |
||||
#define READER_HANDLEIO 0x40 |
||||
|
||||
typedef struct buffy_s |
||||
{ |
||||
byte *data; |
||||
mpg_ssize_t size; |
||||
mpg_ssize_t realsize; |
||||
struct buffy_s *next; |
||||
} buffy_t; |
||||
|
||||
typedef struct bufferchain_s |
||||
{ |
||||
struct buffy_s *first; // the beginning of the chain.
|
||||
struct buffy_s *last; // the end... of the chain.
|
||||
mpg_ssize_t size; // aggregated size of all buffies.
|
||||
|
||||
// these positions are relative to buffer chain beginning.
|
||||
mpg_ssize_t pos; // position in whole chain.
|
||||
mpg_ssize_t firstpos; // the point of return on non-forget()
|
||||
|
||||
// the "real" filepos is fileoff + pos.
|
||||
mpg_off_t fileoff; // beginning of chain is at this file offset.
|
||||
size_t bufblock; // default (minimal) size of buffers.
|
||||
size_t pool_size; // keep that many buffers in storage.
|
||||
size_t pool_fill; // that many buffers are there.
|
||||
|
||||
// a pool of buffers to re-use, if activated. It's a linked list that is worked on from the front.
|
||||
struct buffy_s *pool; |
||||
} bufferchain_t; |
||||
|
||||
// call this before any buffer chain use (even bc_init()).
|
||||
void bc_prepare( bufferchain_t*, size_t pool_size, size_t bufblock ); |
||||
// free persistent data in the buffer chain, after bc_reset().
|
||||
void bc_cleanup( bufferchain_t* ); |
||||
// change pool size. This does not actually allocate/free anything on itself, just instructs later operations to free less / allocate more buffers.
|
||||
void bc_poolsize( bufferchain_t*, size_t pool_size, size_t bufblock ); |
||||
// return available byte count in the buffer.
|
||||
size_t bc_fill( bufferchain_t *bc ); |
||||
|
||||
typedef struct reader_data_s |
||||
{ |
||||
mpg_off_t filelen; // total file length or total buffer size
|
||||
mpg_off_t filepos; // position in file or position in buffer chain
|
||||
int filept; |
||||
|
||||
// custom opaque I/O handle from the client.
|
||||
void *iohandle; |
||||
int flags; |
||||
long timeout_sec; |
||||
|
||||
mpg_ssize_t (*fdread)( mpg123_handle_t*, void*, size_t ); |
||||
|
||||
// user can replace the read and lseek functions. The r_* are the stored replacement functions or NULL.
|
||||
mpg_ssize_t (*r_read)( int fd, void *buf, size_t count ); |
||||
mpg_off_t (*r_lseek)( int fd, mpg_off_t offset, int whence ); |
||||
|
||||
// These are custom I/O routines for opaque user handles.
|
||||
// They get picked if there's some iohandle set.
|
||||
mpg_ssize_t (*r_read_handle)( void *handle, void *buf, size_t count ); |
||||
mpg_off_t (*r_lseek_handle)( void *handle, mpg_off_t offset, int whence ); |
||||
|
||||
// an optional cleaner for the handle on closing the stream.
|
||||
void (*cleanup_handle)( void *handle ); |
||||
|
||||
// these two pointers are the actual workers (default map to POSIX read/lseek).
|
||||
mpg_ssize_t (*read)( int fd, void *buf, size_t count ); |
||||
mpg_off_t (*lseek)( int fd, mpg_off_t offset, int whence ); |
||||
|
||||
// buffered readers want that abstracted, set internally.
|
||||
mpg_ssize_t (*fullread)( mpg123_handle_t*, byte*, mpg_ssize_t ); |
||||
|
||||
bufferchain_t buffer; // not dynamically allocated, these few struct bytes aren't worth the trouble.
|
||||
} reader_data_t; |
||||
|
||||
// start to use mpg_off_t to properly do LFS in future ... used to be long
|
||||
typedef struct reader_s |
||||
{ |
||||
int (*init)( mpg123_handle_t* ); |
||||
void (*close)( mpg123_handle_t* ); |
||||
mpg_ssize_t (*fullread)( mpg123_handle_t*, byte*, mpg_ssize_t); |
||||
int (*head_read)( mpg123_handle_t*, ulong *newhead ); // succ: TRUE, else <= 0 (FALSE or READER_MORE)
|
||||
int (*head_shift)( mpg123_handle_t*, ulong *head ); // succ: TRUE, else <= 0 (FALSE or READER_MORE)
|
||||
mpg_off_t (*skip_bytes)( mpg123_handle_t*, mpg_off_t len ); // succ: >=0, else error or READER_MORE
|
||||
int (*read_frame_body)( mpg123_handle_t*, byte*, int size ); |
||||
int (*back_bytes)( mpg123_handle_t*, mpg_off_t bytes ); |
||||
int (*seek_frame)( mpg123_handle_t*, mpg_off_t num ); |
||||
mpg_off_t (*tell)( mpg123_handle_t* ); |
||||
void (*rewind)( mpg123_handle_t* ); |
||||
void (*forget)( mpg123_handle_t* ); |
||||
} reader_t; |
||||
|
||||
// open a file by path or use an opened file descriptor
|
||||
int open_stream( mpg123_handle_t *fr, const char *path, int fd ); |
||||
// open an external handle.
|
||||
int open_stream_handle( mpg123_handle_t *fr, void *iohandle ); |
||||
|
||||
// feed based operation has some specials
|
||||
int open_feed( mpg123_handle_t *fr ); |
||||
// externally called function, returns 0 on success, -1 on error
|
||||
int feed_more( mpg123_handle_t *fr, const byte *in, long count ); |
||||
// forget the data that has been read (free some buffers)
|
||||
void feed_forget( mpg123_handle_t *fr ); |
||||
// set position (inside available data if possible), return wanted byte offset of next feed.
|
||||
mpg_off_t feed_set_pos( mpg123_handle_t *fr, mpg_off_t pos ); |
||||
// error fallback
|
||||
void open_bad( mpg123_handle_t *fr ); |
||||
|
||||
#endif//READER_H
|
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
sample.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef SAMPLE_H |
||||
#define SAMPLE_H |
||||
|
||||
// define the accurate rounding function.
|
||||
#ifdef IEEE_FLOAT |
||||
// rhis function is only available for IEEE754 single-precision values
|
||||
// rhis is nearly identical to proper rounding, just -+0.5 is rounded to 0
|
||||
static _inline int16_t ftoi16( float x ) |
||||
{ |
||||
union |
||||
{ |
||||
float f; |
||||
int32_t i; |
||||
} u_fi; |
||||
|
||||
u_fi.f = x + 12582912.0f; // Magic Number: 2^23 + 2^22
|
||||
return (int16_t)u_fi.i; |
||||
} |
||||
|
||||
#define REAL_TO_SHORT_ACCURATE( x ) ftoi16(x) |
||||
#else |
||||
// the "proper" rounding, plain C, a bit slow.
|
||||
#define REAL_TO_SHORT_ACCURATE( x ) (short)((x) > 0.0 ? (x) + 0.5 : (x) - 0.5) |
||||
#endif |
||||
|
||||
// now define the normal rounding.
|
||||
#ifdef ACCURATE_ROUNDING |
||||
#define REAL_TO_SHORT( x ) REAL_TO_SHORT_ACCURATE( x ) |
||||
#else |
||||
#define REAL_TO_SHORT( x ) (short)( x ) |
||||
#endif |
||||
|
||||
#endif//SAMPLE_H
|
@ -0,0 +1,311 @@
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
synth.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include "sample.h" |
||||
|
||||
#define BACKPEDAL 0x10 // we use autoincrement and thus need this re-adjustment for window/b0.
|
||||
#define BLOCK 0x40 // one decoding block is 64 samples.
|
||||
|
||||
#define WRITE_SHORT_SAMPLE( samples, sum, clip ) \ |
||||
if(( sum ) > 32767.0 ) { *(samples) = 0x7fff; (clip)++; } \ |
||||
else if(( sum ) < -32768.0 ) { *(samples) = -0x8000; (clip)++; } \ |
||||
else { *(samples) = REAL_TO_SHORT( sum ); } |
||||
|
||||
// main synth function, uses the plain dct64
|
||||
int synth_1to1( float *bandPtr, int channel, mpg123_handle_t *fr, int final ) |
||||
{ |
||||
static const int step = 2; |
||||
short *samples = (short *) (fr->buffer.data + fr->buffer.fill); |
||||
float *b0, **buf; // (*buf)[0x110];
|
||||
int clip = 0; |
||||
int bo1; |
||||
|
||||
if( !channel ) |
||||
{ |
||||
fr->bo--; |
||||
fr->bo &= 0xf; |
||||
buf = fr->float_buffs[0]; |
||||
} |
||||
else |
||||
{ |
||||
samples++; |
||||
buf = fr->float_buffs[1]; |
||||
} |
||||
|
||||
if( fr->bo & 0x1 ) |
||||
{ |
||||
b0 = buf[0]; |
||||
bo1 = fr->bo; |
||||
dct64( buf[1] + ((fr->bo + 1) & 0xf ), buf[0] + fr->bo, bandPtr ); |
||||
} |
||||
else |
||||
{ |
||||
b0 = buf[1]; |
||||
bo1 = fr->bo+1; |
||||
dct64( buf[0] + fr->bo, buf[1] + fr->bo + 1, bandPtr ); |
||||
} |
||||
|
||||
{ |
||||
float *window = fr->decwin + 16 - bo1; |
||||
register int j; |
||||
|
||||
for( j = (BLOCK / 4); j; j--, b0 += 0x400 / BLOCK - BACKPEDAL, window += 0x800 / BLOCK - BACKPEDAL, samples += step ) |
||||
{ |
||||
float sum; |
||||
|
||||
sum = REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum += REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *window++, *b0++ ); |
||||
|
||||
WRITE_SHORT_SAMPLE( samples, sum, clip ); |
||||
} |
||||
|
||||
{ |
||||
float sum; |
||||
|
||||
sum = REAL_MUL_SYNTH( window[0x0], b0[0x0] ); |
||||
sum += REAL_MUL_SYNTH( window[0x2], b0[0x2] ); |
||||
sum += REAL_MUL_SYNTH( window[0x4], b0[0x4] ); |
||||
sum += REAL_MUL_SYNTH( window[0x6], b0[0x6] ); |
||||
sum += REAL_MUL_SYNTH( window[0x8], b0[0x8] ); |
||||
sum += REAL_MUL_SYNTH( window[0xA], b0[0xA] ); |
||||
sum += REAL_MUL_SYNTH( window[0xC], b0[0xC] ); |
||||
sum += REAL_MUL_SYNTH( window[0xE], b0[0xE] ); |
||||
|
||||
WRITE_SHORT_SAMPLE( samples, sum, clip ); |
||||
samples += step; |
||||
b0 -= 0x400 / BLOCK; |
||||
window -= 0x800 / BLOCK; |
||||
} |
||||
window += bo1<<1; |
||||
|
||||
for( j= (BLOCK / 4) - 1; j; j--, b0 -= 0x400 / BLOCK + BACKPEDAL, window -= 0x800 / BLOCK - BACKPEDAL, samples += step ) |
||||
{ |
||||
float sum; |
||||
|
||||
sum = -REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
sum -= REAL_MUL_SYNTH( *(--window), *b0++ ); |
||||
|
||||
WRITE_SHORT_SAMPLE( samples, sum, clip ); |
||||
} |
||||
} |
||||
|
||||
if( final ) fr->buffer.fill += BLOCK * sizeof( short ); |
||||
|
||||
return clip; |
||||
} |
||||
|
||||
// the call of left and right plain synth, wrapped.
|
||||
// this may be replaced by a direct stereo optimized synth.
|
||||
static int synth_stereo( float *bandPtr_l, float *bandPtr_r, mpg123_handle_t *fr ) |
||||
{ |
||||
int clip; |
||||
|
||||
clip = (fr->synth)( bandPtr_l, 0, fr, 0 ); |
||||
clip += (fr->synth)( bandPtr_r, 1, fr, 1 ); |
||||
return clip; |
||||
} |
||||
|
||||
// mono to stereo synth, wrapping over synth_1to1
|
||||
int synth_1to1_m2s(float *bandPtr, mpg123_handle_t *fr ) |
||||
{ |
||||
byte *samples = fr->buffer.data; |
||||
int i, ret; |
||||
|
||||
ret = synth_1to1( bandPtr, 0, fr, 1 ); |
||||
samples += fr->buffer.fill - BLOCK * sizeof( short ); |
||||
|
||||
for( i = 0; i < (BLOCK / 2); i++ ) |
||||
{ |
||||
((short *)samples)[1] = ((short *)samples)[0]; |
||||
samples += 2 * sizeof( short ); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
// mono synth, wrapping over synth_1to1
|
||||
int synth_1to1_mono( float *bandPtr, mpg123_handle_t *fr ) |
||||
{ |
||||
short samples_tmp[BLOCK]; |
||||
short *tmp1 = samples_tmp; |
||||
byte *samples = fr->buffer.data; |
||||
int pnt = fr->buffer.fill; |
||||
int i, ret; |
||||
|
||||
// save buffer stuff, trick samples_tmp into there, decode, restore
|
||||
fr->buffer.data = (byte *)samples_tmp; |
||||
fr->buffer.fill = 0; |
||||
|
||||
ret = synth_1to1( bandPtr, 0, fr, 0 ); // decode into samples_tmp
|
||||
fr->buffer.data = samples; // restore original value
|
||||
|
||||
// now append samples from samples_tmp
|
||||
samples += pnt; // just the next mem in frame buffer
|
||||
|
||||
for( i = 0; i < (BLOCK / 2); i++ ) |
||||
{ |
||||
*((short *)samples) = *tmp1; |
||||
samples += sizeof( short ); |
||||
tmp1 += 2; |
||||
} |
||||
|
||||
fr->buffer.fill = pnt + (BLOCK / 2) * sizeof( short ); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static const struct synth_s synth_base = |
||||
{ |
||||
{ |
||||
{ synth_1to1 } // plain
|
||||
}, |
||||
{ |
||||
{ synth_stereo } // stereo, by default only wrappers over plain synth
|
||||
}, |
||||
{ |
||||
{ synth_1to1_m2s } // mono2stereo
|
||||
}, |
||||
{ |
||||
{ synth_1to1_mono } // mono
|
||||
} |
||||
}; |
||||
|
||||
void init_synth( mpg123_handle_t *fr ) |
||||
{ |
||||
fr->synths = synth_base; |
||||
} |
||||
|
||||
static int find_synth(func_synth synth, const func_synth synths[r_limit][f_limit]) |
||||
{ |
||||
enum synth_resample ri; |
||||
enum synth_format fi; |
||||
|
||||
for( ri = 0; ri < r_limit; ++ri ) |
||||
{ |
||||
for( fi = 0; fi < f_limit; ++fi ) |
||||
{ |
||||
if( synth == synths[ri][fi] ) |
||||
return TRUE; |
||||
} |
||||
} |
||||
|
||||
return FALSE; |
||||
} |
||||
|
||||
enum optdec |
||||
{ |
||||
autodec = 0, |
||||
generic, |
||||
nodec |
||||
}; |
||||
|
||||
// determine what kind of decoder is actually active
|
||||
// this depends on runtime choices which may cause fallback to i386 or generic code.
|
||||
static int find_dectype( mpg123_handle_t *fr ) |
||||
{ |
||||
enum optdec type = nodec; |
||||
func_synth basic_synth = fr->synth; |
||||
|
||||
if( find_synth( basic_synth, synth_base.plain )) |
||||
type = generic; |
||||
|
||||
if( type != nodec ) |
||||
{ |
||||
return MPG123_OK; |
||||
} |
||||
else |
||||
{ |
||||
fr->err = MPG123_BAD_DECODER_SETUP; |
||||
return MPG123_ERR; |
||||
} |
||||
} |
||||
|
||||
|
||||
// set synth functions for current frame
|
||||
int set_synth_functions( mpg123_handle_t *fr ) |
||||
{ |
||||
enum synth_resample resample = r_none; |
||||
enum synth_format basic_format = f_none; // default is always 16bit, or whatever.
|
||||
|
||||
if( fr->af.dec_enc & MPG123_ENC_16 ) |
||||
basic_format = f_16; |
||||
|
||||
// make sure the chosen format is compiled into this lib.
|
||||
if( basic_format == f_none ) |
||||
return -1; |
||||
|
||||
// be explicit about downsampling variant.
|
||||
switch( fr->down_sample ) |
||||
{ |
||||
case 0: resample = r_1to1; break; |
||||
} |
||||
|
||||
if( resample == r_none ) |
||||
return -1; |
||||
|
||||
// finally selecting the synth functions for stereo / mono.
|
||||
fr->synth = fr->synths.plain[resample][basic_format]; |
||||
fr->synth_stereo = fr->synths.stereo[resample][basic_format]; |
||||
fr->synth_mono = fr->af.channels == 2 ? fr->synths.mono2stereo[resample][basic_format] : fr->synths.mono[resample][basic_format]; |
||||
|
||||
if( find_dectype( fr ) != MPG123_OK ) |
||||
{ |
||||
fr->err = MPG123_BAD_DECODER_SETUP; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
if( frame_buffers( fr ) != 0 ) |
||||
{ |
||||
fr->err = MPG123_NO_BUFFERS; |
||||
return MPG123_ERR; |
||||
} |
||||
|
||||
init_layer3_stuff( fr ); |
||||
fr->make_decode_tables = make_decode_tables; |
||||
|
||||
// we allocated the table buffers just now, so (re)create the tables.
|
||||
fr->make_decode_tables( fr ); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
synth.h - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef SYNTH_H |
||||
#define SYNTH_H |
||||
|
||||
typedef int (*func_synth)( float*, int, mpg123_handle_t*, int ); |
||||
typedef int (*func_synth_mono)( float*, mpg123_handle_t* ); |
||||
typedef int (*func_synth_stereo)( float*, float*, mpg123_handle_t* ); |
||||
|
||||
enum synth_channel |
||||
{ |
||||
c_plain = 0, |
||||
c_stereo, |
||||
c_m2s, |
||||
c_mono, |
||||
c_limit |
||||
}; |
||||
|
||||
enum synth_resample |
||||
{ |
||||
r_none = -1, |
||||
r_1to1 = 0, |
||||
r_limit |
||||
}; |
||||
|
||||
enum synth_format |
||||
{ |
||||
f_none = -1, |
||||
f_16, |
||||
f_limit |
||||
}; |
||||
|
||||
typedef struct synth_s |
||||
{ |
||||
func_synth plain[r_limit][f_limit]; |
||||
func_synth_stereo stereo[r_limit][f_limit]; |
||||
func_synth_mono mono2stereo[r_limit][f_limit]; |
||||
func_synth_mono mono[r_limit][f_limit]; |
||||
} synth_t; |
||||
|
||||
void init_synth( mpg123_handle_t *fr ); |
||||
int set_synth_functions( mpg123_handle_t *fr ); |
||||
|
||||
#endif//SYNTH_H
|
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
tabinit.c - compact version of famous library mpg123 |
||||
Copyright (C) 2017 Uncle Mike |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include "mpg123.h" |
||||
#include <math.h> |
||||
|
||||
static float cos64[16]; |
||||
static float cos32[8]; |
||||
static float cos16[4]; |
||||
static float cos8[2]; |
||||
static float cos4[1]; |
||||
|
||||
static long intwinbase[] = { |
||||
0, -1, -1, -1, -1, -1, -1, -2, -2, -2, |
||||
-2, -3, -3, -4, -4, -5, -5, -6, -7, -7, |
||||
-8, -9, -10, -11, -13, -14, -16, -17, -19, -21, |
||||
-24, -26, -29, -31, -35, -38, -41, -45, -49, -53, |
||||
-58, -63, -68, -73, -79, -85, -91, -97, -104, -111, |
||||
-117, -125, -132, -139, -147, -154, -161, -169, -176, -183, |
||||
-190, -196, -202, -208, -213, -218, -222, -225, -227, -228, |
||||
-228, -227, -224, -221, -215, -208, -200, -189, -177, -163, |
||||
-146, -127, -106, -83, -57, -29, 2, 36, 72, 111, |
||||
153, 197, 244, 294, 347, 401, 459, 519, 581, 645, |
||||
711, 779, 848, 919, 991, 1064, 1137, 1210, 1283, 1356, |
||||
1428, 1498, 1567, 1634, 1698, 1759, 1817, 1870, 1919, 1962, |
||||
2001, 2032, 2057, 2075, 2085, 2087, 2080, 2063, 2037, 2000, |
||||
1952, 1893, 1822, 1739, 1644, 1535, 1414, 1280, 1131, 970, |
||||
794, 605, 402, 185, -45, -288, -545, -814, -1095, -1388, |
||||
-1692, -2006, -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, |
||||
-5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, -7910, -8209, |
||||
-8491, -8755, -8998, -9219, -9416, -9585, -9727, -9838, -9916, -9959, |
||||
-9966, -9935, -9863, -9750, -9592, -9389, -9139, -8840, -8492, -8092, |
||||
-7640, -7134, -6574, -5959, -5288, -4561, -3776, -2935, -2037, -1082, |
||||
-70, 998, 2122, 3300, 4533, 5818, 7154, 8540, 9975, 11455, |
||||
12980, 14548, 16155, 17799, 19478, 21189, 22929, 24694, 26482, 28289, |
||||
30112, 31947, 33791, 35640, 37489, 39336, 41176, 43006, 44821, 46617, |
||||
48390, 50137, 51853, 53534, 55178, 56778, 58333, 59838, 61289, 62684, |
||||
64019, 65290, 66494, 67629, 68692, 69679, 70590, 71420, 72169, 72835, |
||||
73415, 73908, 74313, 74630, 74856, 74992, 75038 }; |
||||
|
||||
float *pnts[] = { cos64, cos32, cos16, cos8, cos4 }; |
||||
|
||||
void prepare_decode_tables( void ) |
||||
{ |
||||
int i, k, kr, divv; |
||||
float *costab; |
||||
|
||||
for( i = 0; i < 5; i++ ) |
||||
{ |
||||
kr = 0x10 >> i; |
||||
divv = 0x40 >> i; |
||||
costab = pnts[i]; |
||||
|
||||
for( k = 0; k < kr; k++) |
||||
costab[k] = DOUBLE_TO_REAL( 1.0 / ( 2.0 * cos( M_PI * ((double)k * 2.0 + 1.0 ) / (double)divv ))); |
||||
} |
||||
} |
||||
|
||||
void make_decode_tables( mpg123_handle_t *fr ) |
||||
{ |
||||
int i, j; |
||||
int idx = 0; |
||||
double scaleval; |
||||
|
||||
// scale is always based on 1.0.
|
||||
scaleval = -0.5 * (fr->lastscale < 0 ? fr->p.outscale : fr->lastscale); |
||||
|
||||
for( i = 0, j = 0; i < 256; i++, j++, idx += 32 ) |
||||
{ |
||||
if( idx < 512 + 16 ) |
||||
fr->decwin[idx+16] = fr->decwin[idx] = DOUBLE_TO_REAL( (double)intwinbase[j] * scaleval ); |
||||
|
||||
if( i % 32 == 31 ) |
||||
idx -= 1023; |
||||
|
||||
if( i % 64 == 63 ) |
||||
scaleval = -scaleval; |
||||
} |
||||
|
||||
for( ; i < 512; i++, j--, idx += 32 ) |
||||
{ |
||||
if( idx < 512 + 16 ) |
||||
fr->decwin[idx+16] = fr->decwin[idx] = DOUBLE_TO_REAL( (double)intwinbase[j] * scaleval ); |
||||
|
||||
if( i % 32 == 31 ) |
||||
idx -= 1023; |
||||
if( i % 64 == 63 ) |
||||
scaleval = -scaleval; |
||||
} |
||||
} |
Loading…
Reference in new issue