diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 6d8129aede..af23b34c4f 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -549,6 +549,14 @@ SoundDecoder *SoundRenderer::CreateDecoder(const BYTE *sfxdata, int length) SoundDecoder* SoundRenderer::CreateDecoder(const char *fname, int offset, int length) { SoundDecoder *decoder = NULL; +#ifdef HAVE_MPG123 + decoder = new MPG123Decoder; + if(!decoder->open(fname, offset, length)) + { + delete decoder; + decoder = NULL; + } +#endif #ifdef HAVE_SNDFILE decoder = new SndFileDecoder; if(!decoder->open(fname, offset, length)) diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index 2b3544521a..dd9fb35f8a 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -149,7 +149,7 @@ struct MPG123Decoder : public SoundDecoder virtual bool seek(size_t ms_offset); virtual size_t getSampleOffset(); - MPG123Decoder() : MPG123(0) { } + MPG123Decoder() : MPG123(0), File(0) { } virtual ~MPG123Decoder(); protected: @@ -160,6 +160,12 @@ private: mpg123_handle *MPG123; bool Done; + FILE *File; + size_t FileLength; + size_t FileOffset; + static off_t file_lseek(void *handle, off_t offset, int whence); + static ssize_t file_read(void *handle, void *buffer, size_t bytes); + const char *MemData; size_t MemLength; size_t MemPos; diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp index 4cc1a813bc..bacb7df46a 100644 --- a/src/sound/mpg123_decoder.cpp +++ b/src/sound/mpg123_decoder.cpp @@ -4,6 +4,59 @@ static bool inited = false; +off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) +{ + MPG123Decoder *self = reinterpret_cast(handle); + + long cur = ftell(self->File); + if(cur < 0) return -1; + + switch(whence) + { + case SEEK_SET: + if(offset < 0 || offset > (off_t)self->FileLength) + return -1; + cur = offset; + break; + + case SEEK_CUR: + cur -= self->FileOffset; + if((offset > 0 && (off_t)(self->FileLength-cur) < offset) || + (offset < 0 && (off_t)cur < -offset)) + return -1; + cur += offset; + break; + + case SEEK_END: + if(offset > 0 || -offset > (off_t)self->FileLength) + return -1; + cur = self->FileLength + offset; + break; + + default: + return -1; + } + + if(fseek(self->File, cur + self->FileOffset, SEEK_SET) != 0) + return -1; + return cur; +} + +ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes) +{ + MPG123Decoder *self = reinterpret_cast(handle); + + long cur = ftell(self->File); + if(cur < 0) return -1; + + cur -= self->FileOffset; + if(bytes > (size_t)(self->FileLength-cur)) + bytes = self->FileLength-cur; + + return fread(buffer, 1, bytes, self->File); +} + + off_t MPG123Decoder::mem_lseek(void *handle, off_t offset, int whence) { MPG123Decoder *self = reinterpret_cast(handle); @@ -58,6 +111,9 @@ MPG123Decoder::~MPG123Decoder() mpg123_delete(MPG123); MPG123 = 0; } + if(File) + fclose(File); + File = 0; } bool MPG123Decoder::open(const char *data, size_t length) @@ -135,6 +191,92 @@ bool MPG123Decoder::open(const char *data, size_t length) return false; } +bool MPG123Decoder::open(const char *fname, size_t offset, size_t length) +{ + if(!inited) + { + if(mpg123_init() != MPG123_OK) + return false; + inited = true; + } + + FileLength = length; + FileOffset = offset; + File = fopen(fname, "rb"); + if(!File || fseek(File, FileOffset, SEEK_SET) != 0) + return false; + + char data[10]; + if(file_read(this, data, 10) != 10) + return false; + + // Check for ID3 tags and skip them + if(memcmp(data, "ID3", 3) == 0 && + (BYTE)data[3] <= 4 && (BYTE)data[4] != 0xff && + (data[5]&0x0f) == 0 && (data[6]&0x80) == 0 && + (data[7]&0x80) == 0 && (data[8]&0x80) == 0 && + (data[9]&0x80) == 0) + { + // ID3v2 + int start_offset; + start_offset = (data[6]<<21) | (data[7]<<14) | + (data[8]<< 7) | (data[9] ); + start_offset += ((data[5]&0x10) ? 20 : 10); + offset += start_offset; + length -= start_offset; + } + + if(file_lseek(this, -128, SEEK_END) > 0 && memcmp(data, "TAG", 3) == 0) // ID3v1 + length -= 128; + + FileLength = length; + FileOffset = offset; + if(file_lseek(this, 0, SEEK_SET) != 0) + return false; + + // Check for a frame header + bool frame_ok = false; + if(file_read(this, data, 3) != 3) + { + if((BYTE)data[0] == 0xff && + ((data[1]&0xfe) == 0xfa/*MPEG-1*/ || (data[1]&0xfe) == 0xf2/*MPEG-2*/)) + { + int brate_idx = (data[2]>>4) & 0x0f; + int srate_idx = (data[2]>>2) & 0x03; + if(brate_idx != 0 && brate_idx != 15 && srate_idx != 3) + frame_ok = (file_lseek(this, 0, SEEK_SET) == 0); + } + } + + if(frame_ok) + { + MPG123 = mpg123_new(NULL, NULL); + if(mpg123_replace_reader_handle(MPG123, file_read, file_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; @@ -173,11 +315,6 @@ size_t MPG123Decoder::read(char *buffer, size_t bytes) 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;