From b38589e2dc404fc22897fab5d87ebdccee96a67d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jun 2014 04:13:42 -0700 Subject: [PATCH] Implement and use a libsndfile decoder --- FindSndFile.cmake | 29 ++++ src/CMakeLists.txt | 13 ++ src/sound/audio_sndfile_decoder.cpp | 243 ++++++++++++++++++++++++++++ src/sound/i_sound.cpp | 27 +++- src/sound/i_sound.h | 3 +- src/sound/i_soundinternal.h | 63 +++++++- src/sound/oalsound.cpp | 178 ++++++++++++++++---- 7 files changed, 514 insertions(+), 42 deletions(-) create mode 100644 FindSndFile.cmake create mode 100644 src/sound/audio_sndfile_decoder.cpp diff --git a/FindSndFile.cmake b/FindSndFile.cmake new file mode 100644 index 000000000..ab66fc5c2 --- /dev/null +++ b/FindSndFile.cmake @@ -0,0 +1,29 @@ +# - Try to find SndFile +# Once done this will define +# +# SNDFILE_FOUND - system has SndFile +# SNDFILE_INCLUDE_DIRS - the SndFile include directory +# SNDFILE_LIBRARIES - Link these to use SndFile +# +# Copyright © 2006 Wengo +# Copyright © 2009 Guillaume Martres +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h) + +find_library(SNDFILE_LIBRARY NAMES sndfile sndfile-1) + +set(SNDFILE_INCLUDE_DIRS ${SNDFILE_INCLUDE_DIR}) +set(SNDFILE_LIBRARIES ${SNDFILE_LIBRARY}) + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set SNDFILE_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SndFile DEFAULT_MSG SNDFILE_LIBRARY SNDFILE_INCLUDE_DIR) + +# show the SNDFILE_INCLUDE_DIRS and SNDFILE_LIBRARIES variables only in the advanced view +mark_as_advanced(SNDFILE_INCLUDE_DIRS SNDFILE_LIBRARIES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a74d865e..3fa9a0ee2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -306,6 +306,10 @@ if( NO_OPENAL ) add_definitions( -DNO_OPENAL=1 ) endif( NO_OPENAL ) +# Search for libSndFile + +find_package( SndFile ) + # Search for FluidSynth find_package( FluidSynth ) @@ -556,6 +560,11 @@ message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" ) set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" ) include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" ) +if( SNDFILE_FOUND ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SNDFILE_LIBRARIES}" ) + include_directories( "${SNDFILE_INCLUDE_DIRS}" ) +endif( SNDFILE_FOUND ) + if( NOT DYN_FLUIDSYNTH) if( FLUIDSYNTH_FOUND ) set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FLUIDSYNTH_LIBRARIES}" ) @@ -670,6 +679,9 @@ else( SSE_MATTERS ) set( X86_SOURCES ) endif( SSE_MATTERS ) +if( SNDFILE_FOUND ) + add_definitions( -DHAVE_SNDFILE ) +endif( SNDFILE_FOUND ) if( DYN_FLUIDSYNTH ) add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH ) elseif( FLUIDSYNTH_FOUND ) @@ -1033,6 +1045,7 @@ add_executable( zdoom WIN32 resourcefiles/file_directory.cpp resourcefiles/resourcefile.cpp sfmt/SFMT.cpp + sound/audio_sndfile_decoder.cpp sound/fmodsound.cpp sound/i_music.cpp sound/i_sound.cpp diff --git a/src/sound/audio_sndfile_decoder.cpp b/src/sound/audio_sndfile_decoder.cpp new file mode 100644 index 000000000..dfb49b08c --- /dev/null +++ b/src/sound/audio_sndfile_decoder.cpp @@ -0,0 +1,243 @@ +#include "i_soundinternal.h" + +#ifdef HAVE_SNDFILE +sf_count_t SndFileDecoder::file_get_filelen(void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + return self->FileLength; +} + +sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + + long cur = ftell(self->File); + if(cur < 0 || (unsigned long)cur < self->FileLength) + return -1; + + switch(whence) + { + case SEEK_SET: + if(offset < 0 || offset > (sf_count_t)self->FileLength) + return -1; + cur = offset; + break; + + case SEEK_CUR: + cur -= self->FileOffset; + if((offset > 0 && (sf_count_t)(self->FileLength-cur) < offset) || + (offset < 0 && (sf_count_t)cur < -offset)) + return -1; + cur += offset; + break; + + case SEEK_END: + if(offset > 0 || -offset > (sf_count_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; +} + +sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + + long cur = ftell(self->File); + if(cur < 0 || (unsigned long)cur < self->FileLength) + return -1; + + cur -= self->FileOffset; + if(count > (sf_count_t)(self->FileLength-cur)) + count = self->FileLength-cur; + + return fread(ptr, 1, count, self->File); +} + +sf_count_t SndFileDecoder::file_write(const void *ptr, sf_count_t count, void *user_data) +{ + return -1; +} + +sf_count_t SndFileDecoder::file_tell(void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + + long cur = ftell(self->File); + if(cur < 0 || (unsigned long)cur < self->FileLength) + return -1; + return cur - self->FileOffset; +} + + +sf_count_t SndFileDecoder::mem_get_filelen(void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + return self->MemLength; +} + +sf_count_t SndFileDecoder::mem_seek(sf_count_t offset, int whence, void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + + switch(whence) + { + case SEEK_SET: + if(offset < 0 || offset > (sf_count_t)self->MemLength) + return -1; + self->MemPos = offset; + break; + + case SEEK_CUR: + if((offset > 0 && (sf_count_t)(self->MemLength-self->MemPos) < offset) || + (offset < 0 && (sf_count_t)self->MemPos < -offset)) + return -1; + self->MemPos += offset; + break; + + case SEEK_END: + if(offset > 0 || -offset > (sf_count_t)self->MemLength) + return -1; + self->MemPos = self->MemLength + offset; + break; + + default: + return -1; + } + + return self->MemPos; +} + +sf_count_t SndFileDecoder::mem_read(void *ptr, sf_count_t count, void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + + if(count > (sf_count_t)(self->MemLength-self->MemPos)) + count = self->MemLength-self->MemPos; + + memcpy(ptr, self->MemData+self->MemPos, count); + self->MemPos += count; + + return count; +} + +sf_count_t SndFileDecoder::mem_write(const void *ptr, sf_count_t count, void *user_data) +{ + return -1; +} + +sf_count_t SndFileDecoder::mem_tell(void *user_data) +{ + SndFileDecoder *self = reinterpret_cast(user_data); + return self->MemPos; +} + + +SndFileDecoder::~SndFileDecoder() +{ + if(SndFile) + sf_close(SndFile); + SndFile = 0; +} + +bool SndFileDecoder::open(const char *data, size_t length) +{ + SF_VIRTUAL_IO sfio = { mem_get_filelen, mem_seek, mem_read, mem_write, mem_tell }; + + MemData = data; + MemPos = 0; + MemLength = length; + SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); + if(SndFile) + { + if(SndInfo.channels != 1 && SndInfo.channels != 2) + { + sf_close(SndFile); + SndFile = 0; + } + } + + return SndFile != 0; +} + +bool SndFileDecoder::open(const char *fname, size_t offset, size_t length) +{ + SF_VIRTUAL_IO sfio = { mem_get_filelen, mem_seek, mem_read, mem_write, mem_tell }; + + FileOffset = offset; + FileLength = length; + File = fopen(fname, "rb"); + if(File) + { + if(fseek(File, offset, SEEK_SET) == 0) + { + SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); + if(SndFile) + { + if(SndInfo.channels == 1 || SndInfo.channels == 2) + return true; + + sf_close(SndFile); + SndFile = 0; + } + } + fclose(File); + } + + return false; +} + +void SndFileDecoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) +{ + *samplerate = SndInfo.samplerate; + + if(SndInfo.channels == 2) + *chans = ChannelConfig_Stereo; + else + *chans = ChannelConfig_Mono; + + *type = SampleType_Int16; +} + +size_t SndFileDecoder::read(char *buffer, size_t bytes) +{ + int framesize = 2 * SndInfo.channels; + return sf_readf_short(SndFile, (short*)buffer, bytes/framesize) * framesize; +} + +std::vector SndFileDecoder::readAll() +{ + if(SndInfo.frames <= 0) + return SoundDecoder::readAll(); + + int framesize = 2 * SndInfo.channels; + std::vector output; + + output.resize(SndInfo.frames * framesize); + size_t got = read(&output[0], output.size()); + output.resize(got); + + return output; +} + +bool SndFileDecoder::seek(size_t ms_offset) +{ + size_t smp_offset = (size_t)((double)ms_offset / 1000. * SndInfo.samplerate); + if(sf_seek(SndFile, smp_offset, SEEK_SET) < 0) + return false; + return true; +} + +size_t SndFileDecoder::getSampleOffset() +{ + return sf_seek(SndFile, 0, SEEK_CUR); +} + +#endif diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index f657182ef..da9cb0e66 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -521,9 +521,32 @@ SoundHandle SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length) } -SoundDecoder *SoundRenderer::CreateDecoder(BYTE *sfxdata, int length) +SoundDecoder *SoundRenderer::CreateDecoder(const BYTE *sfxdata, int length) { - return NULL; + SoundDecoder *decoder = NULL; +#ifdef HAVE_SNDFILE + decoder = new SndFileDecoder; + if(!decoder->open((const char*)sfxdata, length)) + { + delete decoder; + decoder = NULL; + } +#endif + return decoder; +} + +SoundDecoder* SoundRenderer::CreateDecoder(const char *fname, int offset, int length) +{ + SoundDecoder *decoder = NULL; +#ifdef HAVE_SNDFILE + decoder = new SndFileDecoder; + if(!decoder->open(fname, offset, length)) + { + delete decoder; + decoder = NULL; + } +#endif + return decoder; } diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index fa419cf02..b82ea85aa 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -154,7 +154,8 @@ public: virtual void DrawWaveDebug(int mode); protected: - virtual SoundDecoder *CreateDecoder(BYTE *sfxdata, int length); + virtual SoundDecoder *CreateDecoder(const BYTE *sfxdata, int length); + virtual SoundDecoder *CreateDecoder(const char *fname, int offset, int length); }; extern SoundRenderer *GSnd; diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index c0282ee6b..26a42bc64 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -2,8 +2,10 @@ #define __SNDINT_H #include +#include #include "basictypes.h" +#include "vectors.h" // For convenience, this structure matches FMOD_REVERB_PROPERTIES. // Since I can't very well #include system-specific stuff in the @@ -106,22 +108,16 @@ struct FISoundChannel enum SampleType { SampleType_UInt8, - SampleType_Int16, - SampleType_Float32 + SampleType_Int16 }; enum ChannelConfig { ChannelConfig_Mono, - ChannelConfig_Stereo, - ChannelConfig_Quad, - ChannelConfig_5point1, - ChannelConfig_7point1 + ChannelConfig_Stereo }; struct SoundDecoder { - virtual bool open(const char *data, size_t length) = 0; - virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual size_t read(char *buffer, size_t bytes) = 0; @@ -132,10 +128,61 @@ struct SoundDecoder SoundDecoder() { } virtual ~SoundDecoder() { } +protected: + virtual bool open(const char *data, size_t length) = 0; + virtual bool open(const char *fname, size_t offset, size_t length) = 0; + friend class SoundRenderer; + private: // Make non-copyable SoundDecoder(const SoundDecoder &rhs); SoundDecoder& operator=(const SoundDecoder &rhs); }; +#ifdef HAVE_SNDFILE +#include "sndfile.h" +struct SndFileDecoder : public SoundDecoder +{ + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); + + virtual size_t read(char *buffer, size_t bytes); + virtual std::vector readAll(); + virtual bool seek(size_t ms_offset); + virtual size_t getSampleOffset(); + + SndFileDecoder() : SndFile(0) { } + virtual ~SndFileDecoder(); + +protected: + virtual bool open(const char *data, size_t length); + virtual bool open(const char *fname, size_t offset, size_t length); + +private: + SNDFILE *SndFile; + SF_INFO SndInfo; + + FILE *File; + size_t FileLength; + size_t FileOffset; + static sf_count_t file_get_filelen(void *user_data); + static sf_count_t file_seek(sf_count_t offset, int whence, void *user_data); + static sf_count_t file_read(void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_write(const void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_tell(void *user_data); + + const char *MemData; + size_t MemLength; + size_t MemPos; + static sf_count_t mem_get_filelen(void *user_data); + static sf_count_t mem_seek(sf_count_t offset, int whence, void *user_data); + static sf_count_t mem_read(void *ptr, sf_count_t count, void *user_data); + static sf_count_t mem_write(const void *ptr, sf_count_t count, void *user_data); + static sf_count_t mem_tell(void *user_data); + + // Make non-copyable + SndFileDecoder(const SndFileDecoder &rhs); + SndFileDecoder& operator=(const SndFileDecoder &rhs); +}; +#endif + #endif diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 475b5a113..37ad877d3 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -212,6 +212,28 @@ class OpenALCallbackStream : public SoundStream ALuint Source; bool Playing; + bool Looping; + ALfloat Volume; + + + std::vector DecoderData; + std::auto_ptr Decoder; + static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) + { + OpenALCallbackStream *self = static_cast(_sstream); + if(length < 0) return false; + + size_t got = self->Decoder->read((char*)ptr, length); + if(got < (unsigned int)length) + { + if(!self->Looping || !self->Decoder->seek(0)) + return false; + got += self->Decoder->read((char*)ptr+got, length-got); + } + + return (got == (unsigned int)length); + } + bool SetupSource() { @@ -251,10 +273,8 @@ class OpenALCallbackStream : public SoundStream } public: - ALfloat Volume; - OpenALCallbackStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Source(0), Playing(false), Volume(1.0f) + : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f) { Renderer->Streams.push_back(this); memset(Buffers, 0, sizeof(Buffers)); @@ -479,6 +499,89 @@ public: return true; } + + bool Init(const char *fname, int offset, int length, bool loop) + { + if(!SetupSource()) + return false; + + Decoder.reset(Renderer->CreateDecoder(fname, offset, length)); + if(!Decoder.get()) return false; + + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + + ChannelConfig chans; + SampleType type; + int srate; + + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + } + + if(Format == AL_NONE) + { + Printf("Unsupported audio format (0x%x / 0x%x)\n", chans, type); + return false; + } + SampleRate = srate; + Looping = loop; + + Data.resize((size_t)(0.2 * SampleRate) * 4); + + return true; + } + + bool Init(const BYTE *data, int length, bool loop) + { + if(!SetupSource()) + return false; + + DecoderData.insert(DecoderData.end(), data, data+length); + Decoder.reset(Renderer->CreateDecoder(&DecoderData[0], DecoderData.size())); + if(!Decoder.get()) return false; + + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + + ChannelConfig chans; + SampleType type; + int srate; + + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + } + + if(Format == AL_NONE) + { + Printf("Unsupported audio format (0x%x / 0x%x)\n", chans, type); + return false; + } + SampleRate = srate; + Looping = loop; + + Data.resize((size_t)(0.2 * SampleRate) * 4); + + return true; + } }; @@ -955,21 +1058,37 @@ SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int fre SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) { SoundHandle retval = { NULL }; - ALenum format; - ALuint srate; + ALenum format = AL_NONE; + ChannelConfig chans; + SampleType type; + int srate; - Decoder decoder(sfxdata, length); - if(!decoder.GetFormat(&format, &srate)) - return retval; + std::auto_ptr decoder(CreateDecoder(sfxdata, length)); + if(!decoder.get()) return retval; - ALsizei size; - void *data = decoder.GetData(&size); - if(data == NULL) + decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; + } + + if(format == AL_NONE) + { + Printf("Unsupported audio format (0x%x / 0x%x)\n", chans, type); return retval; + } + + std::vector data = decoder->readAll(); ALuint buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, data, size, srate); + alBufferData(buffer, format, &data[0], data.size(), srate); ALenum err; if((err=getALError()) != AL_NO_ERROR) @@ -1012,29 +1131,25 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) delete ((ALuint*)sfx.data); } -short *OpenALSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) +short *OpenALSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype) { - short *samples = (short*)malloc(outlen); - memset(samples, 0, outlen); + char *samples = (char*)calloc(1, outlen); + ChannelConfig chans; + SampleType type; + int srate; - Decoder decoder(coded, sizebytes); - ALenum format; - ALuint srate; + std::auto_ptr decoder(CreateDecoder((const BYTE*)coded, sizebytes)); + if(!decoder.get()) return (short*)samples; - if(!decoder.GetFormat(&format, &srate)) - return samples; - if(format != AL_FORMAT_MONO16) + decoder->getInfo(&srate, &chans, &type); + if(chans != ChannelConfig_Mono || type != SampleType_Int16) { DPrintf("Sample is not 16-bit mono\n"); - return samples; + return (short*)samples; } - ALsizei size; - void *data = decoder.GetData(&size); - if(data != NULL) - memcpy(samples, data, std::min(size, outlen)); - - return samples; + decoder->read(samples, outlen); + return (short*)samples; } SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) @@ -1047,10 +1162,11 @@ SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int SoundStream *OpenALSoundRenderer::OpenStream(const char *filename, int flags, int offset, int length) { - std::auto_ptr stream(new OpenALSoundStream(this)); + std::auto_ptr stream(new OpenALCallbackStream(this)); - bool ok = ((offset == -1) ? stream->Init((const BYTE*)filename, length) : - stream->Init(filename, offset, length)); + bool loop = (flags&SoundStream::Loop); + bool ok = ((offset == -1) ? stream->Init((const BYTE*)filename, length, loop) : + stream->Init(filename, offset, length, loop)); if(ok == false) return NULL;