mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-10 23:11:01 +00:00
added libraries: opus flac libsndfile updated: libvorbis libogg openal - Everything works as expected for now. Bare in mind libsndfile needed the check for whether or not it could find the xiph libraries removed in order for this to work.
645 lines
19 KiB
C
645 lines
19 KiB
C
/*
|
|
** Copyright (C) 2019 - 2021 Arthur Taylor <art@ified.ca>
|
|
** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
**
|
|
** This program is free software ; you can redistribute it and/or modify
|
|
** it under the terms of the GNU Lesser General Public License as published by
|
|
** the Free Software Foundation ; either version 2.1 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 Lesser General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU Lesser General Public License
|
|
** along with this program ; if not, write to the Free Software
|
|
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "sfconfig.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "sndfile.h"
|
|
#include "common.h"
|
|
#include "mpeg.h"
|
|
|
|
#if HAVE_MPEG
|
|
|
|
#include "sfendian.h"
|
|
#include "id3.h"
|
|
|
|
#include <mpg123.h>
|
|
|
|
typedef struct
|
|
{ mpg123_handle *pmh ;
|
|
size_t header_remaining ;
|
|
} MPEG_DEC_PRIVATE ;
|
|
|
|
static int mpeg_dec_close (SF_PRIVATE *psf) ;
|
|
static sf_count_t mpeg_dec_seek (SF_PRIVATE *psf, int whence, sf_count_t count) ;
|
|
|
|
static ssize_t mpeg_dec_io_read (void *priv, void *buffer, size_t nbytes) ;
|
|
static off_t mpeg_dec_io_lseek (void *priv, off_t offset, int whence) ;
|
|
|
|
static ssize_t
|
|
mpeg_dec_io_read (void *priv, void *buffer, size_t nbytes)
|
|
{ SF_PRIVATE *psf = (SF_PRIVATE *) priv ;
|
|
MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
|
|
if (pmp3d->header_remaining)
|
|
{ if (pmp3d->header_remaining < nbytes)
|
|
nbytes = pmp3d->header_remaining ;
|
|
psf_binheader_readf (psf, "b", buffer, nbytes) ;
|
|
pmp3d->header_remaining -= nbytes ;
|
|
return nbytes ;
|
|
} ;
|
|
|
|
return psf_fread (buffer, 1, nbytes, psf) ;
|
|
} /* mpeg_dec_io_read */
|
|
|
|
static off_t
|
|
mpeg_dec_io_lseek (void *priv, off_t offset, int whence)
|
|
{ SF_PRIVATE *psf = (SF_PRIVATE *) priv ;
|
|
|
|
return psf_fseek (psf, offset, whence) ;
|
|
} /* mpeg_dec_io_lseek */
|
|
|
|
static int
|
|
mpeg_dec_close (SF_PRIVATE *psf)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
|
|
if (pmp3d)
|
|
{ if (pmp3d->pmh)
|
|
{ mpg123_close (pmp3d->pmh) ;
|
|
mpg123_delete (pmp3d->pmh) ;
|
|
pmp3d->pmh = NULL ;
|
|
}
|
|
free (psf->codec_data) ;
|
|
psf->codec_data = NULL ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* mpeg_dec_close */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_decode (SF_PRIVATE *psf, float *ptr, sf_count_t len)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
size_t done ;
|
|
int error ;
|
|
|
|
error = mpg123_read (pmp3d->pmh, (unsigned char *) ptr, len * sizeof (float), &done) ;
|
|
|
|
if (error == MPG123_OK || error == MPG123_DONE)
|
|
return done / sizeof (float) ;
|
|
|
|
if (error == MPG123_NEW_FORMAT)
|
|
{ psf->error = SFE_MALFORMED_FILE ;
|
|
return -1 ;
|
|
} ;
|
|
|
|
psf->error = SFE_INTERNAL ;
|
|
return -1 ;
|
|
} /* mpeg_dec_decode */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
sf_count_t total, readlen ;
|
|
void (*convert) (const float *, short *, int, int) ;
|
|
const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
|
|
|
|
convert = (psf->add_clipping) ? psf_f2s_clip_array : psf_f2s_array ;
|
|
for (total = 0 ; total < len ; total += readlen)
|
|
{ readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
|
|
if (readlen <= 0)
|
|
break ;
|
|
|
|
convert (ubuf.fbuf, ptr + total, readlen, SF_TRUE) ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /*mpeg_dec_read_s */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
sf_count_t total, readlen ;
|
|
void (*convert) (const float *, int *, int, int) ;
|
|
const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
|
|
|
|
convert = (psf->add_clipping) ? psf_f2i_clip_array : psf_f2i_array ;
|
|
for (total = 0 ; total < len ; total += readlen)
|
|
{ readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
|
|
if (readlen <= 0)
|
|
break ;
|
|
|
|
convert (ubuf.fbuf, ptr + total, readlen, SF_TRUE) ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* mpeg_dec_read_i */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
|
|
{ sf_count_t readlen ;
|
|
|
|
readlen = mpeg_dec_decode (psf, ptr, len) ;
|
|
if (readlen <= 0)
|
|
return 0 ;
|
|
|
|
if (psf->norm_float == SF_FALSE)
|
|
for (int i = 0 ; i < readlen ; i++)
|
|
{ ptr [i] *= (1.0f * 0x8000) ;
|
|
} ;
|
|
|
|
return readlen ;
|
|
} /* mpeg_dec_read_f */
|
|
|
|
static inline void
|
|
f2d_array (const float *src, int count, double *dest, double normfact)
|
|
{ for (int i = 0 ; i < count ; i++)
|
|
{ dest [i] = src [i] * normfact ;
|
|
}
|
|
} /* f2d_array */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
sf_count_t total, readlen ;
|
|
double normfact ;
|
|
const sf_count_t buflen = ARRAY_LEN (ubuf.fbuf) ;
|
|
|
|
normfact = (psf->norm_double == SF_TRUE) ? 1.0 : (1.0 * 0x8000) ;
|
|
|
|
for (total = 0 ; total < len ; total += readlen)
|
|
{ readlen = mpeg_dec_decode (psf, ubuf.fbuf, SF_MIN (buflen, len - total)) ;
|
|
if (readlen <= 0)
|
|
break ;
|
|
|
|
f2d_array (ubuf.fbuf, readlen, ptr + total, normfact) ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* mpeg_dec_read_d */
|
|
|
|
static sf_count_t
|
|
mpeg_dec_seek (SF_PRIVATE *psf, int mode, sf_count_t count)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
off_t ret ;
|
|
|
|
if (mode != SFM_READ || psf->file.mode != SFM_READ)
|
|
{ psf->error = SFE_BAD_SEEK ;
|
|
return PSF_SEEK_ERROR ;
|
|
} ;
|
|
|
|
ret = mpg123_seek (pmp3d->pmh, count, SEEK_SET) ;
|
|
|
|
if (ret < 0)
|
|
return PSF_SEEK_ERROR ;
|
|
|
|
return (sf_count_t) ret ;
|
|
} /* mpeg_dec_seek */
|
|
|
|
static int
|
|
mpeg_dec_fill_sfinfo (SF_PRIVATE* psf, mpg123_handle *mh, SF_INFO *info)
|
|
{ int error ;
|
|
int channels ;
|
|
int encoding ;
|
|
long rate ;
|
|
off_t length ;
|
|
|
|
error = mpg123_getformat (mh, &rate, &channels, &encoding) ;
|
|
if (error != MPG123_OK)
|
|
return error ;
|
|
|
|
info->samplerate = rate ;
|
|
info->channels = channels ;
|
|
|
|
length = mpg123_length (mh) ;
|
|
if (length <= 0 && !psf->is_pipe)
|
|
{ if ((error = mpg123_scan (mh)) != MPG123_OK)
|
|
return error ;
|
|
length = mpg123_length (mh) ;
|
|
}
|
|
|
|
if (length >= 0)
|
|
{ info->frames = length ;
|
|
info->seekable = SF_TRUE ;
|
|
}
|
|
else
|
|
{ info->frames = SF_COUNT_MAX ;
|
|
info->seekable = SF_FALSE ;
|
|
}
|
|
|
|
/* Force 32-bit float samples. */
|
|
if (encoding != MPG123_ENC_FLOAT_32)
|
|
{ error = mpg123_format (mh, rate, channels, MPG123_ENC_FLOAT_32) ;
|
|
} ;
|
|
|
|
return error ;
|
|
} /* mpeg_dec_fill_sfinfo */
|
|
|
|
static void
|
|
mpeg_dec_print_frameinfo (SF_PRIVATE *psf, const struct mpg123_frameinfo *fi)
|
|
{ psf_log_printf (psf, "MPEG-1/2 Audio\n----------------------------------------\n") ;
|
|
psf_log_printf (psf, " MPEG version : %s\n",
|
|
fi->version == MPG123_1_0 ? "MPEG 1.0" :
|
|
fi->version == MPG123_2_0 ? "MPEG 2.0" :
|
|
fi->version == MPG123_2_5 ? "MPEG 2.5" : "???") ;
|
|
psf_log_printf (psf, " layer : %d\n", fi->layer) ;
|
|
psf_log_printf (psf, " rate : %d\n", fi->rate) ;
|
|
psf_log_printf (psf, " mode : %s\n",
|
|
fi->mode == MPG123_M_STEREO ? "stereo" :
|
|
fi->mode == MPG123_M_JOINT ? "joint stereo" :
|
|
fi->mode == MPG123_M_DUAL ? "dual channel" :
|
|
fi->mode == MPG123_M_MONO ? "mono" : "???") ;
|
|
psf_log_printf (psf, " mode ext : %d\n", fi->mode_ext) ;
|
|
psf_log_printf (psf, " framesize : %d\n", fi->framesize) ;
|
|
psf_log_printf (psf, " crc : %d\n", !! (fi->flags & MPG123_CRC)) ;
|
|
psf_log_printf (psf, " copyright flag : %d\n", !! (fi->flags & MPG123_COPYRIGHT)) ;
|
|
psf_log_printf (psf, " private flag : %d\n", !! (fi->flags & MPG123_PRIVATE)) ;
|
|
psf_log_printf (psf, " original flag : %d\n", !! (fi->flags & MPG123_ORIGINAL)) ;
|
|
psf_log_printf (psf, " emphasis : %d\n", fi->emphasis) ;
|
|
psf_log_printf (psf, " bitrate mode : ") ;
|
|
switch (fi->vbr)
|
|
{ case MPG123_CBR :
|
|
psf_log_printf (psf, "constant\n") ;
|
|
psf_log_printf (psf, " bitrate : %d kbps\n", fi->bitrate) ;
|
|
break ;
|
|
case MPG123_VBR :
|
|
psf_log_printf (psf, "variable\n") ;
|
|
break ;
|
|
|
|
case MPG123_ABR :
|
|
psf_log_printf (psf, "average\n") ;
|
|
psf_log_printf (psf, " ABR target : %d\n", fi->abr_rate) ;
|
|
break ;
|
|
|
|
default :
|
|
psf_log_printf (psf, "(%d) ???\n", fi->vbr) ;
|
|
break ;
|
|
} ;
|
|
} /* mpeg_dec_print_frameinfo */
|
|
|
|
/*
|
|
* Like strlcpy, except the size argument is the maximum size of the input,
|
|
* always null terminates the output string. Thus, up to size + 1 bytes may be
|
|
* written.
|
|
*
|
|
* Returns the length of the copied string.
|
|
*/
|
|
static int
|
|
strcpy_inbounded (char *dest, size_t size, const char *src)
|
|
{ char *c = memccpy (dest, src, '\0', size) ;
|
|
if (!c)
|
|
c = dest + size ;
|
|
*c = '\0' ;
|
|
return c - dest ;
|
|
} /* strcpy_inbounded */
|
|
|
|
static void
|
|
mpeg_decoder_read_strings_id3v1 (SF_PRIVATE *psf, mpg123_id3v1 *tags)
|
|
{ const char *genre ;
|
|
char buf [31] ;
|
|
|
|
psf_log_printf (psf, "ID3v1 Tags\n") ;
|
|
|
|
if (strcpy_inbounded (buf, ARRAY_LEN (tags->title), tags->title))
|
|
{ psf_log_printf (psf, " Title : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_TITLE, buf) ;
|
|
} ;
|
|
|
|
if (strcpy_inbounded (buf, ARRAY_LEN (tags->artist), tags->artist))
|
|
{ psf_log_printf (psf, " Artist : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_ARTIST, buf) ;
|
|
} ;
|
|
|
|
if (strcpy_inbounded (buf, ARRAY_LEN (tags->album), tags->album))
|
|
{ psf_log_printf (psf, " Album : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_ALBUM, buf) ;
|
|
} ;
|
|
|
|
if (strcpy_inbounded (buf, ARRAY_LEN (tags->year), tags->year))
|
|
{ psf_log_printf (psf, " Year : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_DATE, buf) ;
|
|
} ;
|
|
|
|
if (strcpy_inbounded (buf, ARRAY_LEN (tags->comment), tags->comment))
|
|
{ psf_log_printf (psf, " Comment : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_COMMENT, buf) ;
|
|
} ;
|
|
|
|
/* ID3v1.1 Tracknumber */
|
|
if (tags->comment [28] == '\0' && tags->comment [29] != '\0')
|
|
{ snprintf (buf, ARRAY_LEN (buf), "%hhu", (unsigned char) tags->comment [29]) ;
|
|
psf_log_printf (psf, " Tracknumber : %s\n", buf) ;
|
|
psf_store_string (psf, SF_STR_TRACKNUMBER, buf) ;
|
|
} ;
|
|
|
|
if ((genre = id3_lookup_v1_genre (tags->genre)) != NULL)
|
|
{ psf_log_printf (psf, " Genre : %s\n", genre) ;
|
|
psf_store_string (psf, SF_STR_GENRE, genre) ;
|
|
} ;
|
|
} /* mpeg_decoder_read_strings_id3v1 */
|
|
|
|
static void
|
|
mpeg_decoder_read_strings_id3v2 (SF_PRIVATE *psf, mpg123_id3v2 *tags)
|
|
{ mpg123_text *text_frame ;
|
|
size_t i ;
|
|
uint32_t marker ;
|
|
const char *title = NULL ;
|
|
const char *copyright = NULL ;
|
|
const char *software = NULL ;
|
|
const char *artist = NULL ;
|
|
const char *comment = NULL ;
|
|
const char *date = NULL ;
|
|
const char *album = NULL ;
|
|
const char *license = NULL ;
|
|
const char *tracknumber = NULL ;
|
|
const char *genre = NULL ;
|
|
const char *tlen = NULL ;
|
|
|
|
psf_log_printf (psf, "ID3v2 Tags\n") ;
|
|
|
|
// Read the parsed text tags
|
|
for (i = 0 ; i < tags->texts ; i++)
|
|
{ text_frame = &tags->text [i] ;
|
|
psf_log_printf (psf, " %.4s : %s\n", text_frame->id, text_frame->text.p) ;
|
|
|
|
// Thankfully mpg123 translates v2.2 3-byte frames to v2.3 4-byte for us.
|
|
marker = MAKE_MARKER (text_frame->id [0], text_frame->id [1],
|
|
text_frame->id [2], text_frame->id [3]) ;
|
|
|
|
/* Use our own map of frame types to metadata for text frames */
|
|
switch (marker)
|
|
{ case MAKE_MARKER ('T', 'I', 'T', '2') :
|
|
title = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'C', 'O', 'P') :
|
|
copyright = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'E', 'N', 'C') :
|
|
case MAKE_MARKER ('T', 'S', 'S', 'E') :
|
|
software = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'P', 'E', '1') :
|
|
artist = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'A', 'L', 'B') :
|
|
album = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'R', 'C', 'K') :
|
|
tracknumber = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'Y', 'E', 'R') :
|
|
case MAKE_MARKER ('T', 'D', 'R', 'C') :
|
|
/* TODO (maybe)
|
|
case MAKE_MARKER ('T', 'D', 'A', 'T') :
|
|
case MAKE_MARKER ('T', 'I', 'M', 'E') :
|
|
case MAKE_MARKER ('T', 'D', 'R', 'A') :
|
|
*/
|
|
date = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'O', 'W', 'N') :
|
|
tracknumber = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'C', 'O', 'N') :
|
|
genre = text_frame->text.p ;
|
|
break ;
|
|
|
|
case MAKE_MARKER ('T', 'L', 'E', 'N') :
|
|
tlen = text_frame->text.p ;
|
|
break ;
|
|
} ;
|
|
} ;
|
|
|
|
/* Use mpg123's handling of comment headers, but print all the comment headers anyways. */
|
|
if (tags->comment)
|
|
comment = tags->comment->p ;
|
|
for (i = 0 ; i < tags->comments ; i++)
|
|
{ text_frame = &tags->comment_list [i] ;
|
|
psf_log_printf (psf, " %.4s : (%s)[%s] %s\n", text_frame->id,
|
|
text_frame->description. p, text_frame->lang, text_frame->text.p) ;
|
|
} ;
|
|
|
|
/* Print extra headers */
|
|
for (i = 0 ; i < tags->extras ; i++)
|
|
{ text_frame = &tags->extra [i] ;
|
|
psf_log_printf (psf, " %.4s : (%s) %s\n", text_frame->id,
|
|
text_frame->description.p, text_frame->text.p) ;
|
|
} ;
|
|
|
|
if (title != NULL)
|
|
psf_store_string (psf, SF_STR_TITLE, title) ;
|
|
if (copyright != NULL)
|
|
psf_store_string (psf, SF_STR_COPYRIGHT, copyright) ;
|
|
if (software != NULL)
|
|
psf_store_string (psf, SF_STR_SOFTWARE, software) ;
|
|
if (artist != NULL)
|
|
psf_store_string (psf, SF_STR_ARTIST, artist) ;
|
|
if (comment != NULL)
|
|
psf_store_string (psf, SF_STR_COMMENT, comment) ;
|
|
if (date != NULL)
|
|
psf_store_string (psf, SF_STR_DATE, date) ;
|
|
if (album != NULL)
|
|
psf_store_string (psf, SF_STR_ALBUM, album) ;
|
|
if (license != NULL)
|
|
psf_store_string (psf, SF_STR_LICENSE, license) ;
|
|
if (tracknumber != NULL)
|
|
psf_store_string (psf, SF_STR_TRACKNUMBER, tracknumber) ;
|
|
if (genre != NULL)
|
|
psf_store_string (psf, SF_STR_GENRE, id3_process_v2_genre (genre)) ;
|
|
if (tlen != NULL)
|
|
{ /* If non-seekable, set framecount? Can we trust it? */
|
|
} ;
|
|
} /* mpeg_decoder_read_strings_id3v2 */
|
|
|
|
static void
|
|
mpeg_decoder_read_strings (SF_PRIVATE *psf)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
mpg123_id3v1 *v1_tags ;
|
|
mpg123_id3v2 *v2_tags ;
|
|
|
|
if (mpg123_id3 (pmp3d->pmh, &v1_tags, &v2_tags) != MPG123_OK)
|
|
return ;
|
|
|
|
if (v1_tags != NULL)
|
|
mpeg_decoder_read_strings_id3v1 (psf, v1_tags) ;
|
|
|
|
if (v2_tags != NULL)
|
|
mpeg_decoder_read_strings_id3v2 (psf, v2_tags) ;
|
|
} /* mpeg_decoder_read_strings */
|
|
|
|
static int
|
|
mpeg_dec_byterate (SF_PRIVATE *psf)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
struct mpg123_frameinfo fi ;
|
|
|
|
if (mpg123_info (pmp3d->pmh, &fi) == MPG123_OK)
|
|
return (fi.bitrate + 7) / 8 ;
|
|
|
|
return -1 ;
|
|
|
|
} /* mpeg_dec_byterate */
|
|
|
|
/*==============================================================================
|
|
** exported functions
|
|
*/
|
|
|
|
int
|
|
mpeg_decoder_init (SF_PRIVATE *psf)
|
|
{ MPEG_DEC_PRIVATE *pmp3d ;
|
|
struct mpg123_frameinfo fi ;
|
|
int error ;
|
|
|
|
if (! (psf->file.mode & SFM_READ))
|
|
return SFE_INTERNAL ;
|
|
|
|
/*
|
|
** *** FIXME - Threading issues ***
|
|
**
|
|
** mpg123_init() is a global call that should only be called once, and
|
|
** should be paried with mpg123_exit() when done. libsndfile does not
|
|
** provide for these requirements.
|
|
**
|
|
** Currently this is a moot issue as mpg123_init() non-conditionally writes
|
|
** static areas with calculated data, and mpg123_exit() is a NOP, but this
|
|
** could change in a future version of it!
|
|
**
|
|
** From mpg123.h:
|
|
** > This should be called once in a non-parallel context. It is not explicitly
|
|
** > thread-safe, but repeated/concurrent calls still _should_ be safe as static
|
|
** > tables are filled with the same values anyway.
|
|
**
|
|
** Note that calling mpg123_init() after it has already completed is a NOP.
|
|
**
|
|
** Update 2021-07-04
|
|
** mpg123 upstream has confirmed that mpg132_init() will become a NOP in future,
|
|
** so this is moot.
|
|
*/
|
|
if (mpg123_init () != MPG123_OK)
|
|
return SFE_INTERNAL ;
|
|
|
|
psf->codec_data = pmp3d = calloc (1, sizeof (MPEG_DEC_PRIVATE)) ;
|
|
if (!psf->codec_data)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
pmp3d->pmh = mpg123_new (NULL, &error) ;
|
|
if (!pmp3d->pmh)
|
|
{ psf_log_printf (psf, "Could not obtain a mpg123 handle: %s\n", mpg123_plain_strerror (error)) ;
|
|
return SFE_INTERNAL ;
|
|
} ;
|
|
|
|
psf->codec_close = mpeg_dec_close ;
|
|
|
|
mpg123_replace_reader_handle (pmp3d->pmh,
|
|
mpeg_dec_io_read, mpeg_dec_io_lseek, NULL) ;
|
|
|
|
mpg123_param (pmp3d->pmh, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 1.0) ;
|
|
mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT | MPG123_GAPLESS, 1.0) ;
|
|
#if MPG123_API_VERSION >= 45
|
|
mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_NO_FRANKENSTEIN, 1.0) ;
|
|
#endif
|
|
|
|
/*
|
|
** Need to pass the first MPEG frame to libmpg123, but that frame was read
|
|
** into psf->binheader in order that we could identify the stream.
|
|
*/
|
|
if (psf->is_pipe)
|
|
{ /*
|
|
** Can't seek, so setup our libmpg123 io callbacks to read the binheader
|
|
** buffer first.
|
|
*/
|
|
psf_binheader_readf (psf, "p", psf->dataoffset) ;
|
|
pmp3d->header_remaining = psf_binheader_readf (psf, NULL) - psf->dataoffset ;
|
|
|
|
/* Tell libmpg123 we can't seek the file. */
|
|
mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_NO_PEEK_END, 1.0) ;
|
|
}
|
|
else
|
|
{ /*
|
|
** libmpg123 can parse the ID3v2 header. Undo the embedded file offset if the
|
|
** enclosing file data is the ID3v2 header.
|
|
*/
|
|
if (psf->id3_header.len > 0 && psf->id3_header.len + psf->id3_header.offset == psf->fileoffset)
|
|
psf->fileoffset = psf->id3_header.offset ;
|
|
|
|
psf_fseek (psf, 0, SEEK_SET) ;
|
|
} ;
|
|
|
|
error = mpg123_open_handle (pmp3d->pmh, psf) ;
|
|
if (error != MPG123_OK)
|
|
{ psf_log_printf (psf, "mpg123 could not open the file: %s\n", mpg123_plain_strerror (error)) ;
|
|
return SFE_BAD_FILE ;
|
|
} ;
|
|
|
|
if (mpeg_dec_fill_sfinfo (psf, pmp3d->pmh, &psf->sf) != MPG123_OK)
|
|
{ psf_log_printf (psf, "Cannot get MPEG decoder configuration: %s\n", mpg123_plain_strerror (error)) ;
|
|
return SFE_BAD_FILE ;
|
|
} ;
|
|
|
|
error = mpg123_info (pmp3d->pmh, &fi) ;
|
|
if (error != MPG123_OK)
|
|
{ psf_log_printf (psf, "Cannot get MPEG frame info: %s\n", mpg123_plain_strerror (error)) ;
|
|
return SFE_INTERNAL ;
|
|
}
|
|
|
|
switch (fi.layer)
|
|
{ case 1 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_I ; break ;
|
|
case 2 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_II ; break ;
|
|
case 3 : psf->sf.format |= SF_FORMAT_MPEG_LAYER_III ; break ;
|
|
default :
|
|
return SFE_BAD_FILE ;
|
|
} ;
|
|
mpeg_dec_print_frameinfo (psf, &fi) ;
|
|
|
|
psf->read_short = mpeg_dec_read_s ;
|
|
psf->read_int = mpeg_dec_read_i ;
|
|
psf->read_float = mpeg_dec_read_f ;
|
|
psf->read_double = mpeg_dec_read_d ;
|
|
psf->seek = mpeg_dec_seek ;
|
|
psf->byterate = mpeg_dec_byterate ;
|
|
|
|
mpeg_decoder_read_strings (psf) ;
|
|
|
|
return 0 ;
|
|
} /* mpeg_decoder_init */
|
|
|
|
int
|
|
mpeg_decoder_get_bitrate_mode (SF_PRIVATE *psf)
|
|
{ MPEG_DEC_PRIVATE *pmp3d = (MPEG_DEC_PRIVATE *) psf->codec_data ;
|
|
struct mpg123_frameinfo fi ;
|
|
|
|
if (mpg123_info (pmp3d->pmh, &fi) == MPG123_OK)
|
|
{
|
|
switch (fi.vbr)
|
|
{ case MPG123_CBR : return SF_BITRATE_MODE_CONSTANT ;
|
|
case MPG123_ABR : return SF_BITRATE_MODE_AVERAGE ;
|
|
case MPG123_VBR : return SF_BITRATE_MODE_VARIABLE ;
|
|
default : break ;
|
|
} ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "Cannot determine MPEG bitrate mode.\n") ;
|
|
return -1 ;
|
|
} /* mpeg_decoder_get_bitrate_mode */
|
|
|
|
#else /* HAVE_MPEG */
|
|
|
|
int mpeg_decoder_init (SF_PRIVATE *psf)
|
|
{ psf_log_printf (psf, "This version of libsndfile was compiled without MPEG decode support.\n") ;
|
|
return SFE_UNIMPLEMENTED ;
|
|
} /* mpeg_decoder_init */
|
|
|
|
#endif /* HAVE_MPEG */
|