diff options
Diffstat (limited to 'src/libmpg123/format.c')
-rw-r--r-- | src/libmpg123/format.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/src/libmpg123/format.c b/src/libmpg123/format.c new file mode 100644 index 0000000..0b37bfa --- /dev/null +++ b/src/libmpg123/format.c @@ -0,0 +1,395 @@ +/* + format:routines to deal with audio (output) format + + copyright 2008-9 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 Thomas Orgis, starting with parts of the old audio.c, with only faintly manage to show now +*/ + +#include "mpg123lib_intern.h" +#include "debug.h" + +/* static int chans[NUM_CHANNELS] = { 1 , 2 }; */ +static const long my_rates[MPG123_RATES] = /* only the standard 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, + MPG123_ENC_SIGNED_32, + MPG123_ENC_UNSIGNED_32, + MPG123_ENC_FLOAT_32, + MPG123_ENC_FLOAT_64, + MPG123_ENC_SIGNED_8, + MPG123_ENC_UNSIGNED_8, + MPG123_ENC_ULAW_8, + MPG123_ENC_ALAW_8 +}; + +/* Only one type of float is supported. */ +# ifdef REAL_IS_FLOAT +# define MPG123_FLOAT_ENC MPG123_ENC_FLOAT_32 +# else +# define MPG123_FLOAT_ENC MPG123_ENC_FLOAT_64 +# endif + +/* The list of actually possible encodings. */ +static const int good_encodings[] = +{ +#ifndef NO_16BIT + MPG123_ENC_SIGNED_16, + MPG123_ENC_UNSIGNED_16, +#endif +#ifndef NO_32BIT + MPG123_ENC_SIGNED_32, + MPG123_ENC_UNSIGNED_32, +#endif +#ifndef NO_REAL + MPG123_FLOAT_ENC, +#endif +#ifndef NO_8BIT + MPG123_ENC_SIGNED_8, + MPG123_ENC_UNSIGNED_8, + MPG123_ENC_ULAW_8, + MPG123_ENC_ALAW_8 +#endif +}; + +/* Check if encoding is a valid one in this build. + ...lazy programming: linear search. */ +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 attribute_align_arg mpg123_rates(const long **list, size_t *number) +{ + if(list != NULL) *list = my_rates; + if(number != NULL) *number = sizeof(my_rates)/sizeof(long); +} + +/* Now that's a bit tricky... One build of the library knows only a subset of the encodings. */ +void attribute_align_arg mpg123_encodings(const int **list, size_t *number) +{ + if(list != NULL) *list = good_encodings; + if(number != NULL) *number = sizeof(good_encodings)/sizeof(int); +} + +/* char audio_caps[NUM_CHANNELS][MPG123_RATES+1][MPG123_ENCODINGS]; */ + +static int rate2num(mpg123_pars *mp, long r) +{ + int i; + for(i=0;i<MPG123_RATES;i++) if(my_rates[i] == r) return i; +#ifndef NO_NTOM + if(mp && mp->force_rate != 0 && mp->force_rate == r) return MPG123_RATES; +#endif + + 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 *fr, struct audioformat *nf, int f0, int f2) +{ + int i; + int c = nf->channels-1; + int rn = rate2num(&fr->p, 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 *fr, struct audioformat *nf, int f0, int f2) +{ + nf->rate = frame_freq(fr)>>fr->p.down_sample; + if(cap_fit(fr,nf,f0,f2)) return 1; + nf->rate>>=1; + if(cap_fit(fr,nf,f0,f2)) return 1; + nf->rate>>=1; + if(cap_fit(fr,nf,f0,f2)) return 1; +#ifndef NO_NTOM + /* If nothing worked, try the other rates, only without constrains from user. + In case you didn't guess: We enable flexible resampling if we find a working rate. */ + if(!fr->p.force_rate && fr->p.down_sample == 0) + { + int i; + int c = nf->channels-1; + int rn = rate2num(&fr->p, frame_freq(fr)); + int rrn; + if(rn < 0) return 0; + /* Try higher rates first. */ + for(i=f0;i<f2;i++) for(rrn=rn+1; rrn<MPG123_RATES; ++rrn) + if(fr->p.audio_caps[c][rrn][i]) + { + nf->rate = my_rates[rrn]; + nf->encoding = my_encodings[i]; + return 1; + } + /* Then lower rates. */ + for(i=f0;i<f2;i++) for(rrn=rn-1; rrn>=0; --rrn) + if(fr->p.audio_caps[c][rrn][i]) + { + nf->rate = my_rates[rrn]; + nf->encoding = my_encodings[i]; + return 1; + } + } +#endif + + 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 *fr) +{ + struct audioformat nf; + int f0=0; + int f2=MPG123_ENCODINGS; /* Omit the 32bit and float encodings. */ + mpg123_pars *p = &fr->p; + /* initialize new format, encoding comes later */ + nf.channels = fr->stereo; + + /* All this forcing should be removed in favour of the capabilities table... */ + if(p->flags & MPG123_FORCE_8BIT) + { + f0 = 6; + f2 = 10; + } + if(p->flags & MPG123_FORCE_FLOAT) + { + f0 = 4; + f2 = 6; + } + + /* force stereo is stronger */ + if(p->flags & MPG123_FORCE_MONO) nf.channels = 1; + if(p->flags & MPG123_FORCE_STEREO) nf.channels = 2; + +#ifndef NO_NTOM + if(p->force_rate) + { + nf.rate = p->force_rate; + if(cap_fit(fr,&nf,f0,2)) goto end; /* 16bit encodings */ + if(cap_fit(fr,&nf,f0<=2 ? 2 : f0,f2)) goto end; /* 8bit encodings */ + + /* 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(cap_fit(fr,&nf,f0,2)) goto end; /* 16bit encodings */ + if(cap_fit(fr,&nf,f0<=2 ? 2 : f0,f2)) goto end; /* 8bit encodings */ + + if(NOQUIET) + error3( "Unable to set up output format! Constraints: %s%s%liHz.", + ( p->flags & MPG123_FORCE_STEREO ? "stereo, " : + (p->flags & MPG123_FORCE_MONO ? "mono, " : "") ), + (p->flags & MPG123_FORCE_8BIT ? "8bit, " : ""), + p->force_rate ); +/* if(NOQUIET && p->verbose <= 1) print_capabilities(fr); */ + + fr->err = MPG123_BAD_OUTFORMAT; + return -1; + } +#endif + + 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 */ + + /* Here is the _bad_ end. */ + if(NOQUIET) + { + error5( "Unable to set up output format! Constraints: %s%s%li, %li or %liHz.", + ( p->flags & MPG123_FORCE_STEREO ? "stereo, " : + (p->flags & MPG123_FORCE_MONO ? "mono, " : "") ), + (p->flags & MPG123_FORCE_8BIT ? "8bit, " : ""), + frame_freq(fr), frame_freq(fr)>>1, frame_freq(fr)>>2 ); + } +/* if(NOQUIET && p->verbose <= 1) print_capabilities(fr); */ + + 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) + { + debug2("Old format with %i channels, and FORCE_MONO=%li", nf.channels, p->flags & MPG123_FORCE_MONO); + return 0; /* the same format as before */ + } + else /* a new format */ + { + debug1("New format with %i channels!", nf.channels); + 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. */ + if(fr->af.encoding & MPG123_ENC_8) + fr->af.encsize = 1; + else if(fr->af.encoding & MPG123_ENC_16) + fr->af.encsize = 2; + else if(fr->af.encoding & MPG123_ENC_32 || fr->af.encoding == MPG123_ENC_FLOAT_32) + fr->af.encsize = 4; + else if(fr->af.encoding == MPG123_ENC_FLOAT_64) + fr->af.encsize = 8; + else + { + if(NOQUIET) error1("Some unknown encoding??? (%i)", fr->af.encoding); + + fr->err = MPG123_BAD_OUTFORMAT; + return -1; + } + return 1; + } +} + +int attribute_align_arg mpg123_format_none(mpg123_handle *mh) +{ + int r; + if(mh == NULL) return MPG123_ERR; + + r = mpg123_fmt_none(&mh->p); + if(r != MPG123_OK){ mh->err = r; r = MPG123_ERR; } + + return r; +} + +int attribute_align_arg mpg123_fmt_none(mpg123_pars *mp) +{ + if(mp == NULL) return MPG123_BAD_PARS; + + if(PVERB(mp,3)) fprintf(stderr, "Note: Disabling all formats.\n"); + + memset(mp->audio_caps,0,sizeof(mp->audio_caps)); + return MPG123_OK; +} + +int attribute_align_arg mpg123_format_all(mpg123_handle *mh) +{ + int r; + if(mh == NULL) return MPG123_ERR; + + r = mpg123_fmt_all(&mh->p); + if(r != MPG123_OK){ mh->err = r; r = MPG123_ERR; } + + return r; +} + +int attribute_align_arg mpg123_fmt_all(mpg123_pars *mp) +{ + size_t rate, ch, enc; + if(mp == NULL) return MPG123_BAD_PARS; + + if(PVERB(mp,3)) fprintf(stderr, "Note: Enabling all formats.\n"); + + 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]) ? 1 : 0; + + return MPG123_OK; +} + +int attribute_align_arg mpg123_format(mpg123_handle *mh, long rate, int channels, int encodings) +{ + int r; + if(mh == NULL) return MPG123_ERR; + r = mpg123_fmt(&mh->p, rate, channels, encodings); + if(r != MPG123_OK){ mh->err = r; r = MPG123_ERR; } + + return r; +} + +int attribute_align_arg mpg123_fmt(mpg123_pars *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(PVERB(mp,3)) fprintf(stderr, "Note: Want to enable format %li/%i for encodings 0x%x.\n", rate, channels, encodings); + + if(!(channels & MPG123_STEREO)) ch[1] = 0; /* {0,0} */ + else if(!(channels & MPG123_MONO)) ch[0] = 1; /* {1,1} */ + ratei = rate2num(mp, 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; +} + +int attribute_align_arg mpg123_format_support(mpg123_handle *mh, long rate, int encoding) +{ + if(mh == NULL) return 0; + else return mpg123_fmt_support(&mh->p, rate, encoding); +} + +int attribute_align_arg mpg123_fmt_support(mpg123_pars *mp, long rate, int encoding) +{ + int ch = 0; + int ratei, enci; + ratei = rate2num(mp, 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; +} + +/* Call this one to ensure that any valid format will be something different than this. */ +void invalidate_format(struct audioformat *af) +{ + af->encoding = 0; + af->rate = 0; + af->channels = 0; +} + +/* take into account: channels, bytes per sample -- NOT resampling!*/ +off_t samples_to_bytes(mpg123_handle *fr , off_t s) +{ + return s * fr->af.encsize * fr->af.channels; +} + +off_t bytes_to_samples(mpg123_handle *fr , off_t b) +{ + return b / fr->af.encsize / fr->af.channels; +} |