diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b21aa91..ac9155561 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/libraries/music_common/fileio.h b/libraries/music_common/fileio.h index 74d6672fa..849c67751 100644 --- a/libraries/music_common/fileio.h +++ b/libraries/music_common/fileio.h @@ -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 - { - delete this; - } - +protected: + MemoryReader() {} }; +//========================================================================== +// +// Inplementation of the FileInterface for an std::vector owned by the reader +// +//========================================================================== + +struct VectorReader : public MemoryReader +{ + std::vector mVector; + + template + VectorReader(getFunc getter) // read contents to a buffer and return a reader to it + { + getter(mVector); + mData = mVector.data(); + mLength = (long)mVector.size(); + } + + +}; + + //========================================================================== // // The follpwing two functions are needed to allow using UTF-8 in the file interface. diff --git a/libraries/wildmidi/file_io.cpp b/libraries/wildmidi/file_io.cpp index ac7dfac15..b0c820b20 100644 --- a/libraries/wildmidi/file_io.cpp +++ b/libraries/wildmidi/file_io.cpp @@ -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) { diff --git a/libraries/zmusic/CMakeLists.txt b/libraries/zmusic/CMakeLists.txt index d57e48cc7..3f7cd6c94 100644 --- a/libraries/zmusic/CMakeLists.txt +++ b/libraries/zmusic/CMakeLists.txt @@ -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 ) diff --git a/src/sound/backend/mpg123_decoder.cpp b/libraries/zmusic/decoder/mpg123_decoder.cpp similarity index 89% rename from src/sound/backend/mpg123_decoder.cpp rename to libraries/zmusic/decoder/mpg123_decoder.cpp index ed13ea20d..cadcf4890 100644 --- a/src/sound/backend/mpg123_decoder.cpp +++ b/libraries/zmusic/decoder/mpg123_decoder.cpp @@ -31,9 +31,10 @@ ** */ -#include "mpg123_decoder.h" +#include +#include +#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(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); diff --git a/src/sound/backend/mpgload.h b/libraries/zmusic/decoder/mpgload.h similarity index 100% rename from src/sound/backend/mpgload.h rename to libraries/zmusic/decoder/mpgload.h diff --git a/src/sound/backend/sndfile_decoder.cpp b/libraries/zmusic/decoder/sndfile_decoder.cpp similarity index 84% rename from src/sound/backend/sndfile_decoder.cpp rename to libraries/zmusic/decoder/sndfile_decoder.cpp index 28bf11374..9269d47d7 100644 --- a/src/sound/backend/sndfile_decoder.cpp +++ b/libraries/zmusic/decoder/sndfile_decoder.cpp @@ -30,10 +30,10 @@ **--------------------------------------------------------------------------- ** */ -#include "sndfile_decoder.h" -#include "templates.h" + +#include +#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(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(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(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(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(frames-total, 64/SndInfo.channels); + size_t todo = std::min(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 SndFileDecoder::readAll() +std::vector SndFileDecoder::readAll() { if(SndInfo.frames <= 0) return SoundDecoder::readAll(); int framesize = 2 * SndInfo.channels; - TArray output; + std::vector 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; } diff --git a/src/sound/backend/sndload.h b/libraries/zmusic/decoder/sndload.h similarity index 100% rename from src/sound/backend/sndload.h rename to libraries/zmusic/decoder/sndload.h diff --git a/libraries/zmusic/decoder/sounddecoder.cpp b/libraries/zmusic/decoder/sounddecoder.cpp new file mode 100644 index 000000000..19bba22a0 --- /dev/null +++ b/libraries/zmusic/decoder/sounddecoder.cpp @@ -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 SoundDecoder::readAll() +{ + std::vector 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; +} diff --git a/libraries/zmusic/i_module.cpp b/libraries/zmusic/i_module.cpp index f22a55791..5b5153f1d 100644 --- a/libraries/zmusic/i_module.cpp +++ b/libraries/zmusic/i_module.cpp @@ -40,6 +40,7 @@ #include #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; +} diff --git a/libraries/zmusic/i_module.h b/libraries/zmusic/i_module.h index 9690d8c2f..26fdd63a6 100644 --- a/libraries/zmusic/i_module.h +++ b/libraries/zmusic/i_module.h @@ -34,6 +34,7 @@ #pragma once #include +#include #include /* 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; diff --git a/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp b/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp index d8962d69f..778f8a231 100644 --- a/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp @@ -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 #else diff --git a/src/sound/thirdparty/mpg123.h b/libraries/zmusic/thirdparty/mpg123.h similarity index 100% rename from src/sound/thirdparty/mpg123.h rename to libraries/zmusic/thirdparty/mpg123.h diff --git a/src/sound/thirdparty/sndfile.h b/libraries/zmusic/thirdparty/sndfile.h similarity index 100% rename from src/sound/thirdparty/sndfile.h rename to libraries/zmusic/thirdparty/sndfile.h diff --git a/src/sound/backend/mpg123_decoder.h b/libraries/zmusic/zmusic/mpg123_decoder.h similarity index 50% rename from src/sound/backend/mpg123_decoder.h rename to libraries/zmusic/zmusic/mpg123_decoder.h index dad0f11fe..abd4d59e4 100644 --- a/src/sound/backend/mpg123_decoder.h +++ b/libraries/zmusic/zmusic/mpg123_decoder.h @@ -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 diff --git a/src/sound/backend/sndfile_decoder.h b/libraries/zmusic/zmusic/sndfile_decoder.h similarity index 73% rename from src/sound/backend/sndfile_decoder.h rename to libraries/zmusic/zmusic/sndfile_decoder.h index cbbcf8714..a931ab349 100644 --- a/src/sound/backend/sndfile_decoder.h +++ b/libraries/zmusic/zmusic/sndfile_decoder.h @@ -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 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 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); diff --git a/libraries/zmusic/zmusic/sounddecoder.h b/libraries/zmusic/zmusic/sounddecoder.h new file mode 100644 index 000000000..433189195 --- /dev/null +++ b/libraries/zmusic/zmusic/sounddecoder.h @@ -0,0 +1,40 @@ +#pragma once + +#include "../../music_common/fileio.h" +#include + +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 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; +}; + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9563e58a0..9c26e33ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/sound/backend/i_sound.cpp b/src/sound/backend/i_sound.cpp index b36ab360a..df5d51749 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -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 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 SoundDecoder::readAll() -{ - TArray 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; -} diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h index bc18f9cda..775d47a3c 100644 --- a/src/sound/backend/i_sound.h +++ b/src/sound/backend/i_sound.h @@ -88,7 +88,7 @@ class MIDIDevice; struct FSoundLoadBuffer { - TArray mBuffer; + std::vector 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; diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index cb0351f3f..d4df22733 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -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 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 { diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index b9893c33d..d5265cdbf 100644 --- a/src/sound/backend/oalsound.cpp +++ b/src/sound/backend/oalsound.cpp @@ -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 OpenALSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, std::pair 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 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 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 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 OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int return std::make_pair(retval, true); } - TArray 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 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 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 OpenALSoundRenderer::LoadSoundBuffered(FSoundLoadBu return std::make_pair(retval, true); } - TArray &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 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)); diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index 4fa282d05..c1cbcc0f8 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -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 &newdata); +static bool ungzip(uint8_t *data, int size, std::vector &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 &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& 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 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 &newdata) +static bool ungzip(uint8_t *data, int complen, std::vector &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 &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) { diff --git a/src/sound/music/i_music.h b/src/sound/music/i_music.h index 26da5f077..4258031d9 100644 --- a/src/sound/music/i_music.h +++ b/src/sound/music/i_music.h @@ -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 -------------------- diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index c792d88db..68e537561 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -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); diff --git a/src/sound/music/i_soundfont.cpp b/src/sound/music/i_soundfont.cpp index feb378437..075d77a5f 100644 --- a/src/sound/music/i_soundfont.cpp +++ b/src/sound/music/i_soundfont.cpp @@ -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" diff --git a/src/sound/music/i_soundfont.h b/src/sound/music/i_soundfont.h index 5733d736e..629d04123 100644 --- a/src/sound/music/i_soundfont.h +++ b/src/sound/music/i_soundfont.h @@ -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); - } - -}; - //========================================================================== // // diff --git a/src/sound/musicformats/music_cd.cpp b/src/sound/musicformats/music_cd.cpp index 58f1d1685..734549965 100644 --- a/src/sound/musicformats/music_cd.cpp +++ b/src/sound/musicformats/music_cd.cpp @@ -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; diff --git a/src/sound/musicformats/music_dumb.cpp b/src/sound/musicformats/music_dumb.cpp index c7ce8a716..ff1a59808 100644 --- a/src/sound/musicformats/music_dumb.cpp +++ b/src/sound/musicformats/music_dumb.cpp @@ -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; diff --git a/src/sound/musicformats/music_gme.cpp b/src/sound/musicformats/music_gme.cpp index 11e4000ce..f196f761a 100644 --- a/src/sound/musicformats/music_gme.cpp +++ b/src/sound/musicformats/music_gme.cpp @@ -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) { diff --git a/src/sound/musicformats/music_libsndfile.cpp b/src/sound/musicformats/music_libsndfile.cpp index 7c7f505ad..4cc8ea7a2 100644 --- a/src/sound/musicformats/music_libsndfile.cpp +++ b/src/sound/musicformats/music_libsndfile.cpp @@ -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(loop_end, 0, sampleLength); - Reader = std::move(reader); Decoder = decoder; Channels = iChannels == ChannelConfig_Stereo? 2:1; } diff --git a/src/sound/s_music.cpp b/src/sound/s_music.cpp index b5309b916..2a579eb88 100644 --- a/src/sound/s_music.cpp +++ b/src/sound/s_music.cpp @@ -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"); + } } //========================================================================== diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index 6d3a9e404..12a3de41e 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -1532,7 +1532,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) std::pair snd; - if (pBuffer->mBuffer.Size() > 0) + if (pBuffer->mBuffer.size() > 0) { snd = GSnd->LoadSoundBuffered(pBuffer, true); } diff --git a/src/utility/filereadermusicinterface.h b/src/utility/filereadermusicinterface.h new file mode 100644 index 000000000..1de766590 --- /dev/null +++ b/src/utility/filereadermusicinterface.h @@ -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; + } +}; + diff --git a/src/utility/files.h b/src/utility/files.h index 854af172b..d363b51bf 100644 --- a/src/utility/files.h +++ b/src/utility/files.h @@ -328,4 +328,5 @@ public: TArray *GetBuffer() { return &mBuffer; } }; + #endif