From 4344f358c22daa45dcafd562b0a02e1027255bce Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 14 Nov 2021 21:34:08 -0800 Subject: [PATCH] Updated libopenmpt to version 0.5.13 --- Frameworks/OpenMPT/OpenMPT/build/dist.mk | 6 +- .../OpenMPT/build/svn_version/svn_version.h | 8 +- .../OpenMPT/OpenMPT/common/versionNumber.h | 2 +- .../OpenMPT/libopenmpt/dox/changelog.md | 18 + .../OpenMPT/libopenmpt/libopenmpt_c.cpp | 4 + .../OpenMPT/libopenmpt/libopenmpt_impl.cpp | 4 +- .../OpenMPT/libopenmpt/libopenmpt_version.h | 2 +- .../OpenMPT/libopenmpt/libopenmpt_version.mk | 4 +- .../OpenMPT/OpenMPT/openmpt123/openmpt123.cpp | 1 + .../OpenMPT/OpenMPT/soundlib/Dlsbank.cpp | 453 ++++++++++-------- Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h | 2 + .../OpenMPT/OpenMPT/soundlib/Load_imf.cpp | 101 ++-- .../OpenMPT/OpenMPT/soundlib/Load_med.cpp | 6 +- .../OpenMPT/OpenMPT/soundlib/Load_mo3.cpp | 2 +- .../OpenMPT/OpenMPT/soundlib/Load_mod.cpp | 2 + .../OpenMPT/OpenMPT/soundlib/Load_mt2.cpp | 19 +- .../OpenMPT/OpenMPT/soundlib/Snd_fx.cpp | 35 +- 17 files changed, 370 insertions(+), 299 deletions(-) diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index 32cbfc597..88c8ec901 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=15759 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12 -MPT_SVNDATE=2021-10-04T13:48:02.912129Z +MPT_SVNVERSION=15956 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.13 +MPT_SVNDATE=2021-11-14T17:01:47.266406Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 2d89fb7d8..27fb5aca7 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "15759" -#define OPENMPT_VERSION_REVISION 15759 +#define OPENMPT_VERSION_SVNVERSION "15956" +#define OPENMPT_VERSION_REVISION 15956 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12" -#define OPENMPT_VERSION_DATE "2021-10-04T13:48:02.912129Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.13" +#define OPENMPT_VERSION_DATE "2021-11-14T17:01:47.266406Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 451be4395..e43fb4972 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN // 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 --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 17e309db2..350fcc3c5 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,6 +5,22 @@ Changelog {#changelog} 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 @@ is just a high-level summary. * 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 --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp index 0ae46be2d..32d56e7fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp @@ -88,6 +88,8 @@ public: 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 @@ static int error_from_exception( const char * * error_message ) { } } try { + // cppcheck false-positive + // cppcheck-suppress rethrowNoCurrentException throw; } catch ( const std::bad_alloc & e ) { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp index 2539b18ad..800a84291 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp @@ -1806,7 +1806,7 @@ double module_impl::ctl_get_floatingpoint( std::string_view ctl, bool throw_if_u } return m_sndFile->m_nFreqFactor / 65536.0; } else if ( ctl == "render.opl.volume_factor" ) { - return static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( m_sndFile->m_OPLVolumeFactorScale ); + return static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( CSoundFile::m_OPLVolumeFactorScale ); } else { MPT_ASSERT_NOTREACHED(); return 0.0; @@ -2045,7 +2045,7 @@ void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, boo m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "render.opl.volume_factor" ) { - m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( value * static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); + m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( value * static_cast( CSoundFile::m_OPLVolumeFactorScale ) ); } else { MPT_ASSERT_NOTREACHED(); } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index a9efad396..d0c61778b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -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 --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index 4e981f6ab..2d8551b20 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -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 --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index 38ee80728..ceb93203e 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -46,6 +46,7 @@ static const char * const license = #include #include #include +#include #include #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index 3dc06874e..5cbe68ef5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -42,7 +42,6 @@ enum RegionFlags DLSREGION_SAMPLELOOP = 0x40, DLSREGION_SELFNONEXCLUSIVE = 0x80, DLSREGION_SUSTAINLOOP = 0x100, - DLSREGION_ISGLOBAL = 0x200, }; /////////////////////////////////////////////////////////////////////////// @@ -376,7 +375,7 @@ enum SF2Generators : uint16 ///////////////////////////////////////////////////////////////////// // SF2 Structures Definitions -struct SFPRESETHEADER +struct SFPresetHeader { char achPresetName[20]; uint16le wPreset; @@ -387,49 +386,49 @@ struct SFPRESETHEADER 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 @@ struct SFSAMPLE uint16le sfSampleType; }; -MPT_BINARY_STRUCT(SFSAMPLE, 46) +MPT_BINARY_STRUCT(SFSample, 46) // End of structures definitions ///////////////////////////////////////////////////////////////////// @@ -581,11 +580,17 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename) 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> 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 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 p { 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(std::distance(m_Instruments.data(), &dlsIns)); + // cppcheck false-positive + // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } @@ -614,7 +619,9 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 p if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { if(pInsNo) - *pInsNo = iIns; + *pInsNo = static_cast(std::distance(m_Instruments.data(), &dlsIns)); + // cppcheck false-positive + // cppcheck-suppress returnDanglingLifetime return &dlsIns; } } @@ -877,7 +884,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade case IFFID_phdr: if(m_Instruments.empty()) { - uint32 numIns = static_cast(chunk.GetLength() / sizeof(SFPRESETHEADER)); + uint32 numIns = static_cast(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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade #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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade 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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade 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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade 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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade case IFFID_shdr: if (m_SamplesEx.empty()) { - uint32 numSmp = static_cast(chunk.GetLength() / sizeof(SFSAMPLE)); + uint32 numSmp = static_cast(chunk.GetLength() / sizeof(SFSample)); if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); @@ -956,7 +963,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade 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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) if (m_Instruments.empty() || m_SamplesEx.empty()) return false; - const uint32 numPresetBags = static_cast(sf2info.presetBags.GetLength() / sizeof(SFPRESETBAG)); - const uint32 numPresetGens = static_cast(sf2info.presetGens.GetLength() / sizeof(SFGENLIST)); - const uint32 numInsts = static_cast(sf2info.insts.GetLength() / sizeof(SFINST)); - const uint32 numInstBags = static_cast(sf2info.instBags.GetLength() / sizeof(SFINSTBAG)); - const uint32 numInstGens = static_cast(sf2info.instGens.GetLength() / sizeof(SFINSTGENLIST)); + const uint32 numInsts = static_cast(sf2info.insts.GetLength() / sizeof(SFInst)); + const uint32 numInstBags = static_cast(sf2info.instBags.GetLength() / sizeof(SFInstBag)); - for (auto &dlsIns : m_Instruments) + std::vector> instruments; // instrument, key range + std::vector generators; + std::vector instrGenerators; + for(auto &dlsIns : m_Instruments) { + instruments.clear(); DLSENVELOPE dlsEnv; - std::vector instruments; int32 instrAttenuation = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; @@ -1024,21 +1031,21 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) 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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) 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(gen.genAmount); @@ -1081,22 +1092,35 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) } // 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(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) { - uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion; + globalZone.uKeyMin = static_cast(keyRange & 0xFF); + globalZone.uKeyMax = static_cast(keyRange >> 8); + if(globalZone.uKeyMin > globalZone.uKeyMax) + std::swap(globalZone.uKeyMin, globalZone.uKeyMax); + } else + { + 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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) { 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(value & 0xFF); + uint8 keyMax = static_cast(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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) { int32 pan = static_cast(value); pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256); - pRgn->panning = static_cast(pan); + rgn.panning = static_cast(pan); pDlsEnv->nDefPan = mpt::saturate_cast(pan); } break; @@ -1172,33 +1196,33 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) case SF2_GEN_SAMPLEID: if (value < m_SamplesEx.size()) { - pRgn->nWaveLink = value; - pRgn->ulLoopStart = mpt::saturate_cast(m_SamplesEx[value].dwStartloop + loopStart); - pRgn->ulLoopEnd = mpt::saturate_cast(m_SamplesEx[value].dwEndloop + loopEnd); + rgn.nWaveLink = value; + rgn.ulLoopStart = mpt::saturate_cast(m_SamplesEx[value].dwStartloop + loopStart); + rgn.ulLoopEnd = mpt::saturate_cast(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(value) * 128; + rgn.sFineTune += static_cast(value) * 128; break; case SF2_GEN_FINETUNE: - pRgn->sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); + rgn.sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); break; case SF2_GEN_SCALE_TUNING: - pRgn->tuning = mpt::saturate_cast(value); + rgn.tuning = mpt::saturate_cast(value); break; case SF2_GEN_START_LOOP_FINE: loopStart += static_cast(value); @@ -1216,14 +1240,14 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) // 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(linearVol); + rgn.usVolume = static_cast(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 @@ bool CDLSBank::Open(FileReader file) 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 @@ bool CDLSBank::Open(FileReader file) } //////////////////////////////////////////////////////////////////////////////////////// -// 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 @@ uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; for(uint32 rgn = 0; rgn < static_cast(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 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI 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 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI 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 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI #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 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI { 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 @@ static uint16 ScaleEnvelope(uint32 time, float tempoScale) 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 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(pDlsIns->Regions.size()); - nEnv = pDlsIns->nMelodicEnv; + minRegion = 0; + maxRegion = static_cast(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; iDbgRegions.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; iDbgRegions[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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui 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(mpt::String::ReadAutoBuf(pDlsIns->szName))); + s += mpt::format(" (%1)")(mpt::String::RTrim(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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui uint32 nLoadedSmp = 0; SAMPLEINDEX nextSample = 0; // Extract Samples - for (uint32 nRgn=nRgnMin; nRgn RgnToSmp(dlsIns.Regions.size()); + std::set 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; iDupRegions[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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui RgnToSmp[nRgn] = nSmp; // Map all notes to the right sample - if (nSmp) + if(nSmp) { - for (uint32 iKey=0; 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) + 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)) { - // 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 pWaveForm; + uint32 dwLen = 0; + if(ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen) && dwLen >= sample.GetSampleSizeInBytes() / 2) { - ModSample &sample = sndFile.GetSample(nSmp); - ctrlSmp::ConvertToStereo(sample, sndFile); - std::vector pWaveForm; - uint32 dwLen = 0; - if(ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen) && dwLen >= sample.GetSampleSizeInBytes() / 2) + SmpLength len = sample.nLength; + const int16 *src = reinterpret_cast(pWaveForm.data()); + int16 *dst = sample.sample16() + ((pan1 == 0) ? 0 : 1); + while(len--) { - SmpLength len = sample.nLength; - const int16 *src = reinterpret_cast(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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui 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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // 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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui 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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } 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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui 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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } } } - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if(isDrum) { // Create a default envelope for drums pIns->VolEnv.dwFlags.reset(ENV_SUSTAIN); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index 4a865cf47..d835fc363 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -37,6 +37,8 @@ struct DLSREGION uint8 uKeyMax; uint8 uUnityNote; uint8 tuning = 100; + + constexpr bool IsDummy() const noexcept { return uKeyMin == 0xFF || nWaveLink == Util::MaxValueOfType(nWaveLink); } }; struct DLSENVELOPE diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp index 202b0a3aa..e2637a2e6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp @@ -16,11 +16,11 @@ OPENMPT_NAMESPACE_BEGIN 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 @@ struct IMFFileHeader 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 @@ struct IMFEnvelope { 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 @@ struct IMFInstrument { 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 @@ struct IMFInstrument minTick++; mptEnv[n].value = static_cast(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 @@ struct IMFSample 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 @@ static void ImportIMFEffect(ModCommand &m) { 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 @@ static void ImportIMFEffect(ModCommand &m) 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(m.param >> 4), uint8(0x0F)); + m.param = 0xF0 | (m.param >> 4); else m.param |= 0xE0; break; @@ -353,8 +354,12 @@ static void ImportIMFEffect(ModCommand &m) 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 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; m_nDefaultTempo.Set(fileHeader.bpm); - m_nDefaultGlobalVolume = Clamp(fileHeader.master, 0, 64) * 4; - m_nSamplePreAmp = Clamp(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 --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 7910d455d..de6f01102 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -1011,10 +1011,8 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) } 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 --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index 1ff0c39f5..387c7dacf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -1456,7 +1456,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // 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 --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 00fc811b2..377e7a1e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -449,6 +449,8 @@ struct AMInstrument 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(1024 / abs(pitchFall)), pitchFall > 0 ? ENVELOPE_MIN : ENVELOPE_MAX); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index 2d8eb03b6..a95122733 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -477,11 +477,13 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) 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 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) 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 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadRaw(mixPlug.pluginData.data() + 4, vstHeader.n); } else { - float32 *f = reinterpret_cast(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(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 --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index ab40c0165..24f6fe7c1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -1048,7 +1048,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod 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 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE // 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 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const // 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 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons 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 @@ void CSoundFile::InvertLoop(ModChannel &chn) // 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 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * // 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(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 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * // 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 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned #endif // NO_PLUGINS return macroLen; - } return 0; - } @@ -5504,7 +5509,7 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const // 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);