You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
8.7 KiB
435 lines
8.7 KiB
/* |
|
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; |
|
} |
|
}
|
|
|