- moved the sound decoding code to the zmusic project.

Since this gets used by both the sound backend and the music code it needs to be in a place accessible to both.
This commit is contained in:
Christoph Oelckers 2019-09-29 12:48:12 +02:00
commit cdf2a17c5a
35 changed files with 544 additions and 426 deletions

View file

@ -23,10 +23,6 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
endif()
endif()
option( DYN_OPENAL "Dynamically load OpenAL" ON )
option( DYN_SNDFILE "Dynamically load libsndfile" ON )
option( DYN_MPG123 "Dynamically load libmpg123" ON )
if( APPLE )
option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON )
endif()
@ -623,24 +619,6 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
if( DYN_SNDFILE)
add_definitions( -DHAVE_SNDFILE -DDYN_SNDFILE )
elseif( SNDFILE_FOUND )
add_definitions( -DHAVE_SNDFILE )
endif()
if( DYN_MPG123)
add_definitions( -DHAVE_MPG123 -DDYN_MPG123 )
elseif( MPG123_FOUND )
add_definitions( -DHAVE_MPG123 )
endif()
if( DYN_FLUIDSYNTH )
add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH )
elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_FLUIDSYNTH )
endif()
option( SEND_ANON_STATS "Enable sending of anonymous hardware statistics" ON )
if( NOT SEND_ANON_STATS )
@ -832,10 +810,8 @@ set( FASTMATH_SOURCES
rendering/swrenderer/r_all.cpp
rendering/swrenderer/r_swscene.cpp
rendering/polyrenderer/poly_all.cpp
sound/backend/mpg123_decoder.cpp
sound/music/music_midi_base.cpp
sound/backend/oalsound.cpp
sound/backend/sndfile_decoder.cpp
gamedata/textures/hires/hqnx/init.cpp
gamedata/textures/hires/hqnx/hq2x.cpp
gamedata/textures/hires/hqnx/hq3x.cpp

View file

@ -39,8 +39,10 @@
#include "oalsound.h"
#include "mpg123_decoder.h"
#include "sndfile_decoder.h"
#include "i_module.h"
#include "cmdlib.h"
#include "zmusic/mpg123_decoder.h"
#include "zmusic/sndfile_decoder.h"
#include "c_dispatch.h"
#include "i_music.h"
@ -247,6 +249,7 @@ public:
void I_InitSound ()
{
FModule_SetProgDir(progdir);
/* Get command line options: */
nosound = !!Args->CheckParm ("-nosound");
nosfx = !!Args->CheckParm ("-nosfx");
@ -351,16 +354,20 @@ FString SoundRenderer::GatherStats ()
short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype)
{
FileReader reader;
short *samples = (short*)calloc(1, outlen);
ChannelConfig chans;
SampleType type;
int srate;
reader.OpenMemory(coded, sizebytes);
// The decoder will take ownership of the reader if it succeeds so this may not be a local variable.
MusicIO::MemoryReader *reader = new MusicIO::MemoryReader((const uint8_t*)coded, sizebytes);
SoundDecoder *decoder = CreateDecoder(reader);
if(!decoder) return samples;
SoundDecoder *decoder = SoundDecoder::CreateDecoder(reader);
if (!decoder)
{
reader->close();
return samples;
}
decoder->getInfo(&srate, &chans, &type);
if(chans != ChannelConfig_Mono || type != SampleType_Int16)
@ -545,46 +552,3 @@ std::pair<SoundHandle, bool> SoundRenderer::LoadSoundBuffered(FSoundLoadBuffer *
return std::make_pair(retval, true);
}
SoundDecoder *SoundRenderer::CreateDecoder(FileReader &reader)
{
SoundDecoder *decoder = NULL;
auto pos = reader.Tell();
#ifdef HAVE_SNDFILE
decoder = new SndFileDecoder;
if (decoder->open(reader))
return decoder;
reader.Seek(pos, FileReader::SeekSet);
delete decoder;
decoder = NULL;
#endif
#ifdef HAVE_MPG123
decoder = new MPG123Decoder;
if (decoder->open(reader))
return decoder;
reader.Seek(pos, FileReader::SeekSet);
delete decoder;
decoder = NULL;
#endif
return decoder;
}
// Default readAll implementation, for decoders that can't do anything better
TArray<uint8_t> SoundDecoder::readAll()
{
TArray<uint8_t> output;
unsigned total = 0;
unsigned got;
output.Resize(total+32768);
while((got=(unsigned)read((char*)&output[total], output.Size()-total)) > 0)
{
total += got;
output.Resize(total*2);
}
output.Resize(total);
return output;
}

View file

@ -88,7 +88,7 @@ class MIDIDevice;
struct FSoundLoadBuffer
{
TArray<uint8_t> mBuffer;
std::vector<uint8_t> mBuffer;
uint32_t loop_start;
uint32_t loop_end;
ChannelConfig chans;
@ -169,8 +169,6 @@ public:
virtual short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type);
virtual void DrawWaveDebug(int mode);
static SoundDecoder *CreateDecoder(FileReader &reader);
};
extern SoundRenderer *GSnd;

View file

@ -6,6 +6,7 @@
#include "doomtype.h"
#include "vectors.h"
#include "tarray.h"
#include "../../libraries/music_common/fileio.h"
class FileReader;
@ -113,45 +114,12 @@ struct FISoundChannel
};
void FindLoopTags(FileReader &fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass);
void FindLoopTags(MusicIO::FileInterface *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass);
enum SampleType
{
SampleType_UInt8,
SampleType_Int16
};
enum ChannelConfig
{
ChannelConfig_Mono,
ChannelConfig_Stereo
};
const char *GetSampleTypeName(enum SampleType type);
const char *GetChannelConfigName(enum ChannelConfig chan);
struct SoundDecoder
{
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
virtual size_t read(char *buffer, size_t bytes) = 0;
virtual TArray<uint8_t> readAll();
virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) = 0;
virtual size_t getSampleOffset() = 0;
virtual size_t getSampleLength() { return 0; }
SoundDecoder() { }
virtual ~SoundDecoder() { }
protected:
virtual bool open(FileReader &reader) = 0;
friend class SoundRenderer;
// Make non-copyable
SoundDecoder(const SoundDecoder &rhs) = delete;
SoundDecoder& operator=(const SoundDecoder &rhs) = delete;
};
class MusInfo;
struct MusPlayingInfo
{

View file

@ -1,234 +0,0 @@
/*
** mpg123_decoder.cpp
**
**---------------------------------------------------------------------------
** Copyright 2008-2010 Chris Robinson
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "mpg123_decoder.h"
#include "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_MPG123
FModule MPG123Module{"MPG123"};
#include "mpgload.h"
#ifdef _WIN32
#define MPG123LIB "libmpg123-0.dll"
#elif defined(__APPLE__)
#define MPG123LIB "libmpg123.0.dylib"
#else
#define MPG123LIB "libmpg123.so.0"
#endif
bool IsMPG123Present()
{
#if !defined DYN_MPG123
return true;
#else
static bool cached_result = false;
static bool done = false;
if (!done)
{
done = true;
cached_result = MPG123Module.Load({NicePath("$PROGDIR/" MPG123LIB), MPG123LIB});
}
return cached_result;
#endif
}
static bool inited = false;
off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence)
{
auto &reader = reinterpret_cast<MPG123Decoder*>(handle)->Reader;
if(whence == SEEK_CUR)
{
if(offset < 0 && reader.Tell()+offset < 0)
return -1;
}
else if(whence == SEEK_END)
{
if(offset < 0 && reader.GetLength()+offset < 0)
return -1;
}
if(reader.Seek(offset, (FileReader::ESeek)whence) != 0)
return -1;
return (off_t)reader.Tell();
}
ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes)
{
auto &reader = reinterpret_cast<MPG123Decoder*>(handle)->Reader;
return (ssize_t)reader.Read(buffer, (long)bytes);
}
MPG123Decoder::~MPG123Decoder()
{
if(MPG123)
{
mpg123_close(MPG123);
mpg123_delete(MPG123);
MPG123 = 0;
}
}
bool MPG123Decoder::open(FileReader &reader)
{
if(!inited)
{
if (!IsMPG123Present()) return false;
if(mpg123_init() != MPG123_OK) return false;
inited = true;
}
Reader = std::move(reader);
{
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;
}
reader = std::move(Reader); // need to give it back.
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::seek(size_t ms_offset, bool ms, bool mayrestart)
{
int enc, channels;
long srate;
if (!mayrestart || ms_offset > 0)
{
if (mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK)
{
size_t smp_offset = ms ? (size_t)((double)ms_offset / 1000. * srate) : ms_offset;
if (mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0)
{
Done = false;
return true;
}
}
return false;
}
else
{
// Restart the song instead of rewinding. A rewind seems to cause distortion when done repeatedly.
// offset is intentionally ignored here.
if (MPG123)
{
mpg123_close(MPG123);
mpg123_delete(MPG123);
MPG123 = 0;
}
Reader.Seek(0, FileReader::SeekSet);
// Do not call open with our own reader variable, that would be catastrophic.
auto reader = std::move(Reader);
return open(reader);
}
}
size_t MPG123Decoder::getSampleOffset()
{
return mpg123_tell(MPG123);
}
size_t MPG123Decoder::getSampleLength()
{
off_t len = mpg123_length(MPG123);
return (len > 0) ? len : 0;
}
#endif

View file

@ -1,50 +0,0 @@
#ifndef MPG123_DECODER_H
#define MPG123_DECODER_H
#include "i_soundinternal.h"
#include "files.h"
#ifdef HAVE_MPG123
#ifdef _MSC_VER
#include <stddef.h>
typedef ptrdiff_t ssize_t;
#endif
#ifndef DYN_MPG123
#include "mpg123.h"
#else
#include "thirdparty/mpg123.h"
#endif
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, bool ms, bool mayrestart);
virtual size_t getSampleOffset();
virtual size_t getSampleLength();
MPG123Decoder() : MPG123(0) { }
virtual ~MPG123Decoder();
protected:
virtual bool open(FileReader &reader);
private:
mpg123_handle *MPG123;
bool Done;
FileReader Reader;
static off_t file_lseek(void *handle, off_t offset, int whence);
static ssize_t file_read(void *handle, void *buffer, size_t bytes);
// Make non-copyable
MPG123Decoder(const MPG123Decoder &rhs);
MPG123Decoder& operator=(const MPG123Decoder &rhs);
};
#endif
#endif /* MPG123_DECODER_H */

View file

@ -1,40 +0,0 @@
#ifndef MPGDEF_H
#define MPGDEF_H
#if defined HAVE_MPG123 && defined DYN_MPG123
#define DEFINE_ENTRY(type, name) static TReqProc<MPG123Module, type> p_##name{#name};
DEFINE_ENTRY(int (*)(mpg123_handle *mh), mpg123_close)
DEFINE_ENTRY(void (*)(mpg123_handle *mh), mpg123_delete)
DEFINE_ENTRY(int (*)(void), mpg123_init)
DEFINE_ENTRY(mpg123_handle* (*)(const char* decoder, int *error), mpg123_new)
DEFINE_ENTRY(int (*)(mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*)), mpg123_replace_reader_handle)
DEFINE_ENTRY(int (*)(mpg123_handle *mh, void *iohandle), mpg123_open_handle)
DEFINE_ENTRY(int (*)(mpg123_handle *mh, long *rate, int *channels, int *encoding), mpg123_getformat)
DEFINE_ENTRY(int (*)(mpg123_handle *mh), mpg123_format_none)
DEFINE_ENTRY(int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done), mpg123_read)
DEFINE_ENTRY(off_t (*)(mpg123_handle *mh, off_t sampleoff, int whence), mpg123_seek)
DEFINE_ENTRY(int (*)(mpg123_handle *mh, long rate, int channels, int encodings), mpg123_format)
DEFINE_ENTRY(off_t (*)(mpg123_handle *mh), mpg123_tell)
DEFINE_ENTRY(off_t (*)(mpg123_handle *mh), mpg123_length)
#undef DEFINE_ENTRY
#ifndef IN_IDE_PARSER
#define mpg123_close p_mpg123_close
#define mpg123_delete p_mpg123_delete
#define mpg123_init p_mpg123_init
#define mpg123_new p_mpg123_new
#define mpg123_replace_reader_handle p_mpg123_replace_reader_handle
#define mpg123_open_handle p_mpg123_open_handle
#define mpg123_getformat p_mpg123_getformat
#define mpg123_format_none p_mpg123_format_none
#define mpg123_read p_mpg123_read
#define mpg123_seek p_mpg123_seek
#define mpg123_tell p_mpg123_tell
#define mpg123_format p_mpg123_format
#define mpg123_length p_mpg123_length
#endif
#endif
#endif

View file

@ -44,6 +44,8 @@
#include "i_music.h"
#include "cmdlib.h"
#include "menu/menu.h"
#include "zmusic/sounddecoder.h"
#include "filereadermusicinterface.h"
FModule OpenALModule{"OpenAL"};
@ -229,7 +231,6 @@ class OpenALSoundStream : public SoundStream
ALfloat Volume;
FileReader Reader;
SoundDecoder *Decoder;
static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user)
{
@ -612,9 +613,13 @@ public:
}
if(Decoder) delete Decoder;
Reader = std::move(reader);
Decoder = Renderer->CreateDecoder(Reader);
if(!Decoder) return false;
auto mreader = new FileReaderMusicInterface(reader);
Decoder = SoundDecoder::CreateDecoder(mreader);
if (!Decoder)
{
mreader->close();
return false;
}
Callback = DecoderCallback;
UserData = NULL;
@ -1285,7 +1290,6 @@ std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSoundRaw(uint8_t *sfxdata,
std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, bool monoize, FSoundLoadBuffer *pBuffer)
{
SoundHandle retval = { NULL };
FileReader reader;
ALenum format = AL_NONE;
ChannelConfig chans;
SampleType type;
@ -1296,13 +1300,16 @@ std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int
/* Only downmix to mono if we can't spatialize multi-channel sounds. */
monoize = monoize && !AL.SOFT_source_spatialize;
reader.OpenMemory(sfxdata, length);
FindLoopTags(reader, &loop_start, &startass, &loop_end, &endass);
reader.Seek(0, FileReader::SeekSet);
std::unique_ptr<SoundDecoder> decoder(CreateDecoder(reader));
if (!decoder) return std::make_pair(retval, true);
auto mreader = new MusicIO::MemoryReader(sfxdata, length);
FindLoopTags(mreader, &loop_start, &startass, &loop_end, &endass);
mreader->seek(0, SEEK_SET);
std::unique_ptr<SoundDecoder> decoder(SoundDecoder::CreateDecoder(mreader));
if (!decoder)
{
delete mreader;
return std::make_pair(retval, true);
}
// the decode will take ownership of the reader here.
decoder->getInfo(&srate, &chans, &type);
int samplesize = 1;
@ -1324,12 +1331,12 @@ std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int
return std::make_pair(retval, true);
}
TArray<uint8_t> data = decoder->readAll();
auto data = decoder->readAll();
if(chans != ChannelConfig_Mono && monoize)
{
size_t chancount = GetChannelCount(chans);
size_t frames = data.Size() / chancount /
size_t frames = data.size() / chancount /
(type == SampleType_Int16 ? 2 : 1);
if(type == SampleType_Int16)
{
@ -1353,13 +1360,13 @@ std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int
sfxdata[i] = uint8_t((sum / chancount) + 128);
}
}
data.Resize(unsigned(data.Size()/chancount));
data.resize((data.size()/chancount));
}
ALenum err;
ALuint buffer = 0;
alGenBuffers(1, &buffer);
alBufferData(buffer, format, &data[0], data.Size(), srate);
alBufferData(buffer, format, &data[0], (ALsizei)data.size(), srate);
if((err=getALError()) != AL_NO_ERROR)
{
Printf("Failed to buffer data: %s\n", alGetString(err));
@ -1370,7 +1377,7 @@ std::pair<SoundHandle,bool> OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int
if (!startass) loop_start = Scale(loop_start, srate, 1000);
if (!endass && loop_end != ~0u) loop_end = Scale(loop_end, srate, 1000);
const uint32_t samples = data.Size() / samplesize;
const uint32_t samples = (uint32_t)data.size() / samplesize;
if (loop_start > samples) loop_start = 0;
if (loop_end > samples) loop_end = samples;
@ -1425,12 +1432,12 @@ std::pair<SoundHandle, bool> OpenALSoundRenderer::LoadSoundBuffered(FSoundLoadBu
return std::make_pair(retval, true);
}
TArray<uint8_t> &data = pBuffer->mBuffer;
auto &data = pBuffer->mBuffer;
if (pBuffer->chans == ChannelConfig_Stereo && monoize)
{
size_t chancount = GetChannelCount(chans);
size_t frames = data.Size() / chancount /
size_t frames = data.size() / chancount /
(type == SampleType_Int16 ? 2 : 1);
if (type == SampleType_Int16)
{
@ -1454,13 +1461,13 @@ std::pair<SoundHandle, bool> OpenALSoundRenderer::LoadSoundBuffered(FSoundLoadBu
sfxdata[i] = uint8_t((sum / chancount) + 128);
}
}
data.Resize(unsigned(data.Size() / chancount));
data.resize(data.size() / chancount);
}
ALenum err;
ALuint buffer = 0;
alGenBuffers(1, &buffer);
alBufferData(buffer, format, &data[0], data.Size(), srate);
alBufferData(buffer, format, &data[0], (ALsizei)data.size(), srate);
if ((err = getALError()) != AL_NO_ERROR)
{
Printf("Failed to buffer data: %s\n", alGetString(err));

View file

@ -1,205 +0,0 @@
/*
** sndfile_decoder.cpp
**
**---------------------------------------------------------------------------
** Copyright 2008-2010 Chris Robinson
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "sndfile_decoder.h"
#include "templates.h"
#include "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_SNDFILE
FModule SndFileModule{"SndFile"};
#include "sndload.h"
#ifdef _WIN32
#define SNDFILELIB "libsndfile-1.dll"
#elif defined(__APPLE__)
#define SNDFILELIB "libsndfile.1.dylib"
#else
#define SNDFILELIB "libsndfile.so.1"
#endif
bool IsSndFilePresent()
{
#if !defined DYN_SNDFILE
return true;
#else
static bool cached_result = false;
static bool done = false;
if (!done)
{
done = true;
cached_result = SndFileModule.Load({NicePath("$PROGDIR/" SNDFILELIB), SNDFILELIB});
}
return cached_result;
#endif
}
sf_count_t SndFileDecoder::file_get_filelen(void *user_data)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.GetLength();
}
sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
if(reader.Seek((long)offset, (FileReader::ESeek)whence) != 0)
return -1;
return reader.Tell();
}
sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.Read(ptr, (long)count);
}
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)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.Tell();
}
SndFileDecoder::~SndFileDecoder()
{
if(SndFile)
sf_close(SndFile);
SndFile = 0;
}
bool SndFileDecoder::open(FileReader &reader)
{
if (!IsSndFilePresent()) return false;
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
Reader = std::move(reader);
SndInfo.format = 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;
}
reader = std::move(Reader); // need to give it back.
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)
{
short *out = (short*)buffer;
size_t frames = bytes / SndInfo.channels / 2;
size_t total = 0;
// It seems libsndfile has a bug with converting float samples from Vorbis
// to the 16-bit shorts we use, which causes some PCM samples to overflow
// and wrap, creating static. So instead, read the samples as floats and
// convert to short ourselves.
// Use a loop to convert a handful of samples at a time, avoiding a heap
// allocation for temporary storage. 64 at a time works, though maybe it
// could be more.
while(total < frames)
{
size_t todo = MIN<size_t>(frames-total, 64/SndInfo.channels);
float tmp[64];
size_t got = (size_t)sf_readf_float(SndFile, tmp, todo);
if(got < todo) frames = total + got;
for(size_t i = 0;i < got*SndInfo.channels;i++)
*out++ = (short)xs_CRoundToInt(clamp(tmp[i] * 32767.f, -32768.f, 32767.f));
total += got;
}
return total * SndInfo.channels * 2;
}
TArray<uint8_t> SndFileDecoder::readAll()
{
if(SndInfo.frames <= 0)
return SoundDecoder::readAll();
int framesize = 2 * SndInfo.channels;
TArray<uint8_t> output;
output.Resize((unsigned)(SndInfo.frames * framesize));
size_t got = read((char*)&output[0], output.Size());
output.Resize((unsigned)got);
return output;
}
bool SndFileDecoder::seek(size_t ms_offset, bool ms, bool /*mayrestart*/)
{
size_t smp_offset = ms? (size_t)((double)ms_offset / 1000. * SndInfo.samplerate) : ms_offset;
if(sf_seek(SndFile, smp_offset, SEEK_SET) < 0)
return false;
return true;
}
size_t SndFileDecoder::getSampleOffset()
{
return (size_t)sf_seek(SndFile, 0, SEEK_CUR);
}
size_t SndFileDecoder::getSampleLength()
{
return (size_t)((SndInfo.frames > 0) ? SndInfo.frames : 0);
}
#endif

View file

@ -1,49 +0,0 @@
#ifndef SNDFILE_DECODER_H
#define SNDFILE_DECODER_H
#include "i_soundinternal.h"
#include "files.h"
#ifdef HAVE_SNDFILE
#ifndef DYN_SNDFILE
#include "sndfile.h"
#else
#include "thirdparty/sndfile.h"
#endif
struct SndFileDecoder : public SoundDecoder
{
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes);
virtual TArray<uint8_t> readAll();
virtual bool seek(size_t ms_offset, bool ms, bool mayrestart);
virtual size_t getSampleOffset();
virtual size_t getSampleLength();
SndFileDecoder() : SndFile(0) { }
virtual ~SndFileDecoder();
protected:
virtual bool open(FileReader &reader);
private:
SNDFILE *SndFile;
SF_INFO SndInfo;
FileReader Reader;
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);
// Make non-copyable
SndFileDecoder(const SndFileDecoder &rhs);
SndFileDecoder& operator=(const SndFileDecoder &rhs);
};
#endif
#endif /* SNDFILE_DECODER_H */

View file

@ -1,23 +0,0 @@
#ifndef SNDDEF_H
#define SNDDEF_H
#if defined HAVE_SNDFILE && defined DYN_SNDFILE
#define DEFINE_ENTRY(type, name) static TReqProc<SndFileModule, type> p_##name{#name};
DEFINE_ENTRY(int (*)(SNDFILE *sndfile), sf_close)
DEFINE_ENTRY(SNDFILE* (*)(SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data), sf_open_virtual)
DEFINE_ENTRY(sf_count_t (*)(SNDFILE *sndfile, float *ptr, sf_count_t frames), sf_readf_float)
DEFINE_ENTRY(sf_count_t (*)(SNDFILE *sndfile, sf_count_t frames, int whence), sf_seek)
#undef DEFINE_ENTRY
#ifndef IN_IDE_PARSER
#define sf_close p_sf_close
#define sf_open_virtual p_sf_open_virtual
#define sf_readf_float p_sf_readf_float
#define sf_seek p_sf_seek
#endif
#endif
#endif

View file

@ -47,7 +47,7 @@
#include "stats.h"
#include "vm.h"
#include "s_music.h"
#include "i_soundfont.h"
#include "filereadermusicinterface.h"
#include "../libraries/zmusic/midisources/midisource.h"
#include "../libraries/dumb/include/dumb.h"
@ -83,7 +83,7 @@ EXTERN_CVAR (Int, snd_mididevice)
static bool MusicDown = true;
static bool ungzip(uint8_t *data, int size, TArray<uint8_t> &newdata);
static bool ungzip(uint8_t *data, int size, std::vector<uint8_t> &newdata);
MusInfo *currSong;
int nomusic = 0;
@ -205,8 +205,6 @@ MusInfo::~MusInfo ()
void MusInfo::Start(bool loop, float rel_vol, int subsong)
{
if (nomusic) return;
if (rel_vol > 0.f)
{
float factor = relative_volume / saved_relative_volume;
@ -287,24 +285,21 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
//
//==========================================================================
static MIDISource *CreateMIDISource(FileReader &reader, EMIDIType miditype)
static MIDISource *CreateMIDISource(const uint8_t *data, size_t length, EMIDIType miditype)
{
MIDISource *source = nullptr;
auto data = reader.Read();
if (data.Size() <= 0) return nullptr;
switch (miditype)
{
case MIDI_MUS:
return new MUSSong2(data.Data(), data.Size());
return new MUSSong2(data, length);
case MIDI_MIDI:
return new MIDISong2(data.Data(), data.Size());
return new MIDISong2(data, length);
case MIDI_HMI:
return new HMISong(data.Data(), data.Size());
return new HMISong(data, length);
case MIDI_XMI:
return new XMISong(data.Data(), data.Size());
return new XMISong(data, length);
default:
return nullptr;
@ -378,153 +373,154 @@ static EMIDIType IdentifyMIDIType(uint32_t *id, int size)
//
//==========================================================================
MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device)
MusInfo *I_RegisterSong (MusicIO::FileInterface *reader, MidiDeviceSetting *device)
{
MusInfo *info = nullptr;
StreamSource *streamsource = nullptr;
const char *fmt;
uint32_t id[32/4];
if (nomusic)
if(reader->read(id, 32) != 32 || reader->seek(-32, FileReader::SeekCur) != 0)
{
reader->close();
return nullptr;
}
if(reader.Read(id, 32) != 32 || reader.Seek(-32, FileReader::SeekCur) != 0)
try
{
return nullptr;
}
// Check for gzip compression. Some formats are expected to have players
// that can handle it, so it simplifies things if we make all songs
// gzippable.
if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID)
{
if (!reader.OpenMemoryArray([&reader](TArray<uint8_t> &array)
// Check for gzip compression. Some formats are expected to have players
// that can handle it, so it simplifies things if we make all songs
// gzippable.
if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID)
{
bool res = false;
auto len = reader.GetLength();
uint8_t *gzipped = new uint8_t[len];
if (reader.Read(gzipped, len) == len)
// swap out the reader with one that reads the decompressed content.
auto zreader = new MusicIO::VectorReader([reader](std::vector<uint8_t>& array)
{
bool res = false;
auto len = reader->filelength();
uint8_t* gzipped = new uint8_t[len];
if (reader->read(gzipped, len) == len)
{
res = ungzip(gzipped, (int)len, array);
}
delete[] gzipped;
});
reader->close();
reader = zreader;
if (reader->read(id, 32) != 32 || reader->seek(-32, FileReader::SeekCur) != 0)
{
res = ungzip(gzipped, (int)len, array);
reader->close();
return nullptr;
}
delete[] gzipped;
return res;
}))
{
return nullptr;
}
if (reader.Read(id, 32) != 32 || reader.Seek(-32, FileReader::SeekCur) != 0)
EMIDIType miditype = IdentifyMIDIType(id, sizeof(id));
if (miditype != MIDI_NOTMIDI)
{
return nullptr;
}
}
std::vector<uint8_t> data(reader->filelength());
if (reader->read(data.data(), (long)data.size()) != (long)data.size())
{
reader->close();
return nullptr;
}
auto source = CreateMIDISource(data.data(), data.size(), miditype);
if (source == nullptr)
{
reader->close();
return nullptr;
}
if (!source->isValid())
{
delete source;
return nullptr;
}
EMIDIType miditype = IdentifyMIDIType(id, sizeof(id));
if (miditype != MIDI_NOTMIDI)
{
auto source = CreateMIDISource(reader, miditype);
if (source == nullptr) return nullptr;
if (!source->isValid())
{
delete source;
return nullptr;
}
EMidiDevice devtype = device == nullptr? MDEV_DEFAULT : (EMidiDevice)device->device;
EMidiDevice devtype = device == nullptr ? MDEV_DEFAULT : (EMidiDevice)device->device;
#ifndef _WIN32
// non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS
if (devtype == MDEV_MMAPI)
devtype = MDEV_SNDSYS;
// non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS
if (devtype == MDEV_MMAPI)
devtype = MDEV_SNDSYS;
#endif
MIDIStreamer *streamer = CreateMIDIStreamer(devtype, device != nullptr? device->args.GetChars() : "");
if (streamer == nullptr)
{
delete source;
return nullptr;
MIDIStreamer* streamer = CreateMIDIStreamer(devtype, device != nullptr ? device->args.GetChars() : "");
if (streamer == nullptr)
{
delete source;
reader->close();
return nullptr;
}
streamer->SetMIDISource(source);
info = streamer;
}
streamer->SetMIDISource(source);
info = streamer;
}
// Check for various raw OPL formats
else if (
(id[0] == MAKE_ID('R','A','W','A') && id[1] == MAKE_ID('D','A','T','A')) || // Rdos Raw OPL
(id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
(id[0] == MAKE_ID('A','D','L','I') && *((uint8_t *)id + 4) == 'B')) // Martin Fernandez's modified IMF
{
OPL_SetupConfig(&oplConfig, device->args.GetChars(), false);
auto mreader = new FileReaderMusicInterface(reader);
streamsource = OPL_OpenSong(mreader, &oplConfig);
reader = mreader->GetReader(); // We need to get this back for the rest of this function.
delete mreader;
}
else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A')))
{
auto mreader = new FileReaderMusicInterface(reader);
streamsource = XA_OpenSong(mreader); // this takes over the reader.
}
// Check for game music
else if ((fmt = GME_CheckFormat(id[0])) != nullptr && fmt[0] != '\0')
{
auto mreader = new FileReaderMusicInterface(reader);
streamsource = GME_OpenSong(mreader, fmt, gme_stereodepth, (int)GSnd->GetOutputRate());
reader = mreader->GetReader(); // We need to get this back for the rest of this function.
delete mreader;
}
// Check for module formats
else
{
auto mreader = new FileReaderMusicInterface(reader);
Dumb_SetupConfig(&dumbConfig);
streamsource = MOD_OpenSong(mreader, &dumbConfig, (int)GSnd->GetOutputRate());
reader = mreader->GetReader(); // We need to get this back for the rest of this function.
delete mreader;
}
if (info == nullptr && streamsource == nullptr)
{
streamsource = SndFile_OpenSong(reader);
}
if (streamsource)
{
info = OpenStreamSong(streamsource);
if (!info)
// Check for various raw OPL formats
else if (
(id[0] == MAKE_ID('R', 'A', 'W', 'A') && id[1] == MAKE_ID('D', 'A', 'T', 'A')) || // Rdos Raw OPL
(id[0] == MAKE_ID('D', 'B', 'R', 'A') && id[1] == MAKE_ID('W', 'O', 'P', 'L')) || // DosBox Raw OPL
(id[0] == MAKE_ID('A', 'D', 'L', 'I') && *((uint8_t*)id + 4) == 'B')) // Martin Fernandez's modified IMF
{
// If this fails we have no more valid data - but it couldn't be a CDDA file anyway.
return nullptr;
OPL_SetupConfig(&oplConfig, device->args.GetChars(), false);
streamsource = OPL_OpenSong(reader, &oplConfig);
}
else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A')))
{
streamsource = XA_OpenSong(reader); // this takes over the reader.
reader = nullptr; // We do not own this anymore.
}
// Check for game music
else if ((fmt = GME_CheckFormat(id[0])) != nullptr && fmt[0] != '\0')
{
streamsource = GME_OpenSong(reader, fmt, gme_stereodepth, (int)GSnd->GetOutputRate());
}
// Check for module formats
else
{
Dumb_SetupConfig(&dumbConfig);
streamsource = MOD_OpenSong(reader, &dumbConfig, (int)GSnd->GetOutputRate());
}
if (info == nullptr && streamsource == nullptr)
{
streamsource = SndFile_OpenSong(reader); // this only takes over the reader if it succeeds. We need to look out for this.
if (streamsource != nullptr) reader = nullptr;
}
if (streamsource)
{
info = OpenStreamSong(streamsource);
}
if (info == nullptr && reader != nullptr)
{
// Check for CDDA "format"
if (id[0] == (('R') | (('I') << 8) | (('F') << 16) | (('F') << 24)))
{
uint32_t subid;
reader->seek(8, SEEK_CUR);
if (reader->read(&subid, 4) == 4)
{
reader->seek(-12, SEEK_CUR);
if (subid == (('C') | (('D') << 8) | (('D') << 16) | (('A') << 24)))
{
// This is a CDDA file
info = new CDDAFile(reader);
}
}
}
}
if (info && !info->IsValid())
{
delete info;
info = nullptr;
}
}
if (info == nullptr)
{
// Check for CDDA "format"
if (id[0] == (('R')|(('I')<<8)|(('F')<<16)|(('F')<<24)))
{
uint32_t subid;
reader.Seek(8, FileReader::SeekCur);
if (reader.Read (&subid, 4) != 4)
{
return nullptr;
}
reader.Seek(-12, FileReader::SeekCur);
if (subid == (('C')|(('D')<<8)|(('D')<<16)|(('A')<<24)))
{
// This is a CDDA file
info = new CDDAFile (reader);
}
}
}
if (info && !info->IsValid ())
catch (...)
{
delete info;
info = nullptr;
// Make sure the reader is closed if this function abnormally terminates
if (reader) reader->close();
throw;
}
return info;
@ -558,7 +554,7 @@ MusInfo *I_RegisterCDSong (int track, int id)
//
//==========================================================================
static bool ungzip(uint8_t *data, int complen, TArray<uint8_t> &newdata)
static bool ungzip(uint8_t *data, int complen, std::vector<uint8_t> &newdata)
{
const uint8_t *max = data + complen - 8;
const uint8_t *compstart = data + 10;
@ -597,7 +593,7 @@ static bool ungzip(uint8_t *data, int complen, TArray<uint8_t> &newdata)
// Decompress
isize = LittleLong(*(uint32_t *)(data + complen - 4));
newdata.Resize(isize);
newdata.resize(isize);
stream.next_in = (Bytef *)compstart;
stream.avail_in = (uInt)(max - compstart);
@ -705,6 +701,7 @@ static MIDISource *GetMIDISource(const char *fn)
}
auto wlump = Wads.OpenLumpReader(lump);
uint32_t id[32 / 4];
if (wlump.Read(id, 32) != 32 || wlump.Seek(-32, FileReader::SeekCur) != 0)
@ -712,9 +709,15 @@ static MIDISource *GetMIDISource(const char *fn)
Printf("Unable to read lump %s\n", src.GetChars());
return nullptr;
}
auto type = IdentifyMIDIType(id, 32);
auto source = CreateMIDISource(wlump, type);
if (type == MIDI_NOTMIDI)
{
Printf("%s is not MIDI-based.\n", src.GetChars());
return nullptr;
}
auto data = wlump.Read();
auto source = CreateMIDISource(data.Data(), data.Size(), type);
if (source == nullptr)
{

View file

@ -55,7 +55,7 @@ void I_SetMusicVolume (double volume);
// Registers a song handle to song data.
class MusInfo;
struct MidiDeviceSetting;
MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device);
MusInfo *I_RegisterSong (MusicIO::FileInterface *reader, MidiDeviceSetting *device);
MusInfo *I_RegisterCDSong (int track, int cdid = 0);
// The base music class. Everything is derived from this --------------------

View file

@ -134,7 +134,7 @@ protected:
class CDDAFile : public CDSong
{
public:
CDDAFile (FileReader &reader);
CDDAFile (MusicIO::FileInterface *reader);
};
// Data interface
@ -154,7 +154,7 @@ class StreamSource;
StreamSource *MOD_OpenSong(MusicIO::FileInterface* reader, DumbConfig* config, int samplerate);
StreamSource* GME_OpenSong(MusicIO::FileInterface* reader, const char* fmt, float stereo_depth, int sample_rate);
StreamSource *SndFile_OpenSong(FileReader &fr);
StreamSource *SndFile_OpenSong(MusicIO::FileInterface* fr);
StreamSource* XA_OpenSong(MusicIO::FileInterface* reader);
StreamSource* OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config);

View file

@ -39,6 +39,7 @@
#include "cmdlib.h"
#include "i_system.h"
#include "gameconfigfile.h"
#include "filereadermusicinterface.h"
#include "resourcefiles/resourcefile.h"
#include "../libraries/timidityplus/timiditypp/common.h"

View file

@ -3,7 +3,7 @@
#include "doomtype.h"
#include "w_wad.h"
#include "files.h"
#include "../libraries/music_common/fileio.h"
#include "filereadermusicinterface.h"
enum
{
@ -21,45 +21,6 @@ struct FSoundFontInfo
int type;
};
struct FileReaderMusicInterface : public MusicIO::FileInterface
{
FileReader fr;
FileReaderMusicInterface(FileReader& fr_in)
{
fr = std::move(fr_in);
}
char* gets(char* buff, int n) override
{
if (!fr.isOpen()) return nullptr;
return fr.Gets(buff, n);
}
long read(void* buff, int32_t size, int32_t nitems) override
{
if (!fr.isOpen()) return 0;
return (long)fr.Read(buff, size * nitems) / size;
}
long seek(long offset, int whence) override
{
if (!fr.isOpen()) return 0;
return (long)fr.Seek(offset, (FileReader::ESeek)whence);
}
long tell() override
{
if (!fr.isOpen()) return 0;
return (long)fr.Tell();
}
void close()
{
delete this;
}
FileReader &&GetReader()
{
return std::move(fr);
}
};
//==========================================================================
//
//

View file

@ -111,33 +111,33 @@ bool CDSong::IsPlaying ()
return m_Status != STATE_Stopped;
}
CDDAFile::CDDAFile (FileReader &reader)
CDDAFile::CDDAFile (MusicIO::FileInterface* reader)
: CDSong ()
{
uint32_t chunk;
uint16_t track;
uint32_t discid;
auto endpos = reader.Tell() + reader.GetLength() - 8;
auto endpos = reader->tell() + reader->filelength() - 8;
// I_RegisterSong already identified this as a CDDA file, so we
// just need to check the contents we're interested in.
reader.Seek(12, FileReader::SeekCur);
reader->seek(12, SEEK_CUR);
while (reader.Tell() < endpos)
while (reader->tell() < endpos)
{
reader.Read(&chunk, 4);
reader->read(&chunk, 4);
if (chunk != (('f')|(('m')<<8)|(('t')<<16)|((' ')<<24)))
{
reader.Read(&chunk, 4);
reader.Seek(chunk, FileReader::SeekCur);
reader->read(&chunk, 4);
reader->seek(LittleLong(chunk), SEEK_CUR);
}
else
{
reader.Seek(6, FileReader::SeekCur);
reader.Read(&track, 2);
reader.Read(&discid, 4);
reader->seek(6, SEEK_CUR);
reader->read(&track, 2);
reader->read(&discid, 4);
if (CD_InitID (discid) && CD_CheckTrack (track))
if (CD_InitID (LittleLong(discid)) && CD_CheckTrack (LittleShort(track)))
{
m_Inited = true;
m_Track = track;

View file

@ -785,9 +785,7 @@ StreamSource* MOD_OpenSong(MusicIO::FileInterface *reader, DumbConfig* config, i
bool is_dos = true;
auto fpos = reader->tell();
reader->seek(0, SEEK_END);
int size = (int)reader->tell();
reader->seek(fpos, SEEK_SET);
int size = (int)reader->filelength();
filestate.ptr = start;
filestate.offset = 0;

View file

@ -124,9 +124,8 @@ StreamSource *GME_OpenSong(MusicIO::FileInterface *reader, const char *fmt, floa
}
auto fpos = reader->tell();
reader->seek(0, SEEK_END);
auto len = reader->tell();
reader->seek(fpos, SEEK_SET);
auto len = reader->filelength();
song = new uint8_t[len];
if (reader->read(song, len) != len)
{

View file

@ -40,6 +40,7 @@
#include "templates.h"
#include "m_fixed.h"
#include "streamsource.h"
#include "zmusic/sounddecoder.h"
// MACROS ------------------------------------------------------------------
@ -48,7 +49,7 @@
class SndFileSong : public StreamSource
{
public:
SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass);
SndFileSong(SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass);
~SndFileSong();
std::string GetStats() override;
SoundStreamInfo GetFormat() override;
@ -56,7 +57,6 @@ public:
protected:
std::mutex CritSec;
FileReader Reader;
SoundDecoder *Decoder;
int Channels;
int SampleRate;
@ -103,23 +103,23 @@ CUSTOM_CVAR(Int, snd_streambuffersize, 64, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
//
//==========================================================================
static void ParseVorbisComments(FileReader &fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass)
static void ParseVorbisComments(MusicIO::FileInterface *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass)
{
uint8_t vc_data[4];
// The VC block starts with a 32LE integer for the vendor string length,
// followed by the vendor string
if(fr.Read(vc_data, 4) != 4)
if(fr->read(vc_data, 4) != 4)
return;
uint32_t vndr_len = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24);
// Skip vendor string
if(fr.Seek(vndr_len, FileReader::SeekCur) == -1)
if(fr->seek(vndr_len, SEEK_CUR) == -1)
return;
// Following the vendor string is a 32LE integer for the number of
// comments, followed by each comment.
if(fr.Read(vc_data, 4) != 4)
if(fr->read(vc_data, 4) != 4)
return;
size_t count = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24);
@ -127,20 +127,20 @@ static void ParseVorbisComments(FileReader &fr, uint32_t *start, bool *startass,
{
// Each comment is a 32LE integer for the comment length, followed by
// the comment text (not null terminated!)
if(fr.Read(vc_data, 4) != 4)
if(fr->read(vc_data, 4) != 4)
return;
uint32_t length = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24);
if(length >= 128)
{
// If the comment is "big", skip it
if(fr.Seek(length, FileReader::SeekCur) == -1)
if(fr->seek(length, SEEK_CUR) == -1)
return;
continue;
}
char strdat[128];
if(fr.Read(strdat, length) != (long)length)
if(fr->read(strdat, length) != (long)length)
return;
strdat[length] = 0;
@ -151,13 +151,13 @@ static void ParseVorbisComments(FileReader &fr, uint32_t *start, bool *startass,
}
}
static void FindFlacComments(FileReader &fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass)
static void FindFlacComments(MusicIO::FileInterface *fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass)
{
// Already verified the fLaC marker, so we're 4 bytes into the file
bool lastblock = false;
uint8_t header[4];
while(!lastblock && fr.Read(header, 4) == 4)
while(!lastblock && fr->read(header, 4) == 4)
{
// The first byte of the block header contains the type and a flag
// indicating the last metadata block
@ -173,18 +173,18 @@ static void FindFlacComments(FileReader &fr, uint32_t *loop_start, bool *startas
return;
}
if(fr.Seek(blocksize, FileReader::SeekCur) == -1)
if(fr->seek(blocksize, SEEK_CUR) == -1)
break;
}
}
static void FindOggComments(FileReader &fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass)
static void FindOggComments(MusicIO::FileInterface *fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass)
{
uint8_t ogghead[27];
// We already read and verified the OggS marker, so skip the first 4 bytes
// of the Ogg page header.
while(fr.Read(ogghead+4, 23) == 23)
while(fr->read(ogghead+4, 23) == 23)
{
// The 19th byte of the Ogg header is a 32LE integer for the page
// number, and the 27th is a uint8 for the number of segments in the
@ -197,7 +197,7 @@ static void FindOggComments(FileReader &fr, uint32_t *loop_start, bool *startass
// each segment in the page. The page segment data follows contiguously
// after.
uint8_t segsizes[256];
if(fr.Read(segsizes, ogg_segments) != ogg_segments)
if(fr->read(segsizes, ogg_segments) != ogg_segments)
break;
// Find the segment with the Vorbis Comment packet (type 3)
@ -208,7 +208,7 @@ static void FindOggComments(FileReader &fr, uint32_t *loop_start, bool *startass
if(segsize > 16)
{
uint8_t vorbhead[7];
if(fr.Read(vorbhead, 7) != 7)
if(fr->read(vorbhead, 7) != 7)
return;
if(vorbhead[0] == 3 && memcmp(vorbhead+1, "vorbis", 6) == 0)
@ -235,7 +235,7 @@ static void FindOggComments(FileReader &fr, uint32_t *loop_start, bool *startass
segsize -= 7;
}
if(fr.Seek(segsize, FileReader::SeekCur) == -1)
if(fr->seek(segsize, SEEK_CUR) == -1)
return;
}
@ -243,16 +243,16 @@ static void FindOggComments(FileReader &fr, uint32_t *loop_start, bool *startass
if(ogg_pagenum >= 2)
break;
if(fr.Read(ogghead, 4) != 4 || memcmp(ogghead, "OggS", 4) != 0)
if(fr->read(ogghead, 4) != 4 || memcmp(ogghead, "OggS", 4) != 0)
break;
}
}
void FindLoopTags(FileReader &fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass)
void FindLoopTags(MusicIO::FileInterface *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass)
{
uint8_t signature[4];
fr.Read(signature, 4);
fr->read(signature, 4);
if(memcmp(signature, "fLaC", 4) == 0)
FindFlacComments(fr, start, startass, end, endass);
else if(memcmp(signature, "OggS", 4) == 0)
@ -266,18 +266,18 @@ void FindLoopTags(FileReader &fr, uint32_t *start, bool *startass, uint32_t *end
//
//==========================================================================
StreamSource *SndFile_OpenSong(FileReader &fr)
StreamSource *SndFile_OpenSong(MusicIO::FileInterface *fr)
{
fr.Seek(0, FileReader::SeekSet);
fr->seek(0, SEEK_SET);
uint32_t loop_start = 0, loop_end = ~0u;
bool startass = false, endass = false;
FindLoopTags(fr, &loop_start, &startass, &loop_end, &endass);
fr.Seek(0, FileReader::SeekSet);
auto decoder = SoundRenderer::CreateDecoder(fr);
if (decoder == nullptr) return nullptr;
return new SndFileSong(fr, decoder, loop_start, loop_end, startass, endass);
fr->seek(0, FileReader::SeekSet);
auto decoder = SoundDecoder::CreateDecoder(fr);
if (decoder == nullptr) return nullptr; // If this fails the file reader has not been taken over and the caller needs to clean up. This is to allow further analysis of the passed file.
return new SndFileSong(decoder, loop_start, loop_end, startass, endass);
}
//==========================================================================
@ -286,7 +286,7 @@ StreamSource *SndFile_OpenSong(FileReader &fr)
//
//==========================================================================
SndFileSong::SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass)
SndFileSong::SndFileSong(SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass)
{
ChannelConfig iChannels;
SampleType Type;
@ -299,7 +299,6 @@ SndFileSong::SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loo
const uint32_t sampleLength = (uint32_t)decoder->getSampleLength();
Loop_Start = loop_start;
Loop_End = sampleLength == 0 ? loop_end : clamp<uint32_t>(loop_end, 0, sampleLength);
Reader = std::move(reader);
Decoder = decoder;
Channels = iChannels == ChannelConfig_Stereo? 2:1;
}

View file

@ -85,6 +85,7 @@
#include "g_game.h"
#include "atterm.h"
#include "s_music.h"
#include "filereadermusicinterface.h"
// MACROS ------------------------------------------------------------------
@ -304,6 +305,7 @@ bool S_StartMusic (const char *m_id)
bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
{
if (nomusic) return false; // skip the entire procedure if music is globally disabled.
if (!force && PlayList)
{ // Don't change if a playlist is active
return false;
@ -457,7 +459,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
{
try
{
mus_playing.handle = I_RegisterSong(reader, devp);
auto mreader = new FileReaderMusicInterface(reader);
mus_playing.handle = I_RegisterSong(mreader, devp);
}
catch (const std::runtime_error& err)
{
@ -647,6 +650,10 @@ CCMD (idmus)
}
}
}
else
{
Printf("Music is disabled\n");
}
}
//==========================================================================
@ -681,6 +688,10 @@ CCMD (changemus)
}
}
}
else
{
Printf("Music is disabled\n");
}
}
//==========================================================================

View file

@ -1532,7 +1532,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer)
std::pair<SoundHandle, bool> snd;
if (pBuffer->mBuffer.Size() > 0)
if (pBuffer->mBuffer.size() > 0)
{
snd = GSnd->LoadSoundBuffered(pBuffer, true);
}

File diff suppressed because it is too large Load diff

View file

@ -1,667 +0,0 @@
/*
** Copyright (C) 1999-2011Erik de Castro Lopo <erikd@mega-nerd.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation; either version 2.1 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
** sndfile.h -- system-wide definitions
**
** API documentation is in the doc/ directory of the source code tarball
** and at http://www.mega-nerd.com/libsndfile/api.html.
*/
#ifndef SNDFILE_H
#define SNDFILE_H
/* This is the version 1.0.X header file. */
#define SNDFILE_1
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* The following file types can be read and written.
** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise
** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and
** SF_FORMAT_SUBMASK can be used to separate the major and minor file
** types.
*/
enum
{ /* Major formats. */
SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */
SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */
SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */
SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */
SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */
SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */
SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */
SF_FORMAT_VOC = 0x080000, /* VOC files. */
SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */
SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */
SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */
SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */
SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */
SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */
SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */
SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */
SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */
SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */
SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */
SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */
SF_FORMAT_CAF = 0x180000, /* Core Audio File format */
SF_FORMAT_WVE = 0x190000, /* Psion WVE format */
SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */
SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */
SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */
/* Subtypes from here on. */
SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */
SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */
SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */
SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */
SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */
SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */
SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */
SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */
SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */
SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */
SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */
SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */
SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */
SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */
SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */
SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */
SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */
SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */
SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */
SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */
SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */
/* Endian-ness options. */
SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */
SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */
SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */
SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */
SF_FORMAT_SUBMASK = 0x0000FFFF,
SF_FORMAT_TYPEMASK = 0x0FFF0000,
SF_FORMAT_ENDMASK = 0x30000000
} ;
/*
** The following are the valid command numbers for the sf_command()
** interface. The use of these commands is documented in the file
** command.html in the doc directory of the source code distribution.
*/
enum
{ SFC_GET_LIB_VERSION = 0x1000,
SFC_GET_LOG_INFO = 0x1001,
SFC_GET_CURRENT_SF_INFO = 0x1002,
SFC_GET_NORM_DOUBLE = 0x1010,
SFC_GET_NORM_FLOAT = 0x1011,
SFC_SET_NORM_DOUBLE = 0x1012,
SFC_SET_NORM_FLOAT = 0x1013,
SFC_SET_SCALE_FLOAT_INT_READ = 0x1014,
SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015,
SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020,
SFC_GET_SIMPLE_FORMAT = 0x1021,
SFC_GET_FORMAT_INFO = 0x1028,
SFC_GET_FORMAT_MAJOR_COUNT = 0x1030,
SFC_GET_FORMAT_MAJOR = 0x1031,
SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032,
SFC_GET_FORMAT_SUBTYPE = 0x1033,
SFC_CALC_SIGNAL_MAX = 0x1040,
SFC_CALC_NORM_SIGNAL_MAX = 0x1041,
SFC_CALC_MAX_ALL_CHANNELS = 0x1042,
SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043,
SFC_GET_SIGNAL_MAX = 0x1044,
SFC_GET_MAX_ALL_CHANNELS = 0x1045,
SFC_SET_ADD_PEAK_CHUNK = 0x1050,
SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051,
SFC_UPDATE_HEADER_NOW = 0x1060,
SFC_SET_UPDATE_HEADER_AUTO = 0x1061,
SFC_FILE_TRUNCATE = 0x1080,
SFC_SET_RAW_START_OFFSET = 0x1090,
SFC_SET_DITHER_ON_WRITE = 0x10A0,
SFC_SET_DITHER_ON_READ = 0x10A1,
SFC_GET_DITHER_INFO_COUNT = 0x10A2,
SFC_GET_DITHER_INFO = 0x10A3,
SFC_GET_EMBED_FILE_INFO = 0x10B0,
SFC_SET_CLIPPING = 0x10C0,
SFC_GET_CLIPPING = 0x10C1,
SFC_GET_INSTRUMENT = 0x10D0,
SFC_SET_INSTRUMENT = 0x10D1,
SFC_GET_LOOP_INFO = 0x10E0,
SFC_GET_BROADCAST_INFO = 0x10F0,
SFC_SET_BROADCAST_INFO = 0x10F1,
SFC_GET_CHANNEL_MAP_INFO = 0x1100,
SFC_SET_CHANNEL_MAP_INFO = 0x1101,
SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110,
/* Support for Wavex Ambisonics Format */
SFC_WAVEX_SET_AMBISONIC = 0x1200,
SFC_WAVEX_GET_AMBISONIC = 0x1201,
SFC_SET_VBR_ENCODING_QUALITY = 0x1300,
/* Following commands for testing only. */
SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001,
/*
** SFC_SET_ADD_* values are deprecated and will disappear at some
** time in the future. They are guaranteed to be here up to and
** including version 1.0.8 to avoid breakage of existng software.
** They currently do nothing and will continue to do nothing.
*/
SFC_SET_ADD_DITHER_ON_WRITE = 0x1070,
SFC_SET_ADD_DITHER_ON_READ = 0x1071
} ;
/*
** String types that can be set and read from files. Not all file types
** support this and even the file types which support one, may not support
** all string types.
*/
enum
{ SF_STR_TITLE = 0x01,
SF_STR_COPYRIGHT = 0x02,
SF_STR_SOFTWARE = 0x03,
SF_STR_ARTIST = 0x04,
SF_STR_COMMENT = 0x05,
SF_STR_DATE = 0x06,
SF_STR_ALBUM = 0x07,
SF_STR_LICENSE = 0x08,
SF_STR_TRACKNUMBER = 0x09,
SF_STR_GENRE = 0x10
} ;
/*
** Use the following as the start and end index when doing metadata
** transcoding.
*/
#define SF_STR_FIRST SF_STR_TITLE
#define SF_STR_LAST SF_STR_GENRE
enum
{ /* True and false */
SF_FALSE = 0,
SF_TRUE = 1,
/* Modes for opening files. */
SFM_READ = 0x10,
SFM_WRITE = 0x20,
SFM_RDWR = 0x30,
SF_AMBISONIC_NONE = 0x40,
SF_AMBISONIC_B_FORMAT = 0x41
} ;
/* Public error values. These are guaranteed to remain unchanged for the duration
** of the library major version number.
** There are also a large number of private error numbers which are internal to
** the library which can change at any time.
*/
enum
{ SF_ERR_NO_ERROR = 0,
SF_ERR_UNRECOGNISED_FORMAT = 1,
SF_ERR_SYSTEM = 2,
SF_ERR_MALFORMED_FILE = 3,
SF_ERR_UNSUPPORTED_ENCODING = 4
} ;
/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP).
*/
enum
{ SF_CHANNEL_MAP_INVALID = 0,
SF_CHANNEL_MAP_MONO = 1,
SF_CHANNEL_MAP_LEFT, /* Apple calls this 'Left' */
SF_CHANNEL_MAP_RIGHT, /* Apple calls this 'Right' */
SF_CHANNEL_MAP_CENTER, /* Apple calls this 'Center' */
SF_CHANNEL_MAP_FRONT_LEFT,
SF_CHANNEL_MAP_FRONT_RIGHT,
SF_CHANNEL_MAP_FRONT_CENTER,
SF_CHANNEL_MAP_REAR_CENTER, /* Apple calls this 'Center Surround', Msft calls this 'Back Center' */
SF_CHANNEL_MAP_REAR_LEFT, /* Apple calls this 'Left Surround', Msft calls this 'Back Left' */
SF_CHANNEL_MAP_REAR_RIGHT, /* Apple calls this 'Right Surround', Msft calls this 'Back Right' */
SF_CHANNEL_MAP_LFE, /* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency' */
SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */
SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */
SF_CHANNEL_MAP_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */
SF_CHANNEL_MAP_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */
SF_CHANNEL_MAP_TOP_CENTER, /* Apple calls this 'Top Center Surround' */
SF_CHANNEL_MAP_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */
SF_CHANNEL_MAP_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */
SF_CHANNEL_MAP_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */
SF_CHANNEL_MAP_TOP_REAR_LEFT, /* Apple and MS call this 'Top Back Left' */
SF_CHANNEL_MAP_TOP_REAR_RIGHT, /* Apple and MS call this 'Top Back Right' */
SF_CHANNEL_MAP_TOP_REAR_CENTER, /* Apple and MS call this 'Top Back Center' */
SF_CHANNEL_MAP_AMBISONIC_B_W,
SF_CHANNEL_MAP_AMBISONIC_B_X,
SF_CHANNEL_MAP_AMBISONIC_B_Y,
SF_CHANNEL_MAP_AMBISONIC_B_Z,
SF_CHANNEL_MAP_MAX
} ;
/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */
typedef struct SNDFILE_tag SNDFILE ;
/* The following typedef is system specific and is defined when libsndfile is
** compiled. sf_count_t will be a 64 bit value when the underlying OS allows
** 64 bit file offsets.
** On windows, we need to allow the same header file to be compiler by both GCC
** and the Microsoft compiler.
*/
#if (defined (_MSCVER) || defined (_MSC_VER))
typedef __int64 sf_count_t ;
#define SF_COUNT_MAX 0x7fffffffffffffffi64
#else
typedef int64_t sf_count_t ;
#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL
#endif
/* A pointer to a SF_INFO structure is passed to sf_open () and filled in.
** On write, the SF_INFO structure is filled in by the user and passed into
** sf_open ().
*/
struct SF_INFO
{ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */
int samplerate ;
int channels ;
int format ;
int sections ;
int seekable ;
} ;
typedef struct SF_INFO SF_INFO ;
/* The SF_FORMAT_INFO struct is used to retrieve information about the sound
** file formats libsndfile supports using the sf_command () interface.
**
** Using this interface will allow applications to support new file formats
** and encoding types when libsndfile is upgraded, without requiring
** re-compilation of the application.
**
** Please consult the libsndfile documentation (particularly the information
** on the sf_command () interface) for examples of its use.
*/
typedef struct
{ int format ;
const char *name ;
const char *extension ;
} SF_FORMAT_INFO ;
/*
** Enums and typedefs for adding dither on read and write.
** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE
** and SFC_SET_DITHER_ON_READ.
*/
enum
{ SFD_DEFAULT_LEVEL = 0,
SFD_CUSTOM_LEVEL = 0x40000000,
SFD_NO_DITHER = 500,
SFD_WHITE = 501,
SFD_TRIANGULAR_PDF = 502
} ;
typedef struct
{ int type ;
double level ;
const char *name ;
} SF_DITHER_INFO ;
/* Struct used to retrieve information about a file embedded within a
** larger file. See SFC_GET_EMBED_FILE_INFO.
*/
typedef struct
{ sf_count_t offset ;
sf_count_t length ;
} SF_EMBED_FILE_INFO ;
/*
** Structs used to retrieve music sample information from a file.
*/
enum
{ /*
** The loop mode field in SF_INSTRUMENT will be one of the following.
*/
SF_LOOP_NONE = 800,
SF_LOOP_FORWARD,
SF_LOOP_BACKWARD,
SF_LOOP_ALTERNATING
} ;
typedef struct
{ int gain ;
char basenote, detune ;
char velocity_lo, velocity_hi ;
char key_lo, key_hi ;
int loop_count ;
struct
{ int mode ;
unsigned int start ;
unsigned int end ;
unsigned int count ;
} loops [16] ; /* make variable in a sensible way */
} SF_INSTRUMENT ;
/* Struct used to retrieve loop information from a file.*/
typedef struct
{
short time_sig_num ; /* any positive integer > 0 */
short time_sig_den ; /* any positive power of 2 > 0 */
int loop_mode ; /* see SF_LOOP enum */
int num_beats ; /* this is NOT the amount of quarter notes !!!*/
/* a full bar of 4/4 is 4 beats */
/* a full bar of 7/8 is 7 beats */
float bpm ; /* suggestion, as it can be calculated using other fields:*/
/* file's lenght, file's sampleRate and our time_sig_den*/
/* -> bpms are always the amount of _quarter notes_ per minute */
int root_key ; /* MIDI note, or -1 for None */
int future [6] ;
} SF_LOOP_INFO ;
/* Struct used to retrieve broadcast (EBU) information from a file.
** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE.
*/
#define SF_BROADCAST_INFO_VAR(coding_hist_size) \
struct \
{ char description [256] ; \
char originator [32] ; \
char originator_reference [32] ; \
char origination_date [10] ; \
char origination_time [8] ; \
unsigned int time_reference_low ; \
unsigned int time_reference_high ; \
short version ; \
char umid [64] ; \
char reserved [190] ; \
unsigned int coding_history_size ; \
char coding_history [coding_hist_size] ; \
}
/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */
typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ;
/* Virtual I/O functionality. */
typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ;
typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ;
typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ;
typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ;
typedef sf_count_t (*sf_vio_tell) (void *user_data) ;
struct SF_VIRTUAL_IO
{ sf_vio_get_filelen get_filelen ;
sf_vio_seek seek ;
sf_vio_read read ;
sf_vio_write write ;
sf_vio_tell tell ;
} ;
typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ;
/* Open the specified file for read, write or both. On error, this will
** return a NULL pointer. To find the error number, pass a NULL SNDFILE
** to sf_strerror ().
** All calls to sf_open() should be matched with a call to sf_close().
*/
SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ;
/* Use the existing file descriptor to create a SNDFILE object. If close_desc
** is TRUE, the file descriptor will be closed when sf_close() is called. If
** it is FALSE, the descritor will not be closed.
** When passed a descriptor like this, the library will assume that the start
** of file header is at the current file offset. This allows sound files within
** larger container files to be read and/or written.
** On error, this will return a NULL pointer. To find the error number, pass a
** NULL SNDFILE to sf_strerror ().
** All calls to sf_open_fd() should be matched with a call to sf_close().
*/
SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
/* sf_error () returns a error number which can be translated to a text
** string using sf_error_number().
*/
int sf_error (SNDFILE *sndfile) ;
/* sf_strerror () returns to the caller a pointer to the current error message for
** the given SNDFILE.
*/
const char* sf_strerror (SNDFILE *sndfile) ;
/* sf_error_number () allows the retrieval of the error string for each internal
** error number.
**
*/
const char* sf_error_number (int errnum) ;
/* The following two error functions are deprecated but they will remain in the
** library for the forseeable future. The function sf_strerror() should be used
** in their place.
*/
int sf_perror (SNDFILE *sndfile) ;
int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ;
/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */
int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ;
/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */
int sf_format_check (const SF_INFO *info) ;
/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses
** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as
** stdio.h function fseek ().
** An offset of zero with whence set to SEEK_SET will position the
** read / write pointer to the first data sample.
** On success sf_seek returns the current position in (multi-channel)
** samples from the start of the file.
** Please see the libsndfile documentation for moving the read pointer
** separately from the write pointer on files open in mode SFM_RDWR.
** On error all of these functions return -1.
*/
sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ;
/* Functions for retrieving and setting string data within sound files.
** Not all file types support this features; AIFF and WAV do. For both
** functions, the str_type parameter must be one of the SF_STR_* values
** defined above.
** On error, sf_set_string() returns non-zero while sf_get_string()
** returns NULL.
*/
int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
/* Return the library version string. */
const char * sf_version_string (void) ;
/* Functions for reading/writing the waveform data of a sound file.
*/
sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ;
/* Functions for reading and writing the data chunk in terms of frames.
** The number of items actually read/written = frames * number of channels.
** sf_xxxx_raw read/writes the raw data bytes from/to the file
** sf_xxxx_short passes data in the native short format
** sf_xxxx_int passes data in the native int format
** sf_xxxx_float passes data in the native float format
** sf_xxxx_double passes data in the native double format
** All of these read/write function return number of frames read/written.
*/
sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ;
sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ;
sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ;
sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ;
/* Functions for reading and writing the data chunk in terms of items.
** Otherwise similar to above.
** All of these read/write function return number of items read/written.
*/
sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ;
sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ;
sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ;
sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ;
/* Close the SNDFILE and clean up all memory allocations associated with this
** file.
** Returns 0 on success, or an error number.
*/
int sf_close (SNDFILE *sndfile) ;
/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file
** to force the writing of data to disk. If the file is opened SFM_READ
** no action is taken.
*/
void sf_write_sync (SNDFILE *sndfile) ;
/* The function sf_wchar_open() is Windows Only!
** Open a file passing in a Windows Unicode filename. Otherwise, this is
** the same as sf_open().
**
** In order for this to work, you need to do the following:
**
** #include <windows.h>
** #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
** #including <sndfile.h>
*/
#if (defined (ENABLE_SNDFILE_WINDOWS_PROTOTYPES) && ENABLE_SNDFILE_WINDOWS_PROTOTYPES)
SNDFILE* sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ;
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* SNDFILE_H */

View file

@ -0,0 +1,39 @@
#pragma once
#include "../libraries/music_common/fileio.h"
#include "files.h"
struct FileReaderMusicInterface : public MusicIO::FileInterface
{
FileReader fr;
FileReaderMusicInterface(FileReader& fr_in)
{
fr = std::move(fr_in);
}
char* gets(char* buff, int n) override
{
if (!fr.isOpen()) return nullptr;
return fr.Gets(buff, n);
}
long read(void* buff, int32_t size, int32_t nitems) override
{
if (!fr.isOpen()) return 0;
return (long)fr.Read(buff, size * nitems) / size;
}
long seek(long offset, int whence) override
{
if (!fr.isOpen()) return 0;
return (long)fr.Seek(offset, (FileReader::ESeek)whence);
}
long tell() override
{
if (!fr.isOpen()) return 0;
return (long)fr.Tell();
}
FileReader& getReader()
{
return fr;
}
};

View file

@ -328,4 +328,5 @@ public:
TArray<unsigned char> *GetBuffer() { return &mBuffer; }
};
#endif