- 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} DESTINATION ${INSTALL_DOCS_PATH}
COMPONENT "Documentation") 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( libraries/lzma )
add_subdirectory( tools ) add_subdirectory( tools )
add_subdirectory( libraries/dumb ) add_subdirectory( libraries/dumb )

View file

@ -50,16 +50,35 @@ namespace MusicIO
struct FileInterface struct FileInterface
{ {
std::string filename; std::string filename;
long length = -1;
// It's really too bad that the using code requires long instead of size_t. // It's really too bad that the using code requires long instead of size_t.
// Fortunately 2GB files are unlikely to come by here. // Fortunately 2GB files are unlikely to come by here.
protected:
//
virtual ~FileInterface() {} virtual ~FileInterface() {}
public:
virtual char* gets(char* buff, int n) = 0; virtual char* gets(char* buff, int n) = 0;
virtual long read(void* buff, int32_t size, int32_t nitems) = 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); } long read(void* buff, int32_t size) { return read(buff, 1, size); }
virtual long seek(long offset, int whence) = 0; virtual long seek(long offset, int whence) = 0;
virtual long tell() = 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; if (!f) return 0;
return ftell(f); return ftell(f);
} }
void close() override
{
if (f) fclose(f);
delete this;
}
}; };
@ -113,7 +127,7 @@ struct StdioFileReader : public FileInterface
struct MemoryReader : public FileInterface struct MemoryReader : public FileInterface
{ {
const uint8_t *mData; const uint8_t *mData;
const long mLength; long mLength;
long mPos; long mPos;
MemoryReader(const uint8_t *data, long length) MemoryReader(const uint8_t *data, long length)
@ -180,13 +194,32 @@ struct MemoryReader : public FileInterface
{ {
return mPos; return mPos;
} }
void close() override protected:
{ MemoryReader() {}
delete this;
}
}; };
//==========================================================================
//
// 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
{
getter(mVector);
mData = mVector.data();
mLength = (long)mVector.size();
}
};
//========================================================================== //==========================================================================
// //
// The follpwing two functions are needed to allow using UTF-8 in the file interface. // 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; return NULL;
} }
auto fsize = (fp->seek(0, SEEK_END), fp->tell()); auto fsize = fp->filelength();
if (fsize > WM_MAXFILESIZE) if (fsize > WM_MAXFILESIZE)
{ {

View file

@ -9,8 +9,6 @@ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}")
include( CheckFunctionExists ) include( CheckFunctionExists )
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON )
CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS ) CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS )
if( NOT STRICMP_EXISTS ) if( NOT STRICMP_EXISTS )
add_definitions( -Dstricmp=strcasecmp ) add_definitions( -Dstricmp=strcasecmp )
@ -21,6 +19,25 @@ if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp ) add_definitions( -Dstrnicmp=strncasecmp )
endif() 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}" ) 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) if (WIN32)
@ -48,6 +65,9 @@ add_library( zmusic STATIC
midisources/midisource_smf.cpp midisources/midisource_smf.cpp
midisources/midisource_hmi.cpp midisources/midisource_hmi.cpp
midisources/midisource_xmi.cpp midisources/midisource_xmi.cpp
decoder/sounddecoder.cpp
decoder/sndfile_decoder.cpp
decoder/mpg123_decoder.cpp
${PLAT_WIN32_SOURCES} ${PLAT_WIN32_SOURCES}
) )
target_link_libraries( zmusic ) 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 "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_MPG123 #ifdef HAVE_MPG123
@ -61,7 +62,8 @@ bool IsMPG123Present()
if (!done) if (!done)
{ {
done = true; 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; return cached_result;
#endif #endif
@ -77,24 +79,24 @@ off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence)
if(whence == SEEK_CUR) if(whence == SEEK_CUR)
{ {
if(offset < 0 && reader.Tell()+offset < 0) if(offset < 0 && reader->tell()+offset < 0)
return -1; return -1;
} }
else if(whence == SEEK_END) else if(whence == SEEK_END)
{ {
if(offset < 0 && reader.GetLength()+offset < 0) if(offset < 0 && reader->filelength() + offset < 0)
return -1; return -1;
} }
if(reader.Seek(offset, (FileReader::ESeek)whence) != 0) if(reader->seek(offset, whence) != 0)
return -1; return -1;
return (off_t)reader.Tell(); return (off_t)reader->tell();
} }
ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes) ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes)
{ {
auto &reader = reinterpret_cast<MPG123Decoder*>(handle)->Reader; 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_delete(MPG123);
MPG123 = 0; MPG123 = 0;
} }
if (Reader) Reader->close();
Reader = nullptr;
} }
bool MPG123Decoder::open(FileReader &reader) bool MPG123Decoder::open(MusicIO::FileInterface *reader)
{ {
if(!inited) if(!inited)
{ {
@ -117,7 +121,7 @@ bool MPG123Decoder::open(FileReader &reader)
inited = true; inited = true;
} }
Reader = std::move(reader); Reader = reader;
{ {
MPG123 = mpg123_new(NULL, NULL); MPG123 = mpg123_new(NULL, NULL);
@ -144,7 +148,7 @@ bool MPG123Decoder::open(FileReader &reader)
MPG123 = 0; MPG123 = 0;
} }
reader = std::move(Reader); // need to give it back. Reader = nullptr; // need to give it back.
return false; return false;
} }
@ -214,7 +218,7 @@ bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart)
mpg123_delete(MPG123); mpg123_delete(MPG123);
MPG123 = 0; 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. // Do not call open with our own reader variable, that would be catastrophic.
auto reader = std::move(Reader); auto reader = std::move(Reader);
return open(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 "i_module.h"
#include "cmdlib.h"
#ifdef HAVE_SNDFILE #ifdef HAVE_SNDFILE
@ -61,7 +61,8 @@ bool IsSndFilePresent()
if (!done) if (!done)
{ {
done = true; 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; return cached_result;
#endif #endif
@ -71,22 +72,22 @@ bool IsSndFilePresent()
sf_count_t SndFileDecoder::file_get_filelen(void *user_data) sf_count_t SndFileDecoder::file_get_filelen(void *user_data)
{ {
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader; 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) sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data)
{ {
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader; 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 -1;
return reader.Tell(); return reader->tell();
} }
sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data) sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data)
{ {
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader; 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) 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) sf_count_t SndFileDecoder::file_tell(void *user_data)
{ {
auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader; auto &reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader;
return reader.Tell(); return reader->tell();
} }
@ -106,15 +107,18 @@ SndFileDecoder::~SndFileDecoder()
if(SndFile) if(SndFile)
sf_close(SndFile); sf_close(SndFile);
SndFile = 0; SndFile = 0;
if (Reader) Reader->close();
Reader = nullptr;
} }
bool SndFileDecoder::open(FileReader &reader) bool SndFileDecoder::open(MusicIO::FileInterface *reader)
{ {
if (!IsSndFilePresent()) return false; if (!IsSndFilePresent()) return false;
SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell }; SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell };
Reader = std::move(reader); Reader = reader;
SndInfo.format = 0; SndInfo.format = 0;
SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this);
if (SndFile) if (SndFile)
@ -125,7 +129,7 @@ bool SndFileDecoder::open(FileReader &reader)
sf_close(SndFile); sf_close(SndFile);
SndFile = 0; SndFile = 0;
} }
reader = std::move(Reader); // need to give it back. Reader = nullptr; // need to give it back.
return false; return false;
} }
@ -156,30 +160,30 @@ size_t SndFileDecoder::read(char *buffer, size_t bytes)
// could be more. // could be more.
while(total < frames) 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]; float tmp[64];
size_t got = (size_t)sf_readf_float(SndFile, tmp, todo); size_t got = (size_t)sf_readf_float(SndFile, tmp, todo);
if(got < todo) frames = total + got; if(got < todo) frames = total + got;
for(size_t i = 0;i < got*SndInfo.channels;i++) 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; total += got;
} }
return total * SndInfo.channels * 2; return total * SndInfo.channels * 2;
} }
TArray<uint8_t> SndFileDecoder::readAll() std::vector<uint8_t> SndFileDecoder::readAll()
{ {
if(SndInfo.frames <= 0) if(SndInfo.frames <= 0)
return SoundDecoder::readAll(); return SoundDecoder::readAll();
int framesize = 2 * SndInfo.channels; int framesize = 2 * SndInfo.channels;
TArray<uint8_t> output; std::vector<uint8_t> output;
output.Resize((unsigned)(SndInfo.frames * framesize)); output.resize((unsigned)(SndInfo.frames * framesize));
size_t got = read((char*)&output[0], output.Size()); size_t got = read((char*)&output[0], output.size());
output.Resize((unsigned)got); output.resize((unsigned)got);
return output; 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> #include <dlfcn.h>
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
#define LoadLibraryA(x) dlopen((x), RTLD_LAZY) #define LoadLibraryA(x) dlopen((x), RTLD_LAZY)
#define GetProcAddress(a,b) dlsym((a),(b)) #define GetProcAddress(a,b) dlsym((a),(b))
@ -98,3 +99,10 @@ void *FModule::GetSym(const char* name)
{ {
return (void *)GetProcAddress((HMODULE)handle, 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 #pragma once
#include <assert.h> #include <assert.h>
#include <string>
#include <initializer_list> #include <initializer_list>
/* FModule Run Time Library Loader /* FModule Run Time Library Loader
@ -227,3 +228,6 @@ public:
operator Proto() const { return Sym; } operator Proto() const { return Sym; }
explicit operator bool() const { return Sym != nullptr; } 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 ------------------------------- // 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 #if !defined DYN_FLUIDSYNTH
#include <fluidsynth.h> #include <fluidsynth.h>
#else #else

View file

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

View file

@ -1,8 +1,7 @@
#ifndef SNDFILE_DECODER_H #ifndef SNDFILE_DECODER_H
#define SNDFILE_DECODER_H #define SNDFILE_DECODER_H
#include "i_soundinternal.h" #include "sounddecoder.h"
#include "files.h"
#ifdef HAVE_SNDFILE #ifdef HAVE_SNDFILE
@ -14,25 +13,25 @@
struct SndFileDecoder : public SoundDecoder 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 size_t read(char *buffer, size_t bytes) override;
virtual TArray<uint8_t> readAll(); virtual std::vector<uint8_t> readAll() override;
virtual bool seek(size_t ms_offset, bool ms, bool mayrestart); virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) override;
virtual size_t getSampleOffset(); virtual size_t getSampleOffset() override;
virtual size_t getSampleLength(); virtual size_t getSampleLength() override;
SndFileDecoder() : SndFile(0) { } SndFileDecoder() : SndFile(0) { }
virtual ~SndFileDecoder(); virtual ~SndFileDecoder();
protected: protected:
virtual bool open(FileReader &reader); virtual bool open(MusicIO::FileInterface *reader) override;
private: private:
SNDFILE *SndFile; SNDFILE *SndFile;
SF_INFO SndInfo; SF_INFO SndInfo;
FileReader Reader; MusicIO::FileInterface* Reader;
static sf_count_t file_get_filelen(void *user_data); 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_seek(sf_count_t offset, int whence, void *user_data);
static sf_count_t file_read(void *ptr, sf_count_t count, void *user_data); static sf_count_t file_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()
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 ) if( APPLE )
option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON )
endif() endif()
@ -623,24 +619,6 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) 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 ) option( SEND_ANON_STATS "Enable sending of anonymous hardware statistics" ON )
if( NOT SEND_ANON_STATS ) if( NOT SEND_ANON_STATS )
@ -832,10 +810,8 @@ set( FASTMATH_SOURCES
rendering/swrenderer/r_all.cpp rendering/swrenderer/r_all.cpp
rendering/swrenderer/r_swscene.cpp rendering/swrenderer/r_swscene.cpp
rendering/polyrenderer/poly_all.cpp rendering/polyrenderer/poly_all.cpp
sound/backend/mpg123_decoder.cpp
sound/music/music_midi_base.cpp sound/music/music_midi_base.cpp
sound/backend/oalsound.cpp sound/backend/oalsound.cpp
sound/backend/sndfile_decoder.cpp
gamedata/textures/hires/hqnx/init.cpp gamedata/textures/hires/hqnx/init.cpp
gamedata/textures/hires/hqnx/hq2x.cpp gamedata/textures/hires/hqnx/hq2x.cpp
gamedata/textures/hires/hqnx/hq3x.cpp gamedata/textures/hires/hqnx/hq3x.cpp

View file

@ -39,8 +39,10 @@
#include "oalsound.h" #include "oalsound.h"
#include "mpg123_decoder.h" #include "i_module.h"
#include "sndfile_decoder.h" #include "cmdlib.h"
#include "zmusic/mpg123_decoder.h"
#include "zmusic/sndfile_decoder.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "i_music.h" #include "i_music.h"
@ -247,6 +249,7 @@ public:
void I_InitSound () void I_InitSound ()
{ {
FModule_SetProgDir(progdir);
/* Get command line options: */ /* Get command line options: */
nosound = !!Args->CheckParm ("-nosound"); nosound = !!Args->CheckParm ("-nosound");
nosfx = !!Args->CheckParm ("-nosfx"); nosfx = !!Args->CheckParm ("-nosfx");
@ -351,16 +354,20 @@ FString SoundRenderer::GatherStats ()
short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype) short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype)
{ {
FileReader reader;
short *samples = (short*)calloc(1, outlen); short *samples = (short*)calloc(1, outlen);
ChannelConfig chans; ChannelConfig chans;
SampleType type; SampleType type;
int srate; 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); SoundDecoder *decoder = SoundDecoder::CreateDecoder(reader);
if(!decoder) return samples; if (!decoder)
{
reader->close();
return samples;
}
decoder->getInfo(&srate, &chans, &type); decoder->getInfo(&srate, &chans, &type);
if(chans != ChannelConfig_Mono || type != SampleType_Int16) if(chans != ChannelConfig_Mono || type != SampleType_Int16)
@ -545,46 +552,3 @@ std::pair<SoundHandle, bool> SoundRenderer::LoadSoundBuffered(FSoundLoadBuffer *
return std::make_pair(retval, true); 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 struct FSoundLoadBuffer
{ {
TArray<uint8_t> mBuffer; std::vector<uint8_t> mBuffer;
uint32_t loop_start; uint32_t loop_start;
uint32_t loop_end; uint32_t loop_end;
ChannelConfig chans; ChannelConfig chans;
@ -169,8 +169,6 @@ public:
virtual short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type); virtual short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type);
virtual void DrawWaveDebug(int mode); virtual void DrawWaveDebug(int mode);
static SoundDecoder *CreateDecoder(FileReader &reader);
}; };
extern SoundRenderer *GSnd; extern SoundRenderer *GSnd;

View file

@ -6,6 +6,7 @@
#include "doomtype.h" #include "doomtype.h"
#include "vectors.h" #include "vectors.h"
#include "tarray.h" #include "tarray.h"
#include "../../libraries/music_common/fileio.h"
class FileReader; 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 *GetSampleTypeName(enum SampleType type);
const char *GetChannelConfigName(enum ChannelConfig chan); 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; class MusInfo;
struct MusPlayingInfo struct MusPlayingInfo
{ {

View file

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

View file

@ -47,7 +47,7 @@
#include "stats.h" #include "stats.h"
#include "vm.h" #include "vm.h"
#include "s_music.h" #include "s_music.h"
#include "i_soundfont.h" #include "filereadermusicinterface.h"
#include "../libraries/zmusic/midisources/midisource.h" #include "../libraries/zmusic/midisources/midisource.h"
#include "../libraries/dumb/include/dumb.h" #include "../libraries/dumb/include/dumb.h"
@ -83,7 +83,7 @@ EXTERN_CVAR (Int, snd_mididevice)
static bool MusicDown = true; 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; MusInfo *currSong;
int nomusic = 0; int nomusic = 0;
@ -205,8 +205,6 @@ MusInfo::~MusInfo ()
void MusInfo::Start(bool loop, float rel_vol, int subsong) void MusInfo::Start(bool loop, float rel_vol, int subsong)
{ {
if (nomusic) return;
if (rel_vol > 0.f) if (rel_vol > 0.f)
{ {
float factor = relative_volume / saved_relative_volume; 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) switch (miditype)
{ {
case MIDI_MUS: case MIDI_MUS:
return new MUSSong2(data.Data(), data.Size()); return new MUSSong2(data, length);
case MIDI_MIDI: case MIDI_MIDI:
return new MIDISong2(data.Data(), data.Size()); return new MIDISong2(data, length);
case MIDI_HMI: case MIDI_HMI:
return new HMISong(data.Data(), data.Size()); return new HMISong(data, length);
case MIDI_XMI: case MIDI_XMI:
return new XMISong(data.Data(), data.Size()); return new XMISong(data, length);
default: default:
return nullptr; 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; MusInfo *info = nullptr;
StreamSource *streamsource = nullptr; StreamSource *streamsource = nullptr;
const char *fmt; const char *fmt;
uint32_t id[32/4]; uint32_t id[32/4];
if (nomusic) if(reader->read(id, 32) != 32 || reader->seek(-32, FileReader::SeekCur) != 0)
{ {
reader->close();
return nullptr; return nullptr;
} }
try
if(reader.Read(id, 32) != 32 || reader.Seek(-32, FileReader::SeekCur) != 0)
{ {
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.
// Check for gzip compression. Some formats are expected to have players if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID)
// 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)
{ {
bool res = false; // swap out the reader with one that reads the decompressed content.
auto len = reader.GetLength(); auto zreader = new MusicIO::VectorReader([reader](std::vector<uint8_t>& array)
uint8_t *gzipped = new uint8_t[len]; {
if (reader.Read(gzipped, len) == len) 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)); EMidiDevice devtype = device == nullptr ? MDEV_DEFAULT : (EMidiDevice)device->device;
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;
#ifndef _WIN32 #ifndef _WIN32
// non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS
if (devtype == MDEV_MMAPI) if (devtype == MDEV_MMAPI)
devtype = MDEV_SNDSYS; devtype = MDEV_SNDSYS;
#endif #endif
MIDIStreamer *streamer = CreateMIDIStreamer(devtype, device != nullptr? device->args.GetChars() : ""); MIDIStreamer* streamer = CreateMIDIStreamer(devtype, device != nullptr ? device->args.GetChars() : "");
if (streamer == nullptr) if (streamer == nullptr)
{ {
delete source; delete source;
return nullptr; reader->close();
return nullptr;
}
streamer->SetMIDISource(source);
info = streamer;
} }
streamer->SetMIDISource(source);
info = streamer;
}
// Check for various raw OPL formats // Check for various raw OPL formats
else if ( 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('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('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 (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)
{ {
// If this fails we have no more valid data - but it couldn't be a CDDA file anyway. OPL_SetupConfig(&oplConfig, device->args.GetChars(), false);
return nullptr; 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) catch (...)
{
// 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 ())
{ {
delete info; // Make sure the reader is closed if this function abnormally terminates
info = nullptr; if (reader) reader->close();
throw;
} }
return info; 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 *max = data + complen - 8;
const uint8_t *compstart = data + 10; const uint8_t *compstart = data + 10;
@ -597,7 +593,7 @@ static bool ungzip(uint8_t *data, int complen, TArray<uint8_t> &newdata)
// Decompress // Decompress
isize = LittleLong(*(uint32_t *)(data + complen - 4)); isize = LittleLong(*(uint32_t *)(data + complen - 4));
newdata.Resize(isize); newdata.resize(isize);
stream.next_in = (Bytef *)compstart; stream.next_in = (Bytef *)compstart;
stream.avail_in = (uInt)(max - compstart); stream.avail_in = (uInt)(max - compstart);
@ -705,6 +701,7 @@ static MIDISource *GetMIDISource(const char *fn)
} }
auto wlump = Wads.OpenLumpReader(lump); auto wlump = Wads.OpenLumpReader(lump);
uint32_t id[32 / 4]; uint32_t id[32 / 4];
if (wlump.Read(id, 32) != 32 || wlump.Seek(-32, FileReader::SeekCur) != 0) 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()); Printf("Unable to read lump %s\n", src.GetChars());
return nullptr; return nullptr;
} }
auto type = IdentifyMIDIType(id, 32); 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) if (source == nullptr)
{ {

View file

@ -55,7 +55,7 @@ void I_SetMusicVolume (double volume);
// Registers a song handle to song data. // Registers a song handle to song data.
class MusInfo; class MusInfo;
struct MidiDeviceSetting; 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); MusInfo *I_RegisterCDSong (int track, int cdid = 0);
// The base music class. Everything is derived from this -------------------- // The base music class. Everything is derived from this --------------------

View file

@ -134,7 +134,7 @@ protected:
class CDDAFile : public CDSong class CDDAFile : public CDSong
{ {
public: public:
CDDAFile (FileReader &reader); CDDAFile (MusicIO::FileInterface *reader);
}; };
// Data interface // Data interface
@ -154,7 +154,7 @@ class StreamSource;
StreamSource *MOD_OpenSong(MusicIO::FileInterface* reader, DumbConfig* config, int samplerate); 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* 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* XA_OpenSong(MusicIO::FileInterface* reader);
StreamSource* OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config); StreamSource* OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config);

View file

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

View file

@ -3,7 +3,7 @@
#include "doomtype.h" #include "doomtype.h"
#include "w_wad.h" #include "w_wad.h"
#include "files.h" #include "files.h"
#include "../libraries/music_common/fileio.h" #include "filereadermusicinterface.h"
enum enum
{ {
@ -21,45 +21,6 @@ struct FSoundFontInfo
int type; 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; return m_Status != STATE_Stopped;
} }
CDDAFile::CDDAFile (FileReader &reader) CDDAFile::CDDAFile (MusicIO::FileInterface* reader)
: CDSong () : CDSong ()
{ {
uint32_t chunk; uint32_t chunk;
uint16_t track; uint16_t track;
uint32_t discid; 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 // I_RegisterSong already identified this as a CDDA file, so we
// just need to check the contents we're interested in. // 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))) if (chunk != (('f')|(('m')<<8)|(('t')<<16)|((' ')<<24)))
{ {
reader.Read(&chunk, 4); reader->read(&chunk, 4);
reader.Seek(chunk, FileReader::SeekCur); reader->seek(LittleLong(chunk), SEEK_CUR);
} }
else else
{ {
reader.Seek(6, FileReader::SeekCur); reader->seek(6, SEEK_CUR);
reader.Read(&track, 2); reader->read(&track, 2);
reader.Read(&discid, 4); reader->read(&discid, 4);
if (CD_InitID (discid) && CD_CheckTrack (track)) if (CD_InitID (LittleLong(discid)) && CD_CheckTrack (LittleShort(track)))
{ {
m_Inited = true; m_Inited = true;
m_Track = track; m_Track = track;

View file

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

View file

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

View file

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

View file

@ -85,6 +85,7 @@
#include "g_game.h" #include "g_game.h"
#include "atterm.h" #include "atterm.h"
#include "s_music.h" #include "s_music.h"
#include "filereadermusicinterface.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -304,6 +305,7 @@ bool S_StartMusic (const char *m_id)
bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) 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) if (!force && PlayList)
{ // Don't change if a playlist is active { // Don't change if a playlist is active
return false; return false;
@ -457,7 +459,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
{ {
try 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) 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; std::pair<SoundHandle, bool> snd;
if (pBuffer->mBuffer.Size() > 0) if (pBuffer->mBuffer.size() > 0)
{ {
snd = GSnd->LoadSoundBuffered(pBuffer, true); 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; } TArray<unsigned char> *GetBuffer() { return &mBuffer; }
}; };
#endif #endif