Add an mp3 decoder using libmpg123

Does not currently handle direct file sources
This commit is contained in:
Chris Robinson 2014-06-19 06:34:40 -07:00
parent 14618cbf30
commit 77b1febd0e
5 changed files with 296 additions and 6 deletions

28
FindMPG123.cmake Normal file
View file

@ -0,0 +1,28 @@
# - Find mpg123
# Find the native mpg123 includes and library
#
# MPG123_INCLUDE_DIR - where to find mpg123.h
# MPG123_LIBRARIES - List of libraries when using mpg123.
# MPG123_FOUND - True if mpg123 found.
IF(MPG123_INCLUDE_DIR AND MPG123_LIBRARIES)
# Already in cache, be silent
SET(MPG123_FIND_QUIETLY TRUE)
ENDIF(MPG123_INCLUDE_DIR AND MPG123_LIBRARIES)
FIND_PATH(MPG123_INCLUDE_DIR mpg123.h
PATHS "${MPG123_DIR}"
PATH_SUFFIXES include
)
FIND_LIBRARY(MPG123_LIBRARIES NAMES mpg123 mpg123-0
PATHS "${MPG123_DIR}"
PATH_SUFFIXES lib
)
MARK_AS_ADVANCED(MPG123_LIBRARIES MPG123_INCLUDE_DIR)
# handle the QUIETLY and REQUIRED arguments and set MPG123_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MPG123 DEFAULT_MSG MPG123_LIBRARIES MPG123_INCLUDE_DIR)

View file

@ -299,6 +299,10 @@ endif( NO_OPENAL )
find_package( SndFile )
# Search for libmpg123
find_package( MPG123 )
# Search for FluidSynth
find_package( FluidSynth )
@ -553,7 +557,10 @@ if( SNDFILE_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SNDFILE_LIBRARIES}" )
include_directories( "${SNDFILE_INCLUDE_DIRS}" )
endif( SNDFILE_FOUND )
if( MPG123_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${MPG123_LIBRARIES}" )
include_directories( "${MPG123_INCLUDE_DIR}" )
endif( MPG123_FOUND )
if( NOT DYN_FLUIDSYNTH)
if( FLUIDSYNTH_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FLUIDSYNTH_LIBRARIES}" )
@ -671,6 +678,9 @@ endif( SSE_MATTERS )
if( SNDFILE_FOUND )
add_definitions( -DHAVE_SNDFILE )
endif( SNDFILE_FOUND )
if( MPG123_FOUND )
add_definitions( -DHAVE_MPG123 )
endif( MPG123_FOUND )
if( DYN_FLUIDSYNTH )
add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH )
elseif( FLUIDSYNTH_FOUND )
@ -1034,6 +1044,7 @@ add_executable( zdoom WIN32
resourcefiles/file_directory.cpp
resourcefiles/resourcefile.cpp
sfmt/SFMT.cpp
sound/audio_mpg123_decoder.cpp
sound/audio_sndfile_decoder.cpp
sound/fmodsound.cpp
sound/i_music.cpp

View file

@ -0,0 +1,167 @@
#include "i_soundinternal.h"
#ifdef HAVE_MPG123
static bool inited = false;
off_t MPG123Decoder::mem_lseek(void *handle, off_t offset, int whence)
{
MPG123Decoder *self = reinterpret_cast<MPG123Decoder*>(handle);
switch(whence)
{
case SEEK_SET:
if(offset < 0 || offset > (off_t)self->MemLength)
return -1;
self->MemPos = offset;
break;
case SEEK_CUR:
if((offset > 0 && (off_t)(self->MemLength-self->MemPos) < offset) ||
(offset < 0 && (off_t)self->MemPos < -offset))
return -1;
self->MemPos += offset;
break;
case SEEK_END:
if(offset > 0 || -offset > (off_t)self->MemLength)
return -1;
self->MemPos = self->MemLength + offset;
break;
default:
return -1;
}
return self->MemPos;
}
ssize_t MPG123Decoder::mem_read(void *handle, void *buffer, size_t bytes)
{
MPG123Decoder *self = reinterpret_cast<MPG123Decoder*>(handle);
if(bytes > self->MemLength-self->MemPos)
bytes = self->MemLength-self->MemPos;
memcpy(buffer, self->MemData+self->MemPos, bytes);
self->MemPos += bytes;
return bytes;
}
MPG123Decoder::~MPG123Decoder()
{
if(MPG123)
{
mpg123_close(MPG123);
mpg123_delete(MPG123);
MPG123 = 0;
}
}
bool MPG123Decoder::open(const char *data, size_t length)
{
if(!inited)
{
if(mpg123_init() != MPG123_OK)
return false;
inited = true;
}
MemData = data;
MemPos = 0;
MemLength = length;
MPG123 = mpg123_new(NULL, NULL);
if(mpg123_replace_reader_handle(MPG123, mem_read, mem_lseek, NULL) == MPG123_OK &&
mpg123_open_handle(MPG123, this) == MPG123_OK)
{
int enc, channels;
long srate;
if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK)
{
if((channels == 1 || channels == 2) && srate > 0 &&
mpg123_format_none(MPG123) == MPG123_OK &&
mpg123_format(MPG123, srate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK)
{
// All OK
Done = false;
return true;
}
}
mpg123_close(MPG123);
}
mpg123_delete(MPG123);
MPG123 = 0;
return false;
}
void MPG123Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{
int enc = 0, channels = 0;
long srate = 0;
mpg123_getformat(MPG123, &srate, &channels, &enc);
*samplerate = srate;
if(channels == 2)
*chans = ChannelConfig_Stereo;
else
*chans = ChannelConfig_Mono;
*type = SampleType_Int16;
}
size_t MPG123Decoder::read(char *buffer, size_t bytes)
{
size_t amt = 0;
while(!Done && bytes > 0)
{
size_t got = 0;
int ret = mpg123_read(MPG123, (unsigned char*)buffer, bytes, &got);
bytes -= got;
buffer += got;
amt += got;
if(ret == MPG123_NEW_FORMAT || ret == MPG123_DONE || got == 0)
{
Done = true;
break;
}
}
return amt;
}
bool MPG123Decoder::open(const char *fname, size_t offset, size_t length)
{
return false;
}
bool MPG123Decoder::seek(size_t ms_offset)
{
int enc, channels;
long srate;
if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK)
{
size_t smp_offset = (size_t)((double)ms_offset / 1000. * srate);
if(mpg123_seek(MPG123, smp_offset, SEEK_SET) >= 0)
{
Done = false;
return true;
}
}
return false;
}
size_t MPG123Decoder::getSampleOffset()
{
return mpg123_tell(MPG123);
}
#endif

