mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 06:42:12 +00:00
- 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:
parent
b883d27a1f
commit
cdf2a17c5a
35 changed files with 548 additions and 430 deletions
|
@ -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 )
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
|
@ -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);
|
|
@ -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;
|
||||||
}
|
}
|
81
libraries/zmusic/decoder/sounddecoder.cpp
Normal file
81
libraries/zmusic/decoder/sounddecoder.cpp
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
40
libraries/zmusic/zmusic/sounddecoder.h
Normal file
40
libraries/zmusic/zmusic/sounddecoder.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 --------------------
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
39
src/utility/filereadermusicinterface.h
Normal file
39
src/utility/filereadermusicinterface.h
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -328,4 +328,5 @@ public:
|
||||||
TArray<unsigned char> *GetBuffer() { return &mBuffer; }
|
TArray<unsigned char> *GetBuffer() { return &mBuffer; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue