Updated libopenmpt to version 0.5.13

CQTexperiment
Christopher Snowhill 2021-11-14 21:34:08 -08:00
parent b53d396a10
commit 4344f358c2
17 changed files with 370 additions and 299 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ) {

View File

@ -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<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 @@ void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, boo
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();
}

View File

@ -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 */

View File

@ -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

View File

@ -46,6 +46,7 @@ static const char * const license =
#include <limits>
#include <locale>
#include <map>
#include <memory>
#include <random>
#include <set>
#include <sstream>

View File

@ -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<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 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 p
{
for(const auto &region : 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 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 p
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 @@ bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &heade
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 @@ 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<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 @@ 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<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 @@ 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<int16>(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<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)
{
uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion;
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
{
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<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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
{
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 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
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 @@ 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<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 @@ 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<uint32>(dlsIns.Regions.size()); rgn++)
{
if(nKey < dlsIns.Regions[rgn].uKeyMin || nKey > dlsIns.Regions[rgn].uKeyMax)
const auto &region = 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<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 @@ 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<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 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
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 @@ 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<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)
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<uint8> 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<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 @@ 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);

View File

@ -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

View File

@ -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<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 @@ 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<uint8>(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<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

View File

@ -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;
}

View File

@ -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)

View File

@ -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<EnvelopeNode::tick_t>(1024 / abs(pitchFall)), pitchFall > 0 ? ENVELOPE_MIN : ENVELOPE_MAX);
}
}

View File

@ -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<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

View File

@ -1048,7 +1048,7 @@ std::vector<GetLengthType> 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<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 @@ 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);