diff --git a/FindMPG123.cmake b/FindMPG123.cmake new file mode 100644 index 0000000000..9b871d439e --- /dev/null +++ b/FindMPG123.cmake @@ -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) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d4bee329ea..90e5be0fde 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/sound/audio_mpg123_decoder.cpp b/src/sound/audio_mpg123_decoder.cpp new file mode 100644 index 0000000000..f0d9a188c1 --- /dev/null +++ b/src/sound/audio_mpg123_decoder.cpp @@ -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(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(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 diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index da9cb0e66f..7b66f9770f 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -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; diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index 26a42bc643..4351fe0fe7 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -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