Updated libopenmpt to version 0.5.13
This commit is contained in:
parent
b53d396a10
commit
4344f358c2
17 changed files with 370 additions and 299 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -46,6 +46,7 @@ static const char * const license =
|
|||
#include <limits>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
|
|
@ -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 ®ion : dlsIns.Regions)
|
||||
{
|
||||
if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink))
|
||||
continue;
|
||||
if(region.fuOptions & DLSREGION_ISGLOBAL)
|
||||
if(region.IsDummy())
|
||||
continue;
|
||||
|
||||
if((!key || key >= 0x80)
|
||||
|| (key >= region.uKeyMin && key <= region.uKeyMax))
|
||||
{
|
||||
if(pInsNo)
|
||||
*pInsNo = iIns;
|
||||
*pInsNo = static_cast<uint32>(std::distance(m_Instruments.data(), &dlsIns));
|
||||
// cppcheck false-positive
|
||||
// cppcheck-suppress returnDanglingLifetime
|
||||
return &dlsIns;
|
||||
}
|
||||
}
|
||||
|
@ -614,7 +619,9 @@ 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 ®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<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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue