Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libopenmpt for openSUSE:Factory checked in at 2021-12-06 23:59:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libopenmpt (Old) and /work/SRC/openSUSE:Factory/.libopenmpt.new.31177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libopenmpt" Mon Dec 6 23:59:02 2021 rev:32 rq:935240 version:0.5.13 Changes: -------- --- /work/SRC/openSUSE:Factory/libopenmpt/libopenmpt.changes 2021-10-29 22:34:27.391687181 +0200 +++ /work/SRC/openSUSE:Factory/.libopenmpt.new.31177/libopenmpt.changes 2021-12-06 23:59:03.464633251 +0100 @@ -1,0 +2,16 @@ +Wed Dec 1 10:26:39 UTC 2021 - Danilo Spinella <[email protected]> + +- Update to 0.5.13: + * [Bug] Fixed various undefined behaviour found with ubsan. + * IMF: Change envelope interpretation to be more like in XM instead of + IT and tighten header validation. + * MED: Some samples had a ping-pong loop when there should be no loop at all. + * MT2: Ignore incorrect drums chunk size in early MT2 files (fixes e.g. + ???A little Rock??? by Csumi). + * MT2: Work around initial master volume of 0 used in some files that apply + a fade-in a the song start using track automation that would stay silent + forever otherwise (track automation is currently not supported). + * OKT: Apply portamento on every tick. + * mpg123: Update to v1.29.2 (2021-10-23). + +------------------------------------------------------------------- Old: ---- libopenmpt-0.5.12+release.autotools.tar.gz New: ---- libopenmpt-0.5.13+release.autotools.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libopenmpt.spec ++++++ --- /var/tmp/diff_new_pack.ZIUHER/_old 2021-12-06 23:59:04.216630592 +0100 +++ /var/tmp/diff_new_pack.ZIUHER/_new 2021-12-06 23:59:04.220630578 +0100 @@ -21,7 +21,7 @@ %define libopenmpt_modplug_version 0.8.9.0 Name: libopenmpt -Version: 0.5.12 +Version: 0.5.13 Release: 0 Summary: C++ and C library to decode tracker music files License: BSD-3-Clause ++++++ libopenmpt-0.5.12+release.autotools.tar.gz -> libopenmpt-0.5.13+release.autotools.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/common/versionNumber.h new/libopenmpt-0.5.13+release.autotools/common/versionNumber.h --- old/libopenmpt-0.5.12+release.autotools/common/versionNumber.h 2021-10-03 17:44:24.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/common/versionNumber.h 2021-11-14 17:44:35.000000000 +0100 @@ -17,7 +17,7 @@ // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 29 -#define VER_MINOR 13 +#define VER_MINOR 14 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/configure new/libopenmpt-0.5.13+release.autotools/configure --- old/libopenmpt-0.5.12+release.autotools/configure 2021-10-04 15:51:33.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/configure 2021-11-14 18:11:21.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libopenmpt 0.5.12+release.autotools. +# Generated by GNU Autoconf 2.69 for libopenmpt 0.5.13+release.autotools. # # Report bugs to <https://bugs.openmpt.org/>. # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='libopenmpt' PACKAGE_TARNAME='libopenmpt' -PACKAGE_VERSION='0.5.12+release.autotools' -PACKAGE_STRING='libopenmpt 0.5.12+release.autotools' +PACKAGE_VERSION='0.5.13+release.autotools' +PACKAGE_STRING='libopenmpt 0.5.13+release.autotools' PACKAGE_BUGREPORT='https://bugs.openmpt.org/' PACKAGE_URL='https://lib.openmpt.org/' @@ -1472,7 +1472,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libopenmpt 0.5.12+release.autotools to adapt to many kinds of systems. +\`configure' configures libopenmpt 0.5.13+release.autotools to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1543,7 +1543,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libopenmpt 0.5.12+release.autotools:";; + short | recursive ) echo "Configuration of libopenmpt 0.5.13+release.autotools:";; esac cat <<\_ACEOF @@ -1729,7 +1729,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libopenmpt configure 0.5.12+release.autotools +libopenmpt configure 0.5.13+release.autotools generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2219,7 +2219,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libopenmpt $as_me 0.5.12+release.autotools, which was +It was created by libopenmpt $as_me 0.5.13+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3090,7 +3090,7 @@ # Define the identity of the package. PACKAGE='libopenmpt' - VERSION='0.5.12+release.autotools' + VERSION='0.5.13+release.autotools' cat >>confdefs.h <<_ACEOF @@ -17251,13 +17251,13 @@ -$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12\"" >>confdefs.h +$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.13\"" >>confdefs.h -$as_echo "#define MPT_SVNVERSION \"15759\"" >>confdefs.h +$as_echo "#define MPT_SVNVERSION \"15956\"" >>confdefs.h -$as_echo "#define MPT_SVNDATE \"2021-10-04T13:48:02.912129Z\"" >>confdefs.h +$as_echo "#define MPT_SVNDATE \"2021-11-14T17:01:47.266406Z\"" >>confdefs.h $as_echo "#define MPT_PACKAGE true" >>confdefs.h @@ -22524,7 +22524,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libopenmpt $as_me 0.5.12+release.autotools, which was +This file was extended by libopenmpt $as_me 0.5.13+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22591,7 +22591,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libopenmpt config.status 0.5.12+release.autotools +libopenmpt config.status 0.5.13+release.autotools configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/configure.ac new/libopenmpt-0.5.13+release.autotools/configure.ac --- old/libopenmpt-0.5.12+release.autotools/configure.ac 2021-10-04 15:51:11.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/configure.ac 2021-11-14 18:11:12.000000000 +0100 @@ -1,4 +1,4 @@ -AC_INIT([libopenmpt], [0.5.12+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) +AC_INIT([libopenmpt], [0.5.13+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) AC_PREREQ([2.68]) AC_CONFIG_MACRO_DIR([m4]) @@ -27,9 +27,9 @@ AC_SUBST([LIBOPENMPT_LTVER_REVISION]) AC_SUBST([LIBOPENMPT_LTVER_AGE]) -AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12"], [svn version]) -AC_DEFINE([MPT_SVNVERSION], ["15759"], [svn version]) -AC_DEFINE([MPT_SVNDATE], ["2021-10-04T13:48:02.912129Z"], [svn date]) +AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.13"], [svn version]) +AC_DEFINE([MPT_SVNVERSION], ["15956"], [svn version]) +AC_DEFINE([MPT_SVNDATE], ["2021-11-14T17:01:47.266406Z"], [svn date]) AC_DEFINE([MPT_PACKAGE], [true], [is package]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/libopenmpt/dox/changelog.md new/libopenmpt-0.5.13+release.autotools/libopenmpt/dox/changelog.md --- old/libopenmpt-0.5.12+release.autotools/libopenmpt/dox/changelog.md 2021-10-04 15:48:00.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/libopenmpt/dox/changelog.md 2021-11-14 18:01:45.000000000 +0100 @@ -5,6 +5,22 @@ For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.5.13 (2021-11-14) + + * [**Bug**] Fixed various undefined behaviour found with ubsan. + + * IMF: Change envelope interpretation to be more like in XM instead of IT and + tighten header validation. + * MED: Some samples had a ping-pong loop when there should be no loop at all. + * MT2: Ignore incorrect drums chunk size in early MT2 files + (fixes e.g. "A little Rock" by Csumi). + * MT2: Work around initial master volume of 0 used in some files that apply a + fade-in a the song start using track automation that would stay silent + forever otherwise (track automation is currently not supported). + * OKT: Apply portamento on every tick. + + * mpg123: Update to v1.29.2 (2021-10-23). + ### libopenmpt 0.5.12 (2021-10-04) * [**Sec**] Possible crash when loading malformed MDL files. (r15603) @@ -24,6 +40,8 @@ * in_openmpt: Song metadata is no longer reverted when viewing file info. + * mpg123: Update to v1.29.0 (2021-09-06). + ### libopenmpt 0.5.11 (2021-08-22) * [**Sec**] Possible crash with malformed modules when trying to access diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_c.cpp new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_c.cpp --- old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_c.cpp 2021-01-10 16:47:53.000000000 +0100 +++ new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_c.cpp 2021-10-05 13:35:16.000000000 +0200 @@ -88,6 +88,8 @@ static std::string format_exception( const char * const function ) { std::string err; try { + // cppcheck false-positive + // cppcheck-suppress rethrowNoCurrentException throw; } catch ( const openmpt::exception & e ) { err += function; @@ -131,6 +133,8 @@ } } try { + // cppcheck false-positive + // cppcheck-suppress rethrowNoCurrentException throw; } catch ( const std::bad_alloc & e ) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_impl.cpp new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_impl.cpp --- old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_impl.cpp 2021-03-30 19:56:20.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_impl.cpp 2021-11-13 13:08:25.000000000 +0100 @@ -1806,7 +1806,7 @@ } return m_sndFile->m_nFreqFactor / 65536.0; } else if ( ctl == "render.opl.volume_factor" ) { - return static_cast<double>( m_sndFile->m_OPLVolumeFactor ) / static_cast<double>( m_sndFile->m_OPLVolumeFactorScale ); + return static_cast<double>( m_sndFile->m_OPLVolumeFactor ) / static_cast<double>( CSoundFile::m_OPLVolumeFactorScale ); } else { MPT_ASSERT_NOTREACHED(); return 0.0; @@ -2045,7 +2045,7 @@ m_sndFile->m_nFreqFactor = mpt::saturate_round<uint32_t>( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "render.opl.volume_factor" ) { - m_sndFile->m_OPLVolumeFactor = mpt::saturate_round<int32>( value * static_cast<double>( m_sndFile->m_OPLVolumeFactorScale ) ); + m_sndFile->m_OPLVolumeFactor = mpt::saturate_round<int32>( value * static_cast<double>( CSoundFile::m_OPLVolumeFactorScale ) ); } else { MPT_ASSERT_NOTREACHED(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_version.h new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_version.h --- old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_version.h 2021-10-04 15:48:00.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_version.h 2021-11-14 18:01:45.000000000 +0100 @@ -19,7 +19,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 5 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 12 +#define OPENMPT_API_VERSION_PATCH 13 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_version.mk new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_version.mk --- old/libopenmpt-0.5.12+release.autotools/libopenmpt/libopenmpt_version.mk 2021-10-04 15:48:00.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/libopenmpt/libopenmpt_version.mk 2021-11-14 18:01:45.000000000 +0100 @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=5 -LIBOPENMPT_VERSION_PATCH=12 +LIBOPENMPT_VERSION_PATCH=13 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=2 -LIBOPENMPT_LTVER_REVISION=12 +LIBOPENMPT_LTVER_REVISION=13 LIBOPENMPT_LTVER_AGE=2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/man/openmpt123.1 new/libopenmpt-0.5.13+release.autotools/man/openmpt123.1 --- old/libopenmpt-0.5.12+release.autotools/man/openmpt123.1 2021-10-04 15:51:11.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/man/openmpt123.1 2021-11-14 18:11:12.000000000 +0100 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. -.TH OPENMPT123 "1" "October 2021" "openmpt123 v0.5.12" "User Commands" +.TH OPENMPT123 "1" "November 2021" "openmpt123 v0.5.13" "User Commands" .SH NAME openmpt123 - command line module music player based on libopenmpt .SH SYNOPSIS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/openmpt123/openmpt123.cpp new/libopenmpt-0.5.13+release.autotools/openmpt123/openmpt123.cpp --- old/libopenmpt-0.5.12+release.autotools/openmpt123/openmpt123.cpp 2021-09-15 17:09:22.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/openmpt123/openmpt123.cpp 2021-11-13 10:43:33.000000000 +0100 @@ -46,6 +46,7 @@ #include <limits> #include <locale> #include <map> +#include <memory> #include <random> #include <set> #include <sstream> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Dlsbank.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Dlsbank.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Dlsbank.cpp 2021-09-13 00:47:14.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Dlsbank.cpp 2021-11-13 15:03:12.000000000 +0100 @@ -42,7 +42,6 @@ DLSREGION_SAMPLELOOP = 0x40, DLSREGION_SELFNONEXCLUSIVE = 0x80, DLSREGION_SUSTAINLOOP = 0x100, - DLSREGION_ISGLOBAL = 0x200, }; /////////////////////////////////////////////////////////////////////////// @@ -376,7 +375,7 @@ ///////////////////////////////////////////////////////////////////// // SF2 Structures Definitions -struct SFPRESETHEADER +struct SFPresetHeader { char achPresetName[20]; uint16le wPreset; @@ -387,49 +386,49 @@ uint32le dwMorphology; }; -MPT_BINARY_STRUCT(SFPRESETHEADER, 38) +MPT_BINARY_STRUCT(SFPresetHeader, 38) -struct SFPRESETBAG +struct SFPresetBag { uint16le wGenNdx; uint16le wModNdx; }; -MPT_BINARY_STRUCT(SFPRESETBAG, 4) +MPT_BINARY_STRUCT(SFPresetBag, 4) -struct SFGENLIST +struct SFGenList { uint16le sfGenOper; uint16le genAmount; }; -MPT_BINARY_STRUCT(SFGENLIST, 4) +MPT_BINARY_STRUCT(SFGenList, 4) -struct SFINST +struct SFInst { char achInstName[20]; uint16le wInstBagNdx; }; -MPT_BINARY_STRUCT(SFINST, 22) +MPT_BINARY_STRUCT(SFInst, 22) -struct SFINSTBAG +struct SFInstBag { uint16le wGenNdx; uint16le wModNdx; }; -MPT_BINARY_STRUCT(SFINSTBAG, 4) +MPT_BINARY_STRUCT(SFInstBag, 4) -struct SFINSTGENLIST +struct SFInstGenList { uint16le sfGenOper; uint16le genAmount; }; -MPT_BINARY_STRUCT(SFINSTGENLIST, 4) +MPT_BINARY_STRUCT(SFInstGenList, 4) -struct SFSAMPLE +struct SFSample { char achSampleName[20]; uint32le dwStart; @@ -443,7 +442,7 @@ uint16le sfSampleType; }; -MPT_BINARY_STRUCT(SFSAMPLE, 46) +MPT_BINARY_STRUCT(SFSample, 46) // End of structures definitions ///////////////////////////////////////////////////////////////////// @@ -581,11 +580,17 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const { - if(m_Instruments.empty()) - return nullptr; - for (uint32 iIns = 0; iIns < m_Instruments.size(); iIns++) + // This helps finding the "more correct" instrument if we search for an instrument in any bank, and the higher-bank instruments appear first in the file + // Fixes issues when loading GeneralUser GS into OpenMPT's MIDI library. + std::vector<std::reference_wrapper<const DLSINSTRUMENT>> sortedInstr{m_Instruments.begin(), m_Instruments.end()}; + if(bank >= 0x4000 || program >= 0x80) + { + std::sort(sortedInstr.begin(), sortedInstr.end(), [](const DLSINSTRUMENT &l, const DLSINSTRUMENT &r) + { return std::tie(l.ulBank, l.ulInstrument) < std::tie(r.ulBank, r.ulInstrument); }); + } + + for(const DLSINSTRUMENT &dlsIns : sortedInstr) { - const DLSINSTRUMENT &dlsIns = m_Instruments[iIns]; uint32 insbank = ((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F); if((bank >= 0x4000) || (insbank == bank)) { @@ -595,16 +600,16 @@ { for(const auto ®ion : dlsIns.Regions) { - if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink)) - continue; - if(region.fuOptions & DLSREGION_ISGLOBAL) + if(region.IsDummy()) continue; if((!key || key >= 0x80) || (key >= region.uKeyMin && key <= region.uKeyMax)) { if(pInsNo) - *pInsNo = iIns; + *pInsNo = static_cast<uint32>(std::distance(m_Instruments.data(), &dlsIns)); + // cppcheck false-positive + // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } @@ -614,7 +619,9 @@ if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { if(pInsNo) - *pInsNo = iIns; + *pInsNo = static_cast<uint32>(std::distance(m_Instruments.data(), &dlsIns)); + // cppcheck false-positive + // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } @@ -877,7 +884,7 @@ case IFFID_phdr: if(m_Instruments.empty()) { - uint32 numIns = static_cast<uint32>(chunk.GetLength() / sizeof(SFPRESETHEADER)); + uint32 numIns = static_cast<uint32>(chunk.GetLength() / sizeof(SFPresetHeader)); if(numIns <= 1) break; // The terminal sfPresetHeader record should never be accessed, and exists only to provide a terminal wPresetBagNdx with which to determine the number of zones in the last preset. @@ -887,9 +894,9 @@ #ifdef DLSBANK_LOG MPT_LOG(LogDebug, "DLSBank", mpt::format(U_("phdr: %1 instruments"))(m_Instruments.size())); #endif - SFPRESETHEADER psfh; + SFPresetHeader psfh; chunk.ReadStruct(psfh); - for (auto &dlsIns : m_Instruments) + for(auto &dlsIns : m_Instruments) { mpt::String::WriteAutoBuf(dlsIns.szName) = mpt::String::ReadAutoBuf(psfh.achPresetName); dlsIns.ulInstrument = psfh.wPreset & 0x7F; @@ -903,7 +910,7 @@ break; case IFFID_pbag: - if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPRESETBAG))) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPresetBag))) { sf2info.presetBags = chunk.GetChunk(chunk.BytesLeft()); } @@ -913,7 +920,7 @@ break; case IFFID_pgen: - if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGENLIST))) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGenList))) { sf2info.presetGens = chunk.GetChunk(chunk.BytesLeft()); } @@ -923,21 +930,21 @@ break; case IFFID_inst: - if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINST))) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInst))) { sf2info.insts = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_ibag: - if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTBAG))) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstBag))) { sf2info.instBags = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_igen: - if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTGENLIST))) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstGenList))) { sf2info.instGens = chunk.GetChunk(chunk.BytesLeft()); } @@ -946,7 +953,7 @@ case IFFID_shdr: if (m_SamplesEx.empty()) { - uint32 numSmp = static_cast<uint32>(chunk.GetLength() / sizeof(SFSAMPLE)); + uint32 numSmp = static_cast<uint32>(chunk.GetLength() / sizeof(SFSample)); if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); @@ -956,7 +963,7 @@ for (uint32 i = 0; i < numSmp; i++) { - SFSAMPLE p; + SFSample p; chunk.ReadStruct(p); DLSSAMPLEEX &dlsSmp = m_SamplesEx[i]; mpt::String::WriteAutoBuf(dlsSmp.szName) = mpt::String::ReadAutoBuf(p.achSampleName); @@ -1006,16 +1013,16 @@ if (m_Instruments.empty() || m_SamplesEx.empty()) return false; - const uint32 numPresetBags = static_cast<uint32>(sf2info.presetBags.GetLength() / sizeof(SFPRESETBAG)); - const uint32 numPresetGens = static_cast<uint32>(sf2info.presetGens.GetLength() / sizeof(SFGENLIST)); - const uint32 numInsts = static_cast<uint32>(sf2info.insts.GetLength() / sizeof(SFINST)); - const uint32 numInstBags = static_cast<uint32>(sf2info.instBags.GetLength() / sizeof(SFINSTBAG)); - const uint32 numInstGens = static_cast<uint32>(sf2info.instGens.GetLength() / sizeof(SFINSTGENLIST)); + const uint32 numInsts = static_cast<uint32>(sf2info.insts.GetLength() / sizeof(SFInst)); + const uint32 numInstBags = static_cast<uint32>(sf2info.instBags.GetLength() / sizeof(SFInstBag)); - for (auto &dlsIns : m_Instruments) + std::vector<std::pair<uint16, uint16>> instruments; // instrument, key range + std::vector<SFGenList> generators; + std::vector<SFInstGenList> instrGenerators; + for(auto &dlsIns : m_Instruments) { + instruments.clear(); DLSENVELOPE dlsEnv; - std::vector<uint32> instruments; int32 instrAttenuation = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; @@ -1024,21 +1031,21 @@ dlsEnv.nVolSustainLevel = 128; dlsEnv.nDefPan = 128; // Load Preset Bags - sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPRESETBAG)); - for (uint32 ipbagcnt=0; ipbagcnt<(uint32)dlsIns.wPresetBagNum; ipbagcnt++) + sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPresetBag)); + for(uint32 ipbagcnt = 0; ipbagcnt < dlsIns.wPresetBagNum; ipbagcnt++) { // Load generators for each preset bag - SFPRESETBAG bag[2]; + SFPresetBag bag[2]; if(!sf2info.presetBags.ReadArray(bag)) break; - sf2info.presetBags.SkipBack(sizeof(SFPRESETBAG)); + sf2info.presetBags.SkipBack(sizeof(SFPresetBag)); - sf2info.presetGens.Seek(bag[0].wGenNdx * sizeof(SFGENLIST)); - for (uint32 ipgenndx = bag[0].wGenNdx; ipgenndx < bag[1].wGenNdx; ipgenndx++) + sf2info.presetGens.Seek(bag[0].wGenNdx * sizeof(SFGenList)); + uint16 keyRange = 0xFFFF; + if(!sf2info.presetGens.ReadVector(generators, bag[1].wGenNdx - bag[0].wGenNdx)) + continue; + for(const auto &gen : generators) { - SFGENLIST gen; - if(!sf2info.presetGens.ReadStruct(gen)) - break; switch(gen.sfGenOper) { case SF2_GEN_ATTACKVOLENV: @@ -1058,8 +1065,12 @@ dlsEnv.wVolRelease = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_INSTRUMENT: - if(std::find(instruments.begin(), instruments.end(), gen.genAmount) == instruments.end()) - instruments.push_back(gen.genAmount); + if(const auto instr = std::make_pair(gen.genAmount.get(), keyRange); std::find(instruments.begin(), instruments.end(), instr) == instruments.end()) + instruments.push_back(instr); + keyRange = 0xFFFF; + break; + case SF2_GEN_KEYRANGE: + keyRange = gen.genAmount; break; case SF2_GEN_ATTENUATION: instrAttenuation = -static_cast<int16>(gen.genAmount); @@ -1081,22 +1092,35 @@ } // Load Instrument Bags dlsIns.Regions.clear(); - for(const auto nInstrNdx : instruments) + for(const auto [nInstrNdx, keyRange] : instruments) { if(nInstrNdx >= numInsts) continue; - sf2info.insts.Seek(nInstrNdx * sizeof(SFINST)); - SFINST insts[2]; + sf2info.insts.Seek(nInstrNdx * sizeof(SFInst)); + SFInst insts[2]; sf2info.insts.ReadArray(insts); - const auto startRegion = static_cast<uint32>(dlsIns.Regions.size()); - const auto endRegion = startRegion + insts[1].wInstBagNdx - insts[0].wInstBagNdx; - dlsIns.Regions.resize(endRegion); + const uint32 numRegions = insts[1].wInstBagNdx - insts[0].wInstBagNdx; + dlsIns.Regions.reserve(dlsIns.Regions.size() + numRegions); //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); - DLSREGION *pRgn = &dlsIns.Regions[startRegion]; - bool hasGlobalZone = false; - for(uint32 nRgn = startRegion; nRgn < endRegion; nRgn++, pRgn++) + DLSREGION globalZone{}; + globalZone.uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note + globalZone.tuning = 100; + globalZone.sFineTune = 0; + globalZone.nWaveLink = Util::MaxValueOfType(globalZone.nWaveLink); + if(keyRange != 0xFFFF) + { + globalZone.uKeyMin = static_cast<uint8>(keyRange & 0xFF); + globalZone.uKeyMax = static_cast<uint8>(keyRange >> 8); + if(globalZone.uKeyMin > globalZone.uKeyMax) + std::swap(globalZone.uKeyMin, globalZone.uKeyMax); + } else { - uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion; + globalZone.uKeyMin = 0; + globalZone.uKeyMax = 127; + } + for(uint32 nRgn = 0; nRgn < numRegions; nRgn++) + { + uint32 ibagcnt = insts[0].wInstBagNdx + nRgn; if(ibagcnt >= numInstBags) break; // Create a new envelope for drums @@ -1105,42 +1129,42 @@ { pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; } + + DLSREGION rgn = globalZone; + // Region Default Values int32 regionAttn = 0; - pRgn->uKeyMin = 0; - pRgn->uKeyMax = 127; - pRgn->uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note - pRgn->tuning = 100; - pRgn->sFineTune = 0; - pRgn->nWaveLink = Util::MaxValueOfType(pRgn->nWaveLink); - if(hasGlobalZone) - *pRgn = dlsIns.Regions[startRegion]; // Load Generators - sf2info.instBags.Seek(ibagcnt * sizeof(SFINSTBAG)); - SFINSTBAG bags[2]; + sf2info.instBags.Seek(ibagcnt * sizeof(SFInstBag)); + SFInstBag bags[2]; sf2info.instBags.ReadArray(bags); - sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFINSTGENLIST)); + sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFInstGenList)); uint16 lastOp = SF2_GEN_SAMPLEID; int32 loopStart = 0, loopEnd = 0; - for(uint32 igenndx = bags[0].wGenNdx; igenndx < bags[1].wGenNdx; igenndx++) + if(!sf2info.instGens.ReadVector(instrGenerators, bags[1].wGenNdx - bags[0].wGenNdx)) + break; + for(const auto &gen : instrGenerators) { - if(igenndx >= numInstGens) - break; - SFINSTGENLIST gen; - sf2info.instGens.ReadStruct(gen); uint16 value = gen.genAmount; lastOp = gen.sfGenOper; switch(gen.sfGenOper) { case SF2_GEN_KEYRANGE: - pRgn->uKeyMin = (uint8)(value & 0xFF); - pRgn->uKeyMax = (uint8)(value >> 8); - if(pRgn->uKeyMin > pRgn->uKeyMax) - std::swap(pRgn->uKeyMin, pRgn->uKeyMax); + { + uint8 keyMin = static_cast<uint8>(value & 0xFF); + uint8 keyMax = static_cast<uint8>(value >> 8); + if(keyMin > keyMax) + std::swap(keyMin, keyMax); + rgn.uKeyMin = std::max(rgn.uKeyMin, keyMin); + rgn.uKeyMax = std::min(rgn.uKeyMax, keyMax); + // There was no overlap between instrument region and preset region - skip it + if(rgn.uKeyMin > rgn.uKeyMax) + rgn.uKeyMin = rgn.uKeyMax = 0xFF; + } break; case SF2_GEN_UNITYNOTE: - if (value < 128) pRgn->uUnityNote = (uint8)value; + if (value < 128) rgn.uUnityNote = (uint8)value; break; case SF2_GEN_ATTACKVOLENV: pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount); @@ -1162,7 +1186,7 @@ { int32 pan = static_cast<int16>(value); pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256); - pRgn->panning = static_cast<int16>(pan); + rgn.panning = static_cast<int16>(pan); pDlsEnv->nDefPan = mpt::saturate_cast<uint8>(pan); } break; @@ -1172,33 +1196,33 @@ case SF2_GEN_SAMPLEID: if (value < m_SamplesEx.size()) { - pRgn->nWaveLink = value; - pRgn->ulLoopStart = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwStartloop + loopStart); - pRgn->ulLoopEnd = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwEndloop + loopEnd); + rgn.nWaveLink = value; + rgn.ulLoopStart = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwStartloop + loopStart); + rgn.ulLoopEnd = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwEndloop + loopEnd); } break; case SF2_GEN_SAMPLEMODES: value &= 3; - pRgn->fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); + rgn.fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); if(value == 1) - pRgn->fuOptions |= DLSREGION_SAMPLELOOP; + rgn.fuOptions |= DLSREGION_SAMPLELOOP; else if(value == 2) - pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP; + rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP; else if(value == 3) - pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP; - pRgn->fuOptions |= DLSREGION_OVERRIDEWSMP; + rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP; + rgn.fuOptions |= DLSREGION_OVERRIDEWSMP; break; case SF2_GEN_KEYGROUP: - pRgn->fuOptions |= (value & DLSREGION_KEYGROUPMASK); + rgn.fuOptions |= (value & DLSREGION_KEYGROUPMASK); break; case SF2_GEN_COARSETUNE: - pRgn->sFineTune += static_cast<int16>(value) * 128; + rgn.sFineTune += static_cast<int16>(value) * 128; break; case SF2_GEN_FINETUNE: - pRgn->sFineTune += static_cast<int16>(Util::muldiv(static_cast<int8>(value), 128, 100)); + rgn.sFineTune += static_cast<int16>(Util::muldiv(static_cast<int8>(value), 128, 100)); break; case SF2_GEN_SCALE_TUNING: - pRgn->tuning = mpt::saturate_cast<uint8>(value); + rgn.tuning = mpt::saturate_cast<uint8>(value); break; case SF2_GEN_START_LOOP_FINE: loopStart += static_cast<int16>(value); @@ -1216,14 +1240,14 @@ // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); } } - if(lastOp != SF2_GEN_SAMPLEID && nRgn == startRegion) - { - hasGlobalZone = true; - pRgn->fuOptions |= DLSREGION_ISGLOBAL; - } int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256; Limit(linearVol, 16, 256); - pRgn->usVolume = static_cast<uint16>(linearVol); + rgn.usVolume = static_cast<uint16>(linearVol); + + if(lastOp != SF2_GEN_SAMPLEID && nRgn == 0) + globalZone = rgn; + else if(!rgn.IsDummy()) + dlsIns.Regions.push_back(rgn); //Log("\n"); } } @@ -1399,10 +1423,10 @@ uint32 subID = listChunk.ReadUint32LE(); if ((subID == IFFID_ins) && (nInsDef < m_Instruments.size())) { - DLSINSTRUMENT *pDlsIns = &m_Instruments[nInsDef]; + DLSINSTRUMENT &dlsIns = m_Instruments[nInsDef]; //Log("Instrument %d:\n", nInsDef); - pDlsIns->Regions.push_back({}); - UpdateInstrumentDefinition(pDlsIns, subData); + dlsIns.Regions.push_back({}); + UpdateInstrumentDefinition(&dlsIns, subData); nInsDef++; } } else @@ -1483,7 +1507,7 @@ } //////////////////////////////////////////////////////////////////////////////////////// -// Extracts the WaveForms from a DLS bank +// Extracts the Waveforms from a DLS/SF2 bank uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const { @@ -1492,9 +1516,10 @@ const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; for(uint32 rgn = 0; rgn < static_cast<uint32>(dlsIns.Regions.size()); rgn++) { - if(nKey < dlsIns.Regions[rgn].uKeyMin || nKey > dlsIns.Regions[rgn].uKeyMax) + const auto ®ion = dlsIns.Regions[rgn]; + if(nKey < region.uKeyMin || nKey > region.uKeyMax) continue; - if(dlsIns.Regions[rgn].fuOptions & DLSREGION_ISGLOBAL) + if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink)) continue; return rgn; } @@ -1590,8 +1615,8 @@ if(nIns >= m_Instruments.size()) return false; - const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; - if(nRgn >= pDlsIns->Regions.size()) + const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; + if(nRgn >= dlsIns.Regions.size()) return false; if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false; @@ -1603,7 +1628,7 @@ if (m_nType & SOUNDBANK_TYPE_SF2) { sndFile.DestroySample(nSample); - uint32 nWaveLink = pDlsIns->Regions[nRgn].nWaveLink; + uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink; ModSample &sample = sndFile.GetSample(nSample); if (sndFile.m_nSamples < nSample) sndFile.m_nSamples = nSample; if (nWaveLink < m_SamplesEx.size()) @@ -1614,15 +1639,15 @@ #endif sample.Initialize(); sample.nLength = dwLen / 2; - sample.nLoopStart = pDlsIns->Regions[nRgn].ulLoopStart; - sample.nLoopEnd = pDlsIns->Regions[nRgn].ulLoopEnd; + sample.nLoopStart = dlsIns.Regions[nRgn].ulLoopStart; + sample.nLoopEnd = dlsIns.Regions[nRgn].ulLoopEnd; sample.nC5Speed = p.dwSampleRate; sample.RelativeTone = p.byOriginalPitch; sample.nFineTune = p.chPitchCorrection; if (p.szName[0]) sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(p.szName); - else if(pDlsIns->szName[0]) - sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); + else if(dlsIns.szName[0]) + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName); FileReader chunk(mpt::as_span(pWaveForm.data(), dwLen)); SampleIO( @@ -1637,13 +1662,13 @@ { FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); - if(pDlsIns->szName[0]) - sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); + if(dlsIns.szName[0]) + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName); } if (hasWaveform) { ModSample &sample = sndFile.GetSample(nSample); - const DLSREGION &rgn = pDlsIns->Regions[nRgn]; + const DLSREGION &rgn = dlsIns.Regions[nRgn]; sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); if (rgn.fuOptions & DLSREGION_SAMPLELOOP) sample.uFlags.set(CHN_LOOP); if (rgn.fuOptions & DLSREGION_SUSTAINLOOP) sample.uFlags.set(CHN_SUSTAINLOOP); @@ -1725,35 +1750,34 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const { - uint32 nRgnMin, nRgnMax, nEnv; + uint32 minRegion, maxRegion, nEnv; - if (nIns >= m_Instruments.size()) return false; - const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; - std::vector<SAMPLEINDEX> RgnToSmp(pDlsIns->Regions.size()); - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if (nIns >= m_Instruments.size()) + return false; + const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; + const bool isDrum = (dlsIns.ulBank & F_INSTRUMENT_DRUMS); + if(isDrum) { - if(nDrumRgn >= pDlsIns->Regions.size()) + if(nDrumRgn >= dlsIns.Regions.size()) return false; - nRgnMin = nDrumRgn; - nRgnMax = nDrumRgn+1; - nEnv = pDlsIns->Regions[nDrumRgn].uPercEnv; + minRegion = nDrumRgn; + maxRegion = nDrumRgn + 1; + nEnv = dlsIns.Regions[nDrumRgn].uPercEnv; } else { - if(pDlsIns->Regions.empty()) + if(dlsIns.Regions.empty()) return false; - nRgnMin = 0; - nRgnMax = static_cast<uint32>(pDlsIns->Regions.size()); - nEnv = pDlsIns->nMelodicEnv; + minRegion = 0; + maxRegion = static_cast<uint32>(dlsIns.Regions.size()); + nEnv = dlsIns.nMelodicEnv; } - if(nRgnMin == 0 && (pDlsIns->Regions[0].fuOptions & DLSREGION_ISGLOBAL)) - nRgnMin++; #ifdef DLSINSTR_LOG - MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(pDlsIns->szName)))); - MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(pDlsIns->ulBank), mpt::ufmt::HEX0<4>(pDlsIns->ulInstrument))); - MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->Regions.size(), pDlsIns->nMelodicEnv)); - for (uint32 iDbg=0; iDbg<pDlsIns->Regions.size(); iDbg++) + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(dlsIns.szName)))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(dlsIns.ulBank), mpt::ufmt::HEX0<4>(dlsIns.ulInstrument))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(dlsIns.Regions.size(), dlsIns.nMelodicEnv)); + for (uint32 iDbg=0; iDbg<dlsIns.Regions.size(); iDbg++) { - const DLSREGION *prgn = &pDlsIns->Regions[iDbg]; + const DLSREGION *prgn = &dlsIns.Regions[iDbg]; MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Region %1:"))(iDbg)); MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" WaveLink = %1 (loop [%2, %3])"))(prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd)); MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Key Range: [%1, %2]"))(prgn->uKeyMin, prgn->uKeyMax)); @@ -1768,57 +1792,57 @@ return false; } - if (sndFile.Instruments[nInstr]) + if(sndFile.Instruments[nInstr]) { sndFile.DestroyInstrument(nInstr, deleteAssociatedSamples); } // Initializes Instrument - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if(isDrum) { - uint32 key = pDlsIns->Regions[nDrumRgn].uKeyMin; + uint32 key = dlsIns.Regions[nDrumRgn].uKeyMin; if((key >= 24) && (key <= 84)) { std::string s = szMidiPercussionNames[key-24]; - if(!mpt::String::ReadAutoBuf(pDlsIns->szName).empty()) + if(!mpt::String::ReadAutoBuf(dlsIns.szName).empty()) { - s += mpt::format(" (%1)")(mpt::String::RTrim<std::string>(mpt::String::ReadAutoBuf(pDlsIns->szName))); + s += mpt::format(" (%1)")(mpt::String::RTrim<std::string>(mpt::String::ReadAutoBuf(dlsIns.szName))); } pIns->name = s; } else { - pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); + pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName); } } else { - pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); + pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName); } - int nTranspose = 0; - if(pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + int transpose = 0; + if(isDrum) { for(uint32 iNoteMap = 0; iNoteMap < NOTE_MAX; iNoteMap++) { if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MID | MOD_TYPE_MPT)) { // Format has instrument note mapping - if(pDlsIns->Regions[nDrumRgn].tuning == 0) + if(dlsIns.Regions[nDrumRgn].tuning == 0) pIns->NoteMap[iNoteMap] = NOTE_MIDDLEC; - else if(iNoteMap < pDlsIns->Regions[nDrumRgn].uKeyMin) - pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMin + NOTE_MIN); - else if(iNoteMap > pDlsIns->Regions[nDrumRgn].uKeyMax) - pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMax + NOTE_MIN); + else if (iNoteMap < dlsIns.Regions[nDrumRgn].uKeyMin) + pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMin + NOTE_MIN); + else if(iNoteMap > dlsIns.Regions[nDrumRgn].uKeyMax) + pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMax + NOTE_MIN); } else { - if(iNoteMap == pDlsIns->Regions[nDrumRgn].uKeyMin) + if(iNoteMap == dlsIns.Regions[nDrumRgn].uKeyMin) { - nTranspose = (pDlsIns->Regions[nDrumRgn].uKeyMin + (pDlsIns->Regions[nDrumRgn].uKeyMax - pDlsIns->Regions[nDrumRgn].uKeyMin) / 2) - 60; + transpose = (dlsIns.Regions[nDrumRgn].uKeyMin + (dlsIns.Regions[nDrumRgn].uKeyMax - dlsIns.Regions[nDrumRgn].uKeyMin) / 2) - 60; } } } } pIns->nFadeOut = 1024; - pIns->nMidiProgram = (uint8)(pDlsIns->ulInstrument & 0x7F) + 1; - pIns->nMidiChannel = (uint8)((pDlsIns->ulBank & F_INSTRUMENT_DRUMS) ? 10 : 0); - pIns->wMidiBank = (uint16)(((pDlsIns->ulBank & 0x7F00) >> 1) | (pDlsIns->ulBank & 0x7F)); + pIns->nMidiProgram = (uint8)(dlsIns.ulInstrument & 0x7F) + 1; + pIns->nMidiChannel = (uint8)(isDrum ? 10 : 0); + pIns->wMidiBank = (uint16)(((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F)); pIns->nNNA = NNA_NOTEOFF; pIns->nDCT = DCT_NOTE; pIns->nDNA = DNA_NOTEFADE; @@ -1826,31 +1850,38 @@ uint32 nLoadedSmp = 0; SAMPLEINDEX nextSample = 0; // Extract Samples - for (uint32 nRgn=nRgnMin; nRgn<nRgnMax; nRgn++) + std::vector<SAMPLEINDEX> RgnToSmp(dlsIns.Regions.size()); + std::set<uint16> extractedSamples; + for(uint32 nRgn = minRegion; nRgn < maxRegion; nRgn++) { bool duplicateRegion = false; SAMPLEINDEX nSmp = 0; - const DLSREGION *pRgn = &pDlsIns->Regions[nRgn]; + const DLSREGION &rgn = dlsIns.Regions[nRgn]; + if(rgn.IsDummy()) + continue; // Elimitate Duplicate Regions - uint32 iDup; - for (iDup=nRgnMin; iDup<nRgn; iDup++) + uint32 dupRegion; + for(dupRegion = minRegion; dupRegion < nRgn; dupRegion++) { - const DLSREGION *pRgn2 = &pDlsIns->Regions[iDup]; - if (((pRgn2->nWaveLink == pRgn->nWaveLink) - && (pRgn2->ulLoopEnd == pRgn->ulLoopEnd) - && (pRgn2->ulLoopStart == pRgn->ulLoopStart)) - || ((pRgn2->uKeyMin == pRgn->uKeyMin) - && (pRgn2->uKeyMax == pRgn->uKeyMax))) + const DLSREGION &rgn2 = dlsIns.Regions[dupRegion]; + if(RgnToSmp[dupRegion] == 0 || rgn2.IsDummy()) + continue; + // No need to extract the same sample data twice + const bool sameSample = (rgn2.nWaveLink == rgn.nWaveLink) && (rgn2.ulLoopEnd == rgn.ulLoopEnd) && (rgn2.ulLoopStart == rgn.ulLoopStart) && extractedSamples.count(rgn.nWaveLink); + // Candidate for stereo sample creation + const bool sameKeyRange = (rgn2.uKeyMin == rgn.uKeyMin) && (rgn2.uKeyMax == rgn.uKeyMax); + if(sameSample || sameKeyRange) { duplicateRegion = true; - nSmp = RgnToSmp[iDup]; + if(!sameKeyRange) + nSmp = RgnToSmp[dupRegion]; break; } } // Create a new sample if (!duplicateRegion) { - uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : 32; + uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : (NOTE_MAX - NOTE_MIN + 1); if (nLoadedSmp >= nmaxsmp) { nSmp = RgnToSmp[nRgn - 1]; @@ -1866,40 +1897,41 @@ RgnToSmp[nRgn] = nSmp; // Map all notes to the right sample - if (nSmp) + if(nSmp) { - for (uint32 iKey=0; iKey<NOTE_MAX; iKey++) + for(uint8 key = 0; key < NOTE_MAX; key++) { - if ((nRgn == nRgnMin) || ((iKey >= pRgn->uKeyMin) && (iKey <= pRgn->uKeyMax))) + if(isDrum || (key >= rgn.uKeyMin && key <= rgn.uKeyMax)) { - pIns->Keyboard[iKey] = nSmp; + pIns->Keyboard[key] = nSmp; } } // Load the sample if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData()) { - ExtractSample(sndFile, nSmp, nIns, nRgn, nTranspose); - } else if(sndFile.GetSample(nSmp).GetNumChannels() == 1) - { - // Try to combine stereo samples - const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, iDup); - if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240)) - { - ModSample &sample = sndFile.GetSample(nSmp); - ctrlSmp::ConvertToStereo(sample, sndFile); - std::vector<uint8> pWaveForm; - uint32 dwLen = 0; - if(ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen) && dwLen >= sample.GetSampleSizeInBytes() / 2) + ExtractSample(sndFile, nSmp, nIns, nRgn, transpose); + extractedSamples.insert(rgn.nWaveLink); + } + } else if(duplicateRegion && sndFile.GetSample(nSmp).GetNumChannels() == 1) + { + // Try to combine stereo samples + const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, dupRegion); + if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240)) + { + ModSample &sample = sndFile.GetSample(nSmp); + ctrlSmp::ConvertToStereo(sample, sndFile); + std::vector<uint8> pWaveForm; + uint32 dwLen = 0; + if(ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen) && dwLen >= sample.GetSampleSizeInBytes() / 2) + { + SmpLength len = sample.nLength; + const int16 *src = reinterpret_cast<int16 *>(pWaveForm.data()); + int16 *dst = sample.sample16() + ((pan1 == 0) ? 0 : 1); + while(len--) { - SmpLength len = sample.nLength; - const int16 *src = reinterpret_cast<int16 *>(pWaveForm.data()); - int16 *dst = sample.sample16() + ((pan1 == 0) ? 0 : 1); - while(len--) - { - *dst = *src; - src++; - dst += 2; - } + *dst = *src; + src++; + dst += 2; } } } @@ -1910,27 +1942,26 @@ if(sndFile.m_nTempoMode == tempoModeModern) { uint32 ticksPerBeat = sndFile.m_nDefaultRowsPerBeat * sndFile.m_nDefaultSpeed; - if(ticksPerBeat == 0) - ticksPerBeat = 24; - tempoScale = ticksPerBeat / 24.0f; + if(ticksPerBeat != 0) + tempoScale = ticksPerBeat / 24.0f; } // Initializes Envelope if ((nEnv) && (nEnv <= m_Envelopes.size())) { - const DLSENVELOPE *part = &m_Envelopes[nEnv-1]; + const DLSENVELOPE &part = m_Envelopes[nEnv - 1]; // Volume Envelope - if ((part->wVolAttack) || (part->wVolDecay < 20*50) || (part->nVolSustainLevel) || (part->wVolRelease < 20*50)) + if ((part.wVolAttack) || (part.wVolDecay < 20*50) || (part.nVolSustainLevel) || (part.wVolRelease < 20*50)) { pIns->VolEnv.dwFlags.set(ENV_ENABLED); // Delay section // -> DLS level 2 // Attack section pIns->VolEnv.clear(); - if (part->wVolAttack) + if (part.wVolAttack) { - pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part->wVolAttack / 2 + 2) + 8)); // /----- - pIns->VolEnv.push_back(ScaleEnvelope(part->wVolAttack, tempoScale), ENVELOPE_MAX); // | + pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part.wVolAttack / 2 + 2) + 8)); // /----- + pIns->VolEnv.push_back(ScaleEnvelope(part.wVolAttack, tempoScale), ENVELOPE_MAX); // | } else { pIns->VolEnv.push_back(0, ENVELOPE_MAX); @@ -1938,24 +1969,24 @@ // Hold section // -> DLS Level 2 // Sustain Level - if (part->nVolSustainLevel > 0) + if (part.nVolSustainLevel > 0) { - if (part->nVolSustainLevel < 128) + if (part.nVolSustainLevel < 128) { uint16 lStartTime = pIns->VolEnv.back().tick; - int32 lSusLevel = - DLS32BitRelativeLinearToGain(part->nVolSustainLevel << 9) / 65536; + int32 lSusLevel = - DLS32BitRelativeLinearToGain(part.nVolSustainLevel << 9) / 65536; int32 lDecayTime = 1; if (lSusLevel > 0) { - lDecayTime = (lSusLevel * (int32)part->wVolDecay) / 960; + lDecayTime = (lSusLevel * (int32)part.wVolDecay) / 960; for (uint32 i=0; i<7; i++) { int32 lFactor = 128 - (1 << i); - if (lFactor <= part->nVolSustainLevel) break; + if (lFactor <= part.nVolSustainLevel) break; int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 9) / 65536; if (lev > 0) { - int32 ltime = (lev * (int32)part->wVolDecay) / 960; + int32 ltime = (lev * (int32)part.wVolDecay) / 960; if ((ltime > 1) && (ltime < lDecayTime)) { uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale); @@ -1971,7 +2002,7 @@ uint16 decayEnd = lStartTime + ScaleEnvelope(lDecayTime, tempoScale); if (decayEnd > pIns->VolEnv.back().tick) { - pIns->VolEnv.push_back(decayEnd, (uint8)((part->nVolSustainLevel+1) / 2)); + pIns->VolEnv.push_back(decayEnd, (uint8)((part.nVolSustainLevel+1) / 2)); } } pIns->VolEnv.dwFlags.set(ENV_SUSTAIN); @@ -1982,9 +2013,9 @@ } pIns->VolEnv.nSustainStart = pIns->VolEnv.nSustainEnd = (uint8)(pIns->VolEnv.size() - 1); // Release section - if ((part->wVolRelease) && (pIns->VolEnv.back().value > 1)) + if ((part.wVolRelease) && (pIns->VolEnv.back().value > 1)) { - int32 lReleaseTime = part->wVolRelease; + int32 lReleaseTime = part.wVolRelease; uint16 lStartTime = pIns->VolEnv.back().tick; int32 lStartFactor = pIns->VolEnv.back().value; int32 lSusLevel = - DLS32BitRelativeLinearToGain(lStartFactor << 10) / 65536; @@ -1999,7 +2030,7 @@ int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 10) / 65536; if (lev > 0) { - int32 ltime = (((int32)part->wVolRelease * lev) / 960) - lDecayEndTime; + int32 ltime = (((int32)part.wVolRelease * lev) / 960) - lDecayEndTime; if ((ltime > 1) && (ltime < lReleaseTime)) { uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale); @@ -2023,7 +2054,7 @@ } } } - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if(isDrum) { // Create a default envelope for drums pIns->VolEnv.dwFlags.reset(ENV_SUSTAIN); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Dlsbank.h new/libopenmpt-0.5.13+release.autotools/soundlib/Dlsbank.h --- old/libopenmpt-0.5.12+release.autotools/soundlib/Dlsbank.h 2021-08-23 23:33:58.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Dlsbank.h 2021-11-13 02:37:36.000000000 +0100 @@ -37,6 +37,8 @@ uint8 uKeyMax; uint8 uUnityNote; uint8 tuning = 100; + + constexpr bool IsDummy() const noexcept { return uKeyMin == 0xFF || nWaveLink == Util::MaxValueOfType(nWaveLink); } }; struct DLSENVELOPE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Load_imf.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Load_imf.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Load_imf.cpp 2020-10-31 13:58:24.000000000 +0100 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Load_imf.cpp 2021-11-13 02:20:37.000000000 +0100 @@ -16,11 +16,11 @@ struct IMFChannel { - char name[12]; // Channel name (ASCIIZ-String, max 11 chars) - uint8 chorus; // Default chorus - uint8 reverb; // Default reverb - uint8 panning; // Pan positions 00-FF - uint8 status; // Channel status: 0 = enabled, 1 = mute, 2 = disabled (ignore effects!) + char name[12]; // Channel name (ASCIIZ-String, max 11 chars) + uint8 chorus; // Default chorus + uint8 reverb; // Default reverb + uint8 panning; // Pan positions 00-FF + uint8 status; // Channel status: 0 = enabled, 1 = mute, 2 = disabled (ignore effects!) }; MPT_BINARY_STRUCT(IMFChannel, 16) @@ -32,19 +32,19 @@ linearSlides = 0x01, }; - char title[32]; // Songname (ASCIIZ-String, max. 31 chars) - uint16le ordNum; // Number of orders saved - uint16le patNum; // Number of patterns saved - uint16le insNum; // Number of instruments saved - uint16le flags; // See SongFlags + char title[32]; // Songname (ASCIIZ-String, max. 31 chars) + uint16le ordNum; // Number of orders saved + uint16le patNum; // Number of patterns saved + uint16le insNum; // Number of instruments saved + uint16le flags; // See SongFlags uint8le unused1[8]; - uint8le tempo; // Default tempo (Axx, 1...255) - uint8le bpm; // Default beats per minute (BPM) (Txx, 32...255) - uint8le master; // Default master volume (Vxx, 0...64) - uint8le amp; // Amplification factor (mixing volume, 4...127) + uint8le tempo; // Default tempo (Axx, 1...255) + uint8le bpm; // Default beats per minute (BPM) (Txx, 32...255) + uint8le master; // Default master volume (Vxx, 0...64) + uint8le amp; // Amplification factor (mixing volume, 4...127) uint8le unused2[8]; - char im10[4]; // 'IM10' - IMFChannel channels[32]; // Channel settings + char im10[4]; // 'IM10' + IMFChannel channels[32]; // Channel settings }; MPT_BINARY_STRUCT(IMFFileHeader, 576) @@ -53,16 +53,16 @@ { enum EnvFlags { - envEnabled = 0x01, - envSustain = 0x02, - envLoop = 0x04, + envEnabled = 0x01, + envSustain = 0x02, + envLoop = 0x04, }; - uint8 points; // Number of envelope points - uint8 sustain; // Envelope sustain point - uint8 loopStart; // Envelope loop start point - uint8 loopEnd; // Envelope loop end point - uint8 flags; // See EnvFlags + uint8 points; // Number of envelope points + uint8 sustain; // Envelope sustain point + uint8 loopStart; // Envelope loop start point + uint8 loopEnd; // Envelope loop end point + uint8 flags; // See EnvFlags uint8 unused[3]; }; @@ -80,19 +80,19 @@ { enum EnvTypes { - volEnv = 0, - panEnv = 1, + volEnv = 0, + panEnv = 1, filterEnv = 2, }; - char name[32]; // Inst. name (ASCIIZ-String, max. 31 chars) - uint8le map[120]; // Multisample settings + char name[32]; // Inst. name (ASCIIZ-String, max. 31 chars) + uint8le map[120]; // Multisample settings uint8le unused[8]; IMFEnvNode nodes[3][16]; IMFEnvelope env[3]; - uint16le fadeout; // Fadeout rate (0...0FFFH) - uint16le smpNum; // Number of samples in instrument - char ii10[4]; // 'II10' + uint16le fadeout; // Fadeout rate (0...0FFFH) + uint16le smpNum; // Number of samples in instrument + char ii10[4]; // 'II10' (not verified by Orpheus) void ConvertEnvelope(InstrumentEnvelope &mptEnv, EnvTypes e) const { @@ -114,6 +114,7 @@ minTick++; mptEnv[n].value = static_cast<uint8>(std::min(nodes[e][n].value >> shift, ENVELOPE_MAX)); } + mptEnv.Convert(MOD_TYPE_XM, MOD_TYPE_IT); } // Convert an IMFInstrument to OpenMPT's internal instrument representation. @@ -155,20 +156,20 @@ smpPanning = 0x08, }; - char filename[13]; // Sample filename (12345678.ABC) */ + char filename[13]; // Sample filename (12345678.ABC) */ uint8le unused1[3]; - uint32le length; // Length (in bytes) - uint32le loopStart; // Loop start (in bytes) - uint32le loopEnd; // Loop end (in bytes) - uint32le c5Speed; // Samplerate - uint8le volume; // Default volume (0...64) - uint8le panning; // Default pan (0...255) + uint32le length; // Length (in bytes) + uint32le loopStart; // Loop start (in bytes) + uint32le loopEnd; // Loop end (in bytes) + uint32le c5Speed; // Samplerate + uint8le volume; // Default volume (0...64) + uint8le panning; // Default pan (0...255) uint8le unused2[14]; - uint8le flags; // Sample flags + uint8le flags; // Sample flags uint8le unused3[5]; - uint16le ems; // Reserved for internal usage - uint32le dram; // Reserved for internal usage - char is10[4]; // 'IS10' + uint16le ems; // Reserved for internal usage + uint32le dram; // Reserved for internal usage + char is10[4]; // 'IS10' // Convert an IMFSample to OpenMPT's internal sample representation. void ConvertToMPT(ModSample &mptSmp) const @@ -255,7 +256,7 @@ { uint8 n; // fix some of them - switch (m.command) + switch(m.command) { case 0xE: // fine volslide // hackaround to get almost-right behavior for fine slides (i think!) @@ -278,7 +279,7 @@ case 0x15: // fine slide down // this is about as close as we can do... if(m.param >> 4) - m.param = 0xF0 | std::min(static_cast<uint8>(m.param >> 4), uint8(0x0F)); + m.param = 0xF0 | (m.param >> 4); else m.param |= 0xE0; break; @@ -353,8 +354,12 @@ static bool ValidateHeader(const IMFFileHeader &fileHeader) { if(std::memcmp(fileHeader.im10, "IM10", 4) - || fileHeader.ordNum > 256 - || fileHeader.insNum >= MAX_INSTRUMENTS) + || fileHeader.ordNum > 256 + || fileHeader.insNum >= MAX_INSTRUMENTS + || fileHeader.bpm < 32 + || fileHeader.master > 64 + || fileHeader.amp < 4 + || fileHeader.amp > 127) { return false; } @@ -480,8 +485,8 @@ m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; m_nDefaultTempo.Set(fileHeader.bpm); - m_nDefaultGlobalVolume = Clamp<uint8, uint8>(fileHeader.master, 0, 64) * 4; - m_nSamplePreAmp = Clamp<uint8, uint8>(fileHeader.amp, 4, 127); + m_nDefaultGlobalVolume = fileHeader.master * 4u; + m_nSamplePreAmp = fileHeader.amp; m_nInstruments = fileHeader.insNum; m_nSamples = 0; // Will be incremented later diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Load_med.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Load_med.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Load_med.cpp 2021-08-01 21:00:31.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Load_med.cpp 2021-10-24 20:30:44.000000000 +0200 @@ -1011,10 +1011,8 @@ } if(size > offsetof(MMDInstrExt, instrFlags)) { - if(instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) - sample.uFlags.set(CHN_LOOP); - if(instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) - sample.uFlags.set(CHN_LOOP | CHN_PINGPONGLOOP); + sample.uFlags.set(CHN_LOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) != 0); + sample.uFlags.set(CHN_PINGPONGLOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) != 0); if(instrExt.instrFlags & MMDInstrExt::SSFLG_DISABLED) sample.nGlobalVol = 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mo3.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mo3.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mo3.cpp 2021-03-20 14:08:00.000000000 +0100 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mo3.cpp 2021-10-26 20:40:00.000000000 +0200 @@ -1456,7 +1456,7 @@ // Note: Every Ogg stream has a unique serial number. // stb_vorbis (currently) ignores this serial number so we can just stitch // together our sample without adjusting the shared header's serial number. - const bool sharedHeader = sharedOggHeader != smp && sharedOggHeader > 0 && sharedOggHeader <= m_nSamples; + const bool sharedHeader = sharedOggHeader != smp && sharedOggHeader > 0 && sharedOggHeader <= m_nSamples && sampleChunk.headerSize > 0; #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mod.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mod.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mod.cpp 2021-06-12 22:45:46.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mod.cpp 2021-10-05 11:23:08.000000000 +0200 @@ -449,6 +449,8 @@ pitchEnv.dwFlags.set(ENV_ENABLED); pitchEnv.reserve(2); pitchEnv.push_back(0, ENVELOPE_MID); + // cppcheck false-positive + // cppcheck-suppress zerodiv pitchEnv.push_back(static_cast<EnvelopeNode::tick_t>(1024 / abs(pitchFall)), pitchFall > 0 ? ENVELOPE_MIN : ENVELOPE_MAX); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mt2.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mt2.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Load_mt2.cpp 2021-09-03 23:46:41.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Load_mt2.cpp 2021-10-29 20:46:12.000000000 +0200 @@ -477,11 +477,13 @@ ReadOrderFromArray(Order(), orders, fileHeader.numOrders); Order().SetRestartPos(fileHeader.restartPos); - FileReader drumData = file.ReadChunk(file.ReadUint16LE()); + // This value is supposed to be the size of the drums data, but in old MT2.0 files it's 8 bytes too small. + // MadTracker itself unconditionally reads 274 bytes here if the value is != 0, so we do the same. + const bool hasDrumChannels = file.ReadUint16LE() != 0; + FileReader drumData = file.ReadChunk(hasDrumChannels ? sizeof(MT2DrumsData) : 0); FileReader extraData = file.ReadChunk(file.ReadUint32LE()); const CHANNELINDEX channelsWithoutDrums = m_nChannels; - const bool hasDrumChannels = drumData.CanRead(sizeof(MT2DrumsData)); static_assert(MAX_BASECHANNELS >= 64 + 8); if(hasDrumChannels) { @@ -604,7 +606,10 @@ break; case MagicLE("TRKS"): - m_nSamplePreAmp = chunk.ReadUint16LE() / 256u; // 131072 is 0dB... I think (that's how MTIOModule_MT2.cpp reads) + m_nSamplePreAmp = chunk.ReadUint16LE() / 256u; // 131072 is 0dB... I think (that's how MTIOModule_MT2.cpp reads) + // Dirty workaround for modules that use track automation for a fade-in at the song start (e.g. Rock.mt2) + if(!m_nSamplePreAmp) + m_nSamplePreAmp = 48; m_nVSTiVolume = m_nSamplePreAmp / 2u; for(CHANNELINDEX c = 0; c < GetNumChannels(); c++) { @@ -736,11 +741,11 @@ chunk.ReadRaw(mixPlug.pluginData.data() + 4, vstHeader.n); } else { - float32 *f = reinterpret_cast<float32 *>(mixPlug.pluginData.data()); - *(f++) = 0; // Plugin data type - for(uint32 param = 0; param < vstHeader.n; param++, f++) + auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0)); + mpt::IO::WriteIntLE<uint32>(memFile, 0); // Plugin data type + for(uint32 param = 0; param < vstHeader.n; param++) { - *f = chunk.ReadFloatLE(); + mpt::IO::Write(memFile, IEEE754binary32LE{chunk.ReadFloatLE()}); } } } else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libopenmpt-0.5.12+release.autotools/soundlib/Snd_fx.cpp new/libopenmpt-0.5.13+release.autotools/soundlib/Snd_fx.cpp --- old/libopenmpt-0.5.12+release.autotools/soundlib/Snd_fx.cpp 2021-10-03 13:56:57.000000000 +0200 +++ new/libopenmpt-0.5.13+release.autotools/soundlib/Snd_fx.cpp 2021-11-14 17:12:57.000000000 +0100 @@ -1048,7 +1048,7 @@ startTick = playState.m_nMusicSpeed - 1; } else if(m.volcmd == VOLCMD_OFFSET) { - if(m.vol <= CountOf(chn.pModSample->cues) && chn.pModSample != nullptr) + if(chn.pModSample != nullptr && m.vol <= CountOf(chn.pModSample->cues)) { SmpLength offset; if(m.vol == 0) @@ -1965,7 +1965,7 @@ // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = chn.nLength = pSmp->nLength; - if(chn.dwFlags[CHN_REVERSE]) + if(chn.dwFlags[CHN_REVERSE] && chn.nLength > 0) { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.position.SetInt(chn.nLength - 1); @@ -3838,7 +3838,7 @@ // Regular Slide if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) - || GetType() == MOD_TYPE_669 + || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT)) || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, -int(param) * 4); @@ -3906,7 +3906,7 @@ if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) - || GetType() == MOD_TYPE_669 + || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT)) || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, int(param) * 4); @@ -4963,19 +4963,20 @@ // Process a MIDI Macro. // Parameters: -// [in] nChn: Mod channel to apply macro on -// [in] isSmooth: If true, internal macros are interpolated between two rows -// [in] macro: Actual MIDI Macro string -// [in] param: Parameter for parametric macros (Z00 - Z7F) -// [in] plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected) +// nChn: Mod channel to apply macro on +// isSmooth: If true, internal macros are interpolated between two rows +// macro: Actual MIDI Macro string +// param: Parameter for parametric macros (Z00 - Z7F) +// plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected) void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *macro, uint8 param, PLUGINDEX plugin) { ModChannel &chn = m_PlayState.Chn[nChn]; const ModInstrument *pIns = GetNumInstruments() ? chn.pModInstrument : nullptr; uint8 out[MACRO_LENGTH]; - uint32 outPos = 0; // output buffer position, which also equals the number of complete bytes - const uint8 lastZxxParam = chn.lastZxxParam; + uint32 outPos = 0; // output buffer position, which also equals the number of complete bytes + const uint8 lastZxxParam = chn.lastZxxParam; // always interpolate based on original value in case z appears multiple times in macro string + uint8 updateZxxParam = 0xFF; // avoid updating lastZxxParam immediately if macro contains both internal and external MIDI message bool firstNibble = true; for(uint32 pos = 0; pos < (MACRO_LENGTH - 1) && macro[pos]; pos++) @@ -5088,8 +5089,12 @@ // Interpolation for external MIDI messages - interpolation for internal messages // is handled separately to allow for more than 7-bit granularity where it's possible data = static_cast<uint8>(CalculateSmoothParamChange(lastZxxParam, data)); + chn.lastZxxParam = data; + updateZxxParam = 0x80; + } else if(updateZxxParam == 0xFF) + { + updateZxxParam = data; } - chn.lastZxxParam = data; } else if(macro[pos] == 's') { // SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience) @@ -5137,6 +5142,8 @@ // Finish current byte outPos++; } + if(updateZxxParam < 0x80) + chn.lastZxxParam = updateZxxParam; // Macro string has been parsed and translated, now send the message(s)... uint32 sendPos = 0; @@ -5384,11 +5391,9 @@ #endif // NO_PLUGINS return macroLen; - } return 0; - } @@ -5504,7 +5509,7 @@ // void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const { - if(chn.pModSample != nullptr) + if(chn.pModSample != nullptr && chn.nLength > 0) { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.reset(CHN_LOOP);