View file

@ -524,12 +524,64 @@ SoundHandle SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length)
SoundDecoder *SoundRenderer::CreateDecoder(const BYTE *sfxdata, int length)
{
SoundDecoder *decoder = NULL;
#ifdef HAVE_SNDFILE
decoder = new SndFileDecoder;
if(!decoder->open((const char*)sfxdata, length))
#ifdef HAVE_MPG123
int mpg_start = -1;
int mpg_len = -1;
// Check for an ID3 tag to identify an mp3 (and skip the tag)
if(length > 10 && memcmp(sfxdata, "ID3", 3) == 0 &&
sfxdata[3] <= 4 && sfxdata[4] != 0xff &&
(sfxdata[5]&0x0f) == 0 && (sfxdata[6]&0x80) == 0 &&
(sfxdata[7]&0x80) == 0 && (sfxdata[8]&0x80) == 0 &&
(sfxdata[9]&0x80) == 0)
{
delete decoder;
decoder = NULL;
// ID3v2
mpg_start = (sfxdata[6]<<21) | (sfxdata[7]<<14) |
(sfxdata[8]<< 7) | (sfxdata[9] );
mpg_start += ((sfxdata[5]&0x10) ? 20 : 10);
mpg_len = length - mpg_start;
}
else if(length > 128 && memcmp(sfxdata+length-128, "TAG", 3) == 0)
{
// ID3v1
mpg_start = 0;
mpg_len = length - 128;
}
else if(length > 3)
{
// No ID3 tag. Check for a frame header
if((sfxdata[0] == 0xff && sfxdata[1]>>1 == 0x7d) || // MPEG-1
(sfxdata[0] == 0xff && sfxdata[1]>>1 == 0x79)) // MPEG-2
{
int brate_idx = (sfxdata[2]>>4) & 0x0f;
int srate_idx = (sfxdata[2]>>2) & 0x03;
if(brate_idx != 0 && brate_idx != 15 && srate_idx != 3)
{
mpg_start = 0;
mpg_len = length;
}
}
}
if(mpg_start >= 0 && mpg_len > 0 && mpg_start < length && mpg_len <= length-mpg_start)
{
decoder = new MPG123Decoder;
if(!decoder->open((const char*)sfxdata+mpg_start, mpg_len))
{
delete decoder;
decoder = NULL;
}
}
#endif
#ifdef HAVE_SNDFILE
if(!decoder)
{
decoder = new SndFileDecoder;
if(!decoder->open((const char*)sfxdata, length))
{
delete decoder;
decoder = NULL;
}
}
#endif
return decoder;

View file

@ -139,6 +139,38 @@ private:
SoundDecoder& operator=(const SoundDecoder &rhs);
};
#ifdef HAVE_MPG123
#include "mpg123.h"
struct MPG123Decoder : public SoundDecoder
{
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes);
virtual bool seek(size_t ms_offset);
virtual size_t getSampleOffset();
MPG123Decoder() : MPG123(0) { }
virtual ~MPG123Decoder();
protected:
virtual bool open(const char *data, size_t length);
virtual bool open(const char *fname, size_t offset, size_t length);
private:
mpg123_handle *MPG123;
bool Done;
const char *MemData;
size_t MemLength;
size_t MemPos;
static off_t mem_lseek(void *handle, off_t offset, int whence);
static ssize_t mem_read(void *handle, void *buffer, size_t bytes);
// Make non-copyable
MPG123Decoder(const MPG123Decoder &rhs);
MPG123Decoder& operator=(const MPG123Decoder &rhs);
};
#endif
#ifdef HAVE_SNDFILE
#include "sndfile.h"
struct SndFileDecoder : public SoundDecoder