Implement and use a libsndfile decoder

This commit is contained in:
Chris Robinson 2014-06-19 04:13:42 -07:00
parent 5370a1cb46
commit b38589e2dc
7 changed files with 514 additions and 42 deletions

29
FindSndFile.cmake Normal file
View file

@ -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)

View file

@ -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

View file

@ -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<SndFileDecoder*>(user_data);
return self->FileLength;
}
sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data)
{
SndFileDecoder *self = reinterpret_cast<SndFileDecoder*>(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<SndFileDecoder*>(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<SndFileDecoder*>(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<SndFileDecoder*>(user_data);
return self->MemLength;
}
sf_count_t SndFileDecoder::mem_seek(sf_count_t offset, int whence, void *user_data)
{
SndFileDecoder *self = reinterpret_cast<SndFileDecoder*>(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<SndFileDecoder*>(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<SndFileDecoder*>(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<char> SndFileDecoder::readAll()
{
if(SndInfo.frames <= 0)
return SoundDecoder::readAll();
int framesize = 2 * SndInfo.channels;
std::vector<char> 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

View file

@ -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;
}

View file

@ -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;

View file

@ -2,8 +2,10 @@
#define __SNDINT_H
#include <vector>
#include <stdio.h>
#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<char> 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

View file

@ -212,6 +212,28 @@ class OpenALCallbackStream : public SoundStream
ALuint Source;
bool Playing;
bool Looping;
ALfloat Volume;
std::vector<BYTE> DecoderData;
std::auto_ptr<SoundDecoder> Decoder;
static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user)
{
OpenALCallbackStream *self = static_cast<OpenALCallbackStream*>(_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<SoundDecoder> 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<char> 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<SoundDecoder> 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_t>(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<OpenALSoundStream> stream(new OpenALSoundStream(this));
std::auto_ptr<OpenALCallbackStream> 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;