- 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
parent b883d27a1f
commit cdf2a17c5a
35 changed files with 548 additions and 430 deletions

View File

@ -407,6 +407,11 @@ install(DIRECTORY docs/
DESTINATION ${INSTALL_DOCS_PATH}
COMPONENT "Documentation")
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON )
option( DYN_OPENAL "Dynamically load OpenAL" ON )
option( DYN_SNDFILE "Dynamically load libsndfile" ON )
option( DYN_MPG123 "Dynamically load libmpg123" ON )
add_subdirectory( libraries/lzma )
add_subdirectory( tools )
add_subdirectory( libraries/dumb )

View File

@ -50,16 +50,35 @@ namespace MusicIO
struct FileInterface
{
std::string filename;
long length = -1;
// It's really too bad that the using code requires long instead of size_t.
// Fortunately 2GB files are unlikely to come by here.
protected:
//
virtual ~FileInterface() {}
public:
virtual char* gets(char* buff, int n) = 0;
virtual long read(void* buff, int32_t size, int32_t nitems) = 0;
long read(void* buff, int32_t size) { return read(buff, 1, size); }
virtual long seek(long offset, int whence) = 0;
virtual long tell() = 0;
virtual void close() = 0;
virtual void close()
{
delete this;
}
long filelength()
{
if (length == -1)
{
long pos = tell();
seek(0, SEEK_END);
length = tell();
seek(pos, SEEK_SET);
}
return length;
}
};
//==========================================================================
@ -96,11 +115,6 @@ struct StdioFileReader : public FileInterface
if (!f) return 0;
return ftell(f);
}
void close() override
{
if (f) fclose(f);
delete this;
}
};
@ -113,7 +127,7 @@ struct StdioFileReader : public FileInterface
struct MemoryReader : public FileInterface
{
const uint8_t *mData;
const long mLength;
long mLength;
long mPos;
MemoryReader(const uint8_t *data, long length)
@ -180,13 +194,32 @@ struct MemoryReader : public FileInterface
{
return mPos;
}
void close() override
protected:
MemoryReader() {}
};
//==========================================================================
//
// Inplementation of the FileInterface for an std::vector owned by the reader
//
//==========================================================================
struct VectorReader : public MemoryReader
{
std::vector<uint8_t> mVector;
template <class getFunc>
VectorReader(getFunc getter) // read contents to a buffer and return a reader to it
{
delete this;
getter(mVector);
mData = mVector.data();
mLength = (long)mVector.size();
}
};
//==========================================================================
//
// The follpwing two functions are needed to allow using UTF-8 in the file interface.

View File

@ -56,7 +56,7 @@ unsigned char *_WM_BufferFile(MusicIO::SoundFontReaderInterface *reader, const c
return NULL;
}
auto fsize = (fp->seek(0, SEEK_END), fp->tell());
auto fsize = fp->filelength();
if (fsize > WM_MAXFILESIZE)
{

View File

@ -9,8 +9,6 @@ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}")
include( CheckFunctionExists )
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON )
CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS )
if( NOT STRICMP_EXISTS )
add_definitions( -Dstricmp=strcasecmp )
@ -21,6 +19,25 @@ if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp )
endif()
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()
include_directories( "${ADL_INCLUDE_DIR}" "${OPN_INCLUDE_DIR}" "${TIMIDITYPP_INCLUDE_DIR}" "${TIMIDITY_INCLUDE_DIR}" "${WILDMIDI_INCLUDE_DIR}" "${OPLSYNTH_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" )
if (WIN32)
@ -48,6 +65,9 @@ add_library( zmusic STATIC
midisources/midisource_smf.cpp
midisources/midisource_hmi.cpp
midisources/midisource_xmi.cpp
decoder/sounddecoder.cpp
decoder/sndfile_decoder.cpp
decoder/mpg123_decoder.cpp
${PLAT_WIN32_SOURCES}
)
target_link_libraries( zmusic )

View File

@ -31,9 +31,10 @@
**
*/
#include "mpg123_decoder.h"
#include <algorithm>
#include <stdio.h>
#include "zmusic/mpg123_decoder.h"
#include "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_MPG123
@ -61,7 +62,8 @@ bool IsMPG123Present()
if (!done)
{
done = true;
cached_result = MPG123Module.Load({NicePath("$PROGDIR/" MPG123LIB), MPG123LIB});
auto abspath = module_progdir + "/" MPG123LIB;
cached_result = MPG123Module.Load({abspath.c_str(), MPG123LIB});
}
return cached_result;
#endif
@ -77,24 +79,24 @@ off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence)
if(whence == SEEK_CUR)
{
if(offset < 0 && reader.Tell()+offset < 0)
if(offset < 0 && reader->tell()+offset < 0)
return -1;
}
else if(whence == SEEK_END)
{
if(offset < 0 && reader.GetLength()+offset < 0)
if(offset < 0 && reader->filelength() + offset < 0)
return -1;
}
if(reader.Seek(offset, (FileReader::ESeek)whence) != 0)
if(reader->seek(offset, whence) != 0)
return -1;
return (off_t)reader.Tell();
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);
return (ssize_t)reader->read(buffer, (long)bytes);
}
@ -106,9 +108,11 @@ MPG123Decoder::~MPG123Decoder()
mpg123_delete(MPG123);
MPG123 = 0;
}
if (Reader) Reader->close();
Reader = nullptr;
}
bool MPG123Decoder::open(FileReader &reader)
bool MPG123Decoder::open(MusicIO::FileInterface *reader)
{
if(!inited)
{
@ -117,7 +121,7 @@ bool MPG123Decoder::open(FileReader &reader)
inited = true;
}
Reader = std::move(reader);
Reader = reader;
{
MPG123 = mpg123_new(NULL, NULL);
@ -144,7 +148,7 @@ bool MPG123Decoder::open(FileReader &reader)
MPG123 = 0;
}
reader = std::move(Reader); // need to give it back.
Reader = nullptr; // need to give it back.
return false;
}
@ -214,7 +218,7 @@ bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart)
mpg123_delete(MPG123);
MPG123 = 0;
}
Reader.Seek(0, FileReader::SeekSet);
Reader->seek(0, SEEK_SET);
// Do not call open with our own reader variable, that would be catastrophic.
auto reader = std::move(Reader);
return open(reader);

View File

@ -30,10 +30,10 @@
**---------------------------------------------------------------------------
**
*/
#include "sndfile_decoder.h"
#include "templates.h"
#include <algorithm>
#include "zmusic/sndfile_decoder.h"
#include "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_SNDFILE
@ -61,7 +61,8 @@ bool IsSndFilePresent()
if (!done)
{
done = true;
cached_result = SndFileModule.Load({NicePath("$PROGDIR/" SNDFILELIB), SNDFILELIB});
auto abspath = module_progdir + "/" SNDFILELIB;
cached_result = SndFileModule.Load({abspath.c_str(), SNDFILELIB});
}
return cached_result;
#endif
@ -71,22 +72,22 @@ bool IsSndFilePresent()
sf_count_t SndFileDecoder::file_get_filelen(void *user_data)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.GetLength();
return reader->filelength();
}
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)
if(reader->seek((long)offset, whence) != 0)
return -1;
return reader.Tell();
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);
return reader->read(ptr, (long)count);
}
sf_count_t SndFileDecoder::file_write(const void *ptr, sf_count_t count, void *user_data)
@ -97,7 +98,7 @@ sf_count_t SndFileDecoder::file_write(const void *ptr, sf_count_t count, void *u
sf_count_t SndFileDecoder::file_tell(void *user_data)
{
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.Tell();
return reader->tell();
}
@ -106,15 +107,18 @@ SndFileDecoder::~SndFileDecoder()
if(SndFile)
sf_close(SndFile);
SndFile = 0;
if (Reader) Reader->close();
Reader = nullptr;
}
bool SndFileDecoder::open(FileReader &reader)
bool SndFileDecoder::open(MusicIO::FileInterface *reader)
{
if (!IsSndFilePresent()) return false;
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
Reader = std::move(reader);
Reader = reader;
SndInfo.format = 0;
SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this);
if (SndFile)
@ -125,7 +129,7 @@ bool SndFileDecoder::open(FileReader &reader)
sf_close(SndFile);
SndFile = 0;
}
reader = std::move(Reader); // need to give it back.
Reader = nullptr; // need to give it back.
return false;
}
@ -156,30 +160,30 @@ size_t SndFileDecoder::read(char *buffer, size_t bytes)
// could be more.
while(total < frames)
{
size_t todo = MIN<size_t>(frames-total, 64/SndInfo.channels);
size_t todo = std::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));
*out++ = (short)std::max(std::min(tmp[i] * 32767.f, 32767.f), -32768.f);
total += got;
}
return total * SndInfo.channels * 2;
}
TArray<uint8_t> SndFileDecoder::readAll()
std::vector<uint8_t> SndFileDecoder::readAll()
{
if(SndInfo.frames <= 0)
return SoundDecoder::readAll();
int framesize = 2 * SndInfo.channels;
TArray<uint8_t> output;
std::vector<uint8_t> output;
output.Resize((unsigned)(SndInfo.frames * framesize));
size_t got = read((char*)&output[0], output.Size());
output.Resize((unsigned)got);
output.resize((unsigned)(SndInfo.frames * framesize));
size_t got = read((char*)&output[0], output.size());
output.resize((unsigned)got);
return output;
}

View File

@ -0,0 +1,81 @@
/*
** sounddecoder.cpp
** baseclass for sound format decoders
**
**---------------------------------------------------------------------------
** Copyright 2008-2019 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 "zmusic/sndfile_decoder.h"
#include "zmusic/mpg123_decoder.h"
SoundDecoder *SoundDecoder::CreateDecoder(MusicIO::FileInterface *reader)
{
SoundDecoder *decoder = NULL;
auto pos = reader->tell();
#ifdef HAVE_SNDFILE
decoder = new SndFileDecoder;
if (decoder->open(reader))
return decoder;
reader->seek(pos, SEEK_SET);
delete decoder;
decoder = NULL;
#endif
#ifdef HAVE_MPG123
decoder = new MPG123Decoder;
if (decoder->open(reader))
return decoder;
reader->seek(pos, SEEK_SET);
delete decoder;
decoder = NULL;
#endif
return decoder;
}
// Default readAll implementation, for decoders that can't do anything better
std::vector<uint8_t> SoundDecoder::readAll()
{
std::vector<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

@ -40,6 +40,7 @@
#include <dlfcn.h>
#endif
#ifndef _WIN32
#define LoadLibraryA(x) dlopen((x), RTLD_LAZY)
#define GetProcAddress(a,b) dlsym((a),(b))
@ -98,3 +99,10 @@ void *FModule::GetSym(const char* name)
{
return (void *)GetProcAddress((HMODULE)handle, name);
}
std::string module_progdir("."); // current program directory used to look up dynamic libraries. Default to something harmless in case the user didn't set it.
void FModule_SetProgDir(const char* progdir)
{
module_progdir = progdir;
}

View File

@ -34,6 +34,7 @@
#pragma once
#include <assert.h>
#include <string>
#include <initializer_list>
/* FModule Run Time Library Loader
@ -227,3 +228,6 @@ public:
operator Proto() const { return Sym; }
explicit operator bool() const { return Sym != nullptr; }
};
void FModule_SetProgDir(const char* progdir);
extern std::string module_progdir;

View File

@ -41,10 +41,6 @@
// FluidSynth implementation of a MIDI device -------------------------------
#if !defined DYN_FLUIDSYNTH && defined _WIN32
#define DYN_FLUIDSYNTH 1 // On Windows this is the only supported way to link to FluidSynth.
#endif
#if !defined DYN_FLUIDSYNTH
#include <fluidsynth.h>
#else

View File

@ -1,8 +1,7 @@
#ifndef MPG123_DECODER_H
#define MPG123_DECODER_H
#include "i_soundinternal.h"
#include "files.h"
#include "sounddecoder.h"
#ifdef HAVE_MPG123
@ -19,25 +18,25 @@ typedef ptrdiff_t ssize_t;
struct MPG123Decoder : public SoundDecoder
{
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual void getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) override;
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();
virtual size_t read(char* buffer, size_t bytes) override;
virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) override;
virtual size_t getSampleOffset() override;
virtual size_t getSampleLength() override;
MPG123Decoder() : MPG123(0) { }
virtual ~MPG123Decoder();
MPG123Decoder() : MPG123(0) { }
virtual ~MPG123Decoder();
protected:
virtual bool open(FileReader &reader);
virtual bool open(MusicIO::FileInterface *reader) override;
private:
mpg123_handle *MPG123;
bool Done;
FileReader Reader;
static off_t file_lseek(void *handle, off_t offset, int whence);
MusicIO::FileInterface* 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

View File

@ -1,8 +1,7 @@
#ifndef SNDFILE_DECODER_H
#define SNDFILE_DECODER_H
#include "i_soundinternal.h"
#include "files.h"
#include "sounddecoder.h"
#ifdef HAVE_SNDFILE
@ -14,25 +13,25 @@
struct SndFileDecoder : public SoundDecoder
{
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
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();
virtual size_t read(char *buffer, size_t bytes) override;
virtual std::vector<uint8_t> readAll() override;
virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) override;
virtual size_t getSampleOffset() override;
virtual size_t getSampleLength() override;
SndFileDecoder() : SndFile(0) { }
virtual ~SndFileDecoder();
protected:
virtual bool open(FileReader &reader);
virtual bool open(MusicIO::FileInterface *reader) override;
private:
SNDFILE *SndFile;
SF_INFO SndInfo;
FileReader Reader;
MusicIO::FileInterface* 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);

View File

@ -0,0 +1,40 @@
#pragma once
#include "../../music_common/fileio.h"
#include <vector>
enum SampleType
{
SampleType_UInt8,
SampleType_Int16
};
enum ChannelConfig
{
ChannelConfig_Mono,
ChannelConfig_Stereo
};
struct SoundDecoder
{
static SoundDecoder* CreateDecoder(MusicIO::FileInterface* reader);
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
virtual size_t read(char *buffer, size_t bytes) = 0;
virtual std::vector<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; }
virtual bool open(MusicIO::FileInterface* reader) = 0;
SoundDecoder() { }
virtual ~SoundDecoder() { }
protected:
friend class SoundRenderer;
// Make non-copyable
SoundDecoder(const SoundDecoder &rhs) = delete;
SoundDecoder& operator=(const SoundDecoder &rhs) = delete;
};

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

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

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

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