Updated libopenmpt to version 0.3.9.

CQTexperiment
Chris Moeller 2018-04-30 20:48:06 -07:00
parent baace3fea9
commit dce35b2924
13 changed files with 113 additions and 63 deletions

View File

@ -629,6 +629,13 @@ mpt::ustring GetFullCreditsString()
"http://www.hermannseib.com/english/vsthost.htm\n"
"\n"
#endif
"Storlek for all the IT compatibility hints and testcases\n"
"as well as the IMF, MDL, OKT and ULT loaders\n"
"http://schismtracker.org/\n"
"\n"
"Sergei \"x0r\" Kolzun for various hints on Scream Tracker 2 compatibility\n"
"https://github.com/viiri/st2play\n"
"\n"
"Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n"
"https://github.com/lclevy/unmo3\n"
"\n"
@ -716,10 +723,6 @@ mpt::ustring GetFullCreditsString()
"https://github.com/kazuho/picojson\n"
"\n"
#endif
"Storlek for all the IT compatibility hints and testcases\n"
"as well as the IMF, MDL, OKT and ULT loaders\n"
"http://schismtracker.org/\n"
"\n"
#ifdef MODPLUG_TRACKER
"Lennart Poettering and David Henningsson for RealtimeKit\n"
"http://git.0pointer.net/rtkit.git/\n"

View File

@ -19,7 +19,7 @@ OPENMPT_NAMESPACE_BEGIN
#define VER_MAJORMAJOR 1
#define VER_MAJOR 27
#define VER_MINOR 07
#define VER_MINORMINOR 00
#define VER_MINORMINOR 02
//Version string. For example "1.17.02.28"
#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR)

View File

@ -5,10 +5,26 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
### libopenmpt 0.3.9 (2018-04-29)
* [**Sec**] Possible write near address 0 in out-of-memory situations when
reading AMS files (r10149).
* [**Bug**] openmpt123: Fixed build failure in C++17 due to use of removed
feature `std::random_shuffle`.
* STM: Having both Bxx and Cxx commands in a pattern imported the Bxx command
incorrectly.
* STM: Last character of sample name was missing.
* Speed up reading of truncated ULT files.
* ULT: Portamento import was sometimes broken.
* The resonant filter was sometimes unstable when combining low-volume
samples, low cutoff and high mixing rates.
### libopenmpt 0.3.8 (2018-04-08)
* [**Sec**] Possible out-of-bounds memory read with IT / ITP / MO3 files
containing pattern loops (r10028).
* [**Sec**] Possible out-of-bounds memory read with IT and MO3 files
containing many nested pattern loops (r10028).
* Keep track of active SFx macro during seeking.
* The "note cut" duplicate note action did not volume-ramp the previously
@ -60,7 +76,6 @@ is just a high-level summary.
* Tighten M15 and MOD file rejection heuristics.
* J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken
since libopenmpt-0.2.6401-beta17.
* STM: Last character of sample name was missing.
* ParamEq plugin center frequency was not limited correctly.
* libopenmpt_ext C API was not included in the documentation.

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 3
/*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 8
#define OPENMPT_API_VERSION_PATCH 9
/*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */

View File

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=3
LIBOPENMPT_VERSION_PATCH=8
LIBOPENMPT_VERSION_PATCH=9
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=1
LIBOPENMPT_LTVER_REVISION=8
LIBOPENMPT_LTVER_REVISION=9
LIBOPENMPT_LTVER_AGE=1

View File

@ -290,7 +290,7 @@ static void load_settings_from_map( libopenmpt::plugin::settings & s, const std:
static void load_settings_from_xml( libopenmpt::plugin::settings & s, const std::string & xml ) {
pugi::xml_document doc;
doc.load( xml.c_str() );
doc.load_string( xml.c_str() );
pugi::xml_node settings_node = doc.child( "settings" );
std::map<std::string,int> map;
for ( pugi::xml_attribute_iterator it = settings_node.attributes_begin(); it != settings_node.attributes_end(); ++it ) {

View File

@ -47,6 +47,7 @@ static const char * const license =
#include <iterator>
#include <limits>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
@ -1754,18 +1755,17 @@ static void render_file( commandlineflags & flags, const std::string & filename,
}
static std::string get_random_filename(std::set<std::string> & filenames) {
// TODO: actually use a useful random distribution
std::size_t index = std::rand() % filenames.size();
static std::string get_random_filename( std::set<std::string> & filenames, std::default_random_engine & prng ) {
std::size_t index = std::uniform_int_distribution<std::size_t>( 0, filenames.size() - 1 )( prng );
std::set<std::string>::iterator it = filenames.begin();
std::advance( it, index );
return *it;
}
static void render_files( commandlineflags & flags, textout & log, write_buffers_interface & audio_stream ) {
static void render_files( commandlineflags & flags, textout & log, write_buffers_interface & audio_stream, std::default_random_engine & prng ) {
if ( flags.randomize ) {
std::random_shuffle( flags.filenames.begin(), flags.filenames.end() );
std::shuffle( flags.filenames.begin(), flags.filenames.end(), prng );
}
try {
while ( true ) {
@ -1777,7 +1777,7 @@ static void render_files( commandlineflags & flags, textout & log, write_buffers
if ( shuffle_set.empty() ) {
break;
}
std::string filename = get_random_filename( shuffle_set );
std::string filename = get_random_filename( shuffle_set, prng );
try {
flags.playlist_index = std::find( flags.filenames.begin(), flags.filenames.end(), filename ) - flags.filenames.begin();
render_file( flags, filename, log, audio_stream );
@ -2393,7 +2393,10 @@ static int main( int argc, char * argv [] ) {
log.writeout();
std::srand( static_cast<unsigned int>( std::time( NULL ) ) );
std::random_device rd;
std::seed_seq seq{ rd(), static_cast<unsigned int>( std::time( NULL ) ) };
std::default_random_engine prng( seq );
std::srand( std::uniform_int_distribution<unsigned int>()( prng ) );
switch ( flags.mode ) {
case ModeProbe: {
@ -2404,42 +2407,42 @@ static int main( int argc, char * argv [] ) {
} break;
case ModeInfo: {
void_audio_stream dummy;
render_files( flags, log, dummy );
render_files( flags, log, dummy, prng );
} break;
case ModeUI:
case ModeBatch: {
if ( flags.use_stdout ) {
flags.apply_default_buffer_sizes();
stdout_stream_raii stdout_audio_stream;
render_files( flags, log, stdout_audio_stream );
render_files( flags, log, stdout_audio_stream, prng );
} else if ( !flags.output_filename.empty() ) {
flags.apply_default_buffer_sizes();
file_audio_stream_raii file_audio_stream( flags, flags.output_filename, log );
render_files( flags, log, file_audio_stream );
render_files( flags, log, file_audio_stream, prng );
#if defined( MPT_WITH_PULSEAUDIO )
} else if ( flags.driver == "pulseaudio" || flags.driver.empty() ) {
pulseaudio_stream_raii pulseaudio_stream( flags, log );
render_files( flags, log, pulseaudio_stream );
render_files( flags, log, pulseaudio_stream, prng );
#endif
#if defined( MPT_WITH_SDL2 )
} else if ( flags.driver == "sdl2" || flags.driver.empty() ) {
sdl2_stream_raii sdl2_stream( flags, log );
render_files( flags, log, sdl2_stream );
render_files( flags, log, sdl2_stream, prng );
#endif
#if defined( MPT_WITH_SDL )
} else if ( flags.driver == "sdl" || flags.driver.empty() ) {
sdl_stream_raii sdl_stream( flags, log );
render_files( flags, log, sdl_stream );
render_files( flags, log, sdl_stream, prng );
#endif
#if defined( MPT_WITH_PORTAUDIO )
} else if ( flags.driver == "portaudio" || flags.driver.empty() ) {
portaudio_stream_raii portaudio_stream( flags, log );
render_files( flags, log, portaudio_stream );
render_files( flags, log, portaudio_stream, prng );
#endif
#if defined( WIN32 )
} else if ( flags.driver == "waveout" || flags.driver.empty() ) {
waveout_stream_raii waveout_stream( flags );
render_files( flags, log, waveout_stream );
render_files( flags, log, waveout_stream, prng );
#endif
} else {
if ( flags.driver.empty() ) {

View File

@ -361,8 +361,10 @@ struct ResonantFilter
}
}
// To avoid a precision loss in the state variables especially with quiet samples at low cutoff and high mix rate, we pre-amplify the sample.
#define MIXING_FILTER_PREAMP 256
// Filter values are clipped to double the input range
#define ClipFilter(x) Clamp<typename Traits::output_t, typename Traits::output_t>(x, int16_min * 2, int16_max * 2)
#define ClipFilter(x) Clamp<typename Traits::output_t, typename Traits::output_t>(x, int16_min * 2 * MIXING_FILTER_PREAMP, int16_max * 2 * MIXING_FILTER_PREAMP)
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn)
{
@ -370,14 +372,15 @@ struct ResonantFilter
for(int i = 0; i < Traits::numChannelsIn; i++)
{
typename Traits::output_t val = static_cast<typename Traits::output_t>((
Util::mul32to64(outSample[i], chn.nFilter_A0) +
const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP;
typename Traits::output_t val = static_cast<typename Traits::output_t>(mpt::rshift_signed(
Util::mul32to64(inputAmp, chn.nFilter_A0) +
Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) +
Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) +
(1 << (MIXING_FILTER_PRECISION - 1))) / (1 << MIXING_FILTER_PRECISION));
(1 << (MIXING_FILTER_PRECISION - 1)), MIXING_FILTER_PRECISION));
fy[i][1] = fy[i][0];
fy[i][0] = val - (outSample[i] & chn.nFilter_HP);
outSample[i] = val;
fy[i][0] = val - (inputAmp & chn.nFilter_HP);
outSample[i] = val / MIXING_FILTER_PREAMP;
}
}

View File

@ -179,7 +179,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46
return false;
sampleHeader.ConvertToMPT(Samples[smp]);
mpt::String::Read<mpt::String::nullTerminated>(m_szNames[smp], sampleHeader.filename);
mpt::String::Read<mpt::String::maybeNullTerminated>(m_szNames[smp], sampleHeader.filename);
sampleOffsets[smp - 1] = sampleHeader.offset;
}
@ -212,7 +212,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
ORDERINDEX breakPos = ORDERINDEX_INVALID;
ROWINDEX breakRow = 63; // Candidate row for inserting pattern break
for(int i = 0; i < 64 * 4; i++, m++)
for(unsigned int i = 0; i < 64 * 4; i++, m++)
{
uint8 note = file.ReadUint8(), insvol, volcmd, cmdinf;
switch(note)
@ -226,9 +226,13 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
m->note = NOTE_NOTECUT;
continue;
default:
insvol = file.ReadUint8();
volcmd = file.ReadUint8();
cmdinf = file.ReadUint8();
{
uint8 patData[3];
file.ReadArray(patData);
insvol = patData[0];
volcmd = patData[1];
cmdinf = patData[2];
}
break;
}
@ -266,16 +270,22 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
{
case CMD_VOLUMESLIDE:
// Lower nibble always has precedence, and there are no fine slides.
if(m->param & 0x0F) m->param &= 0x0F;
else m->param &= 0xF0;
if(m->param & 0x0F)
m->param &= 0x0F;
else
m->param &= 0xF0;
break;
case CMD_PATTERNBREAK:
m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F);
if(breakRow > m->param)
if(breakPos != ORDERINDEX_INVALID && m->param == 0)
{
breakRow = m->param;
// Merge Bxx + C00 into just Bxx
m->command = CMD_POSITIONJUMP;
m->param = static_cast<ModCommand::PARAM>(breakPos);
breakPos = ORDERINDEX_INVALID;
}
LimitMax(breakRow, i / 4u);
break;
case CMD_POSITIONJUMP:

View File

@ -85,6 +85,17 @@ struct UltSample
MPT_BINARY_STRUCT(UltSample, 66)
struct UltPatternCommand
{
uint8 instr;
uint8 cmd;
uint8 param1;
uint8 param2;
};
MPT_BINARY_STRUCT(UltPatternCommand, 4)
/* Unhandled effects:
5x1 - do not loop sample (x is unused)
9xx - set sample offset to xx * 1024
@ -215,13 +226,14 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
b = file.ReadUint8();
}
m.note = (b > 0 && b < 61) ? b + 36 : NOTE_NONE;
m.instr = file.ReadUint8();
b = file.ReadUint8();
cmd1 = b & 0x0F;
cmd2 = b >> 4;
param1 = file.ReadUint8();
param2 = file.ReadUint8();
m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE;
UltPatternCommand patCmd;
file.ReadStruct(patCmd);
m.instr = patCmd.instr;
cmd1 = patCmd.cmd & 0x0F;
cmd2 = patCmd.cmd >> 4;
param1 = patCmd.param1;
param2 = patCmd.param2;
TranslateULTCommands(cmd1, param1, version);
TranslateULTCommands(cmd2, param2, version);
@ -295,7 +307,7 @@ struct PostFixUltCommands
// Apply porta?
if(m.note == NOTE_NONE && isPortaActive[curChannel])
{
if(m.command == CMD_NONE && m.vol != VOLCMD_TONEPORTAMENTO)
if(m.command == CMD_NONE && m.volcmd != VOLCMD_TONEPORTAMENTO)
{
m.command = CMD_TONEPORTAMENTO;
m.param = 0;
@ -345,6 +357,10 @@ static bool ValidateHeader(const UltFileHeader &fileHeader)
return true;
}
static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader)
{
return fileHeader.messageLength * 32u;
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize)
{
@ -357,8 +373,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, co
{
return ProbeFailure;
}
MPT_UNREFERENCED_PARAMETER(pfilesize);
return ProbeSuccess;
return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
}
@ -379,6 +394,10 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags)
{
return true;
}
if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
{
return false;
}
InitializeGlobals(MOD_TYPE_ULT);
mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, fileHeader.songName);
@ -442,12 +461,9 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags)
for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
{
ModCommand evnote;
ModCommand *note;
evnote.Clear();
for(PATTERNINDEX pat = 0; pat < numPats; pat++)
for(PATTERNINDEX pat = 0; pat < numPats && file.CanRead(5); pat++)
{
note = Patterns[pat].GetpModCommand(0, chn);
ModCommand *note = Patterns[pat].GetpModCommand(0, chn);
ROWINDEX row = 0;
while(row < 64)
{

View File

@ -16,7 +16,7 @@ OPENMPT_NAMESPACE_BEGIN
#ifdef MPT_INTMIXER
typedef int32 mixsample_t;
enum { MIXING_FILTER_PRECISION = 16 }; // Fixed point resonant filter bits
enum { MIXING_FILTER_PRECISION = 24 }; // Fixed point resonant filter bits
#else
typedef float mixsample_t;
#endif

View File

@ -1665,7 +1665,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
}
if(!region.name.empty())
{
mpt::String::Copy(m_szNames[smp], region.name);
mpt::String::Copy(m_szNames[smp], mpt::ToCharset(GetCharsetInternal(), mpt::CharsetUTF8, region.name));
}
if(!m_szNames[smp][0])
{

View File

@ -133,7 +133,7 @@ public:
const_iterator end() const { return m_ModCommands.end(); }
const_iterator cend() const { return m_ModCommands.cend(); }
CPattern(CPatternContainer& patCont) : m_ModCommands(0), m_Rows(64), m_RowsPerBeat(0), m_RowsPerMeasure(0), m_rPatternContainer(patCont) {};
CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {};
CPattern(const CPattern &) = default;
CPattern(CPattern &&) noexcept = default;
@ -148,9 +148,9 @@ protected:
//BEGIN: DATA
protected:
std::vector<ModCommand> m_ModCommands;
ROWINDEX m_Rows;
ROWINDEX m_RowsPerBeat; // patterns-specific time signature. if != 0, this is implicitely set.
ROWINDEX m_RowsPerMeasure; // ditto
ROWINDEX m_Rows = 0;
ROWINDEX m_RowsPerBeat = 0; // patterns-specific time signature. if != 0, this is implicitely set.
ROWINDEX m_RowsPerMeasure = 0; // ditto
TempoSwing m_tempoSwing;
std::string m_PatternName;
CPatternContainer& m_rPatternContainer;