Update libopenmpt to version 0.5.4

CQTexperiment
Christopher Snowhill 2020-12-04 16:22:42 -08:00
parent b375f06faa
commit 24231ecdbb
29 changed files with 464 additions and 330 deletions

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=13775
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3
MPT_SVNDATE=2020-10-25T14:02:16.624929Z
MPT_SVNVERSION=13932
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.4
MPT_SVNDATE=2020-11-29T15:01:39.790705Z

View File

@ -1,10 +1,10 @@
#pragma once
#define OPENMPT_VERSION_SVNVERSION "13775"
#define OPENMPT_VERSION_REVISION 13775
#define OPENMPT_VERSION_SVNVERSION "13932"
#define OPENMPT_VERSION_REVISION 13932
#define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3"
#define OPENMPT_VERSION_DATE "2020-10-25T14:02:16.624929Z"
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.4"
#define OPENMPT_VERSION_DATE "2020-11-29T15:01:39.790705Z"
#define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -423,7 +423,7 @@ std::shared_ptr<const type> ReloadComponent()
}
static inline mpt::PathString GetComponentPath()
inline mpt::PathString GetComponentPath()
{
return ComponentManager::Instance()->GetComponentPath();
}
@ -454,7 +454,7 @@ std::shared_ptr<const type> GetComponent()
}
static inline mpt::PathString GetComponentPath()
inline mpt::PathString GetComponentPath()
{
return mpt::PathString();
}

View File

@ -44,22 +44,22 @@ using std::endian;
static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported");
static constexpr mpt::endian get_endian() noexcept
constexpr mpt::endian get_endian() noexcept
{
return mpt::endian::native;
}
static constexpr bool endian_is_little() noexcept
constexpr bool endian_is_little() noexcept
{
return get_endian() == mpt::endian::little;
}
static constexpr bool endian_is_big() noexcept
constexpr bool endian_is_big() noexcept
{
return get_endian() == mpt::endian::big;
}
static constexpr bool endian_is_weird() noexcept
constexpr bool endian_is_weird() noexcept
{
return !endian_is_little() && !endian_is_big();
}
@ -128,7 +128,7 @@ static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar
namespace detail {
static MPT_FORCEINLINE mpt::endian endian_probe() noexcept
MPT_FORCEINLINE mpt::endian endian_probe() noexcept
{
using endian_probe_type = uint32;
static_assert(sizeof(endian_probe_type) == 4);
@ -154,7 +154,7 @@ namespace detail {
} // namespace detail
static MPT_FORCEINLINE mpt::endian get_endian() noexcept
MPT_FORCEINLINE mpt::endian get_endian() noexcept
{
#if MPT_COMPILER_MSVC
#pragma warning(push)
@ -172,17 +172,17 @@ static MPT_FORCEINLINE mpt::endian get_endian() noexcept
#endif // MPT_COMPILER_MSVC
}
static MPT_FORCEINLINE bool endian_is_little() noexcept
MPT_FORCEINLINE bool endian_is_little() noexcept
{
return get_endian() == mpt::endian::little;
}
static MPT_FORCEINLINE bool endian_is_big() noexcept
MPT_FORCEINLINE bool endian_is_big() noexcept
{
return get_endian() == mpt::endian::big;
}
static MPT_FORCEINLINE bool endian_is_weird() noexcept
MPT_FORCEINLINE bool endian_is_weird() noexcept
{
return !endian_is_little() && !endian_is_big();
}
@ -265,19 +265,19 @@ namespace mpt { namespace detail {
// catch system macros
#ifndef MPT_bswap16
#ifdef bswap16
static MPT_FORCEINLINE uint16 mpt_bswap16(uint16 x) { return bswap16(x); }
MPT_FORCEINLINE uint16 mpt_bswap16(uint16 x) { return bswap16(x); }
#define MPT_bswap16 mpt::detail::mpt_bswap16
#endif
#endif
#ifndef MPT_bswap32
#ifdef bswap32
static MPT_FORCEINLINE uint32 mpt_bswap32(uint32 x) { return bswap32(x); }
MPT_FORCEINLINE uint32 mpt_bswap32(uint32 x) { return bswap32(x); }
#define MPT_bswap32 mpt::detail::mpt_bswap32
#endif
#endif
#ifndef MPT_bswap64
#ifdef bswap64
static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); }
MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); }
#define MPT_bswap64 mpt::detail::mpt_bswap64
#endif
#endif
@ -297,7 +297,7 @@ static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); }
template <typename T, typename Tendian, std::size_t size>
static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept
MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept
{
static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
static_assert(std::numeric_limits<T>::is_integer);
@ -325,7 +325,7 @@ static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexc
}
template <typename T, typename Tendian, std::size_t size>
static MPT_CONSTEXPR17_FUN T EndianDecode(std::array<std::byte, size> data) noexcept
MPT_CONSTEXPR17_FUN T EndianDecode(std::array<std::byte, size> data) noexcept
{
static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
static_assert(std::numeric_limits<T>::is_integer);
@ -359,20 +359,20 @@ namespace mpt
namespace detail
{
static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } }
static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } }
static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } }
static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } }
static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } }
static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } }
MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } }
MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } }
MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } }
MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } }
MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } }
MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } }
// Do NOT remove these overloads, even if they seem useless.
// We do not want risking to extend 8bit integers to int and then
// endian-converting and casting back to int.
// Thus these overloads.
static MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; }
static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; }
static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; }
MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; }
MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; }
MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; }
} // namespace detail
} // namespace mpt
@ -387,7 +387,7 @@ static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return valu
// 1.0f --> 0x3f800000u
static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f)
MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f)
{
if constexpr(mpt::float_traits<float32>::is_ieee754_binary32ne)
{
@ -419,7 +419,7 @@ static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f)
}
}
}
static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f)
MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f)
{
if constexpr(mpt::float_traits<float64>::is_ieee754_binary64ne)
{
@ -453,7 +453,7 @@ static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f)
}
// 0x3f800000u --> 1.0f
static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i)
MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i)
{
if constexpr(mpt::float_traits<float32>::is_ieee754_binary32ne)
{
@ -479,7 +479,7 @@ static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i)
}
}
}
static MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i)
MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i)
{
if constexpr(mpt::float_traits<float64>::is_ieee754_binary64ne)
{

View File

@ -1348,7 +1348,7 @@ using MemoryFileReader = detail::FileReader<FileReaderTraitsMemory>;
// Initialize file reader object with pointer to data and data length.
template <typename Tbyte> static inline FileReader make_FileReader(mpt::span<Tbyte> bytedata, const mpt::PathString *filename = nullptr)
template <typename Tbyte> inline FileReader make_FileReader(mpt::span<Tbyte> bytedata, const mpt::PathString *filename = nullptr)
{
return FileReader(mpt::byte_cast<mpt::const_byte_span>(bytedata), filename);
}
@ -1356,7 +1356,7 @@ template <typename Tbyte> static inline FileReader make_FileReader(mpt::span<Tby
#if defined(MPT_FILEREADER_CALLBACK_STREAM)
// Initialize file reader object with a CallbackStream.
static inline FileReader make_FileReader(CallbackStream s, const mpt::PathString *filename = nullptr)
inline FileReader make_FileReader(CallbackStream s, const mpt::PathString *filename = nullptr)
{
return FileReader(
FileDataContainerCallbackStreamSeekable::IsSeekable(s) ?
@ -1369,7 +1369,7 @@ static inline FileReader make_FileReader(CallbackStream s, const mpt::PathString
#endif // MPT_FILEREADER_CALLBACK_STREAM
// Initialize file reader object with a std::istream.
static inline FileReader make_FileReader(std::istream *s, const mpt::PathString *filename = nullptr)
inline FileReader make_FileReader(std::istream *s, const mpt::PathString *filename = nullptr)
{
return FileReader(
FileDataContainerStdStreamSeekable::IsSeekable(s) ?

View File

@ -126,7 +126,7 @@ extern bool ConsoleEnabled;
void SetFacilities(const std::string &solo, const std::string &blocked);
bool IsFacilityActive(const char *facility);
#else
static MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; }
MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; }
#endif
@ -179,7 +179,7 @@ namespace Trace {
// if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz),
// which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all.
extern std::atomic<bool> g_Enabled;
static inline bool IsEnabled() { return g_Enabled; }
inline bool IsEnabled() { return g_Enabled; }
enum class Direction : int8
{

View File

@ -52,13 +52,13 @@ extern uint8 ProcStepping;
void InitProcSupport();
// enabled processor features for inline asm and intrinsics
static inline uint32 GetProcSupport()
inline uint32 GetProcSupport()
{
return ProcSupport;
}
// available processor features
static inline uint32 GetRealProcSupport()
inline uint32 GetRealProcSupport()
{
return RealProcSupport;
}

View File

@ -169,7 +169,7 @@ enum class FlushMode
Full = 2, // explicitly flush *all* layers, up to and including disk write caches
};
static inline FlushMode FlushModeFromBool(bool flush)
inline FlushMode FlushModeFromBool(bool flush)
{
return flush ? FlushMode::Full : FlushMode::None;
}

View File

@ -277,17 +277,17 @@ public:
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#if MPT_OS_WINDOWS
#ifdef UNICODE
[[deprecated]] static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
[[deprecated]] inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
#else
MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); }
MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); }
#endif
#else
MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
#endif
#endif
static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); }
inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); }
#if MPT_WSTRING_FORMAT
static inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); }
inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); }
#endif
} // namespace mpt

View File

@ -958,7 +958,7 @@ static widestring FromUTF8(const Tsrcstring &str, widechar replacement = wide_de
if ( charsleft == 0 ) {
if ( ( c & 0x80 ) == 0x00 ) {
out.push_back( (wchar_t)c );
out.push_back( (widechar)c );
} else if ( ( c & 0xE0 ) == 0xC0 ) {
ucs4 = c & 0x1F;
charsleft = 1;
@ -1030,7 +1030,7 @@ static Tdststring ToUTF8(const widestring &str, char replacement = '?')
for ( std::size_t i=0; i<in.length(); i++ ) {
wchar_t wc = in[i];
widechar wc = in[i];
char32_t ucs4 = 0;
if constexpr ( sizeof( widechar ) == 2 ) {

View File

@ -179,7 +179,7 @@ inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &new
} // namespace String
static inline std::string truncate(std::string str, std::size_t maxLen)
inline std::string truncate(std::string str, std::size_t maxLen)
{
if(str.length() > maxLen)
{
@ -343,10 +343,10 @@ using u8string = MPT_ENCODED_STRING_TYPE(mpt::Charset::UTF8);
// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t).
// If str does not contain any invalid characters, this conversion is lossless.
// Invalid source bytes will be replaced by some replacement character or string.
static inline std::wstring ToWide(const std::wstring &str) { return str; }
static inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
inline std::wstring ToWide(const std::wstring &str) { return str; }
inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
std::wstring ToWide(Charset from, const std::string &str);
static inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); }
inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::wstring ToWide(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
@ -360,10 +360,10 @@ std::wstring ToWide(const mpt::lstring &str);
// destination charset will be replaced by some replacement character or string.
#if MPT_WSTRING_CONVERT
std::string ToCharset(Charset to, const std::wstring &str);
static inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); }
inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); }
#endif
std::string ToCharset(Charset to, Charset from, const std::string &str);
static inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); }
inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::string ToCharset(Charset to, const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
@ -371,20 +371,20 @@ std::string ToCharset(Charset to, const mpt::lstring &str);
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#if MPT_WSTRING_CONVERT
mpt::lstring ToLocale(const std::wstring &str);
static inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); }
inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); }
#endif
mpt::lstring ToLocale(Charset from, const std::string &str);
static inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); }
static inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; }
inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); }
inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; }
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
#if MPT_WSTRING_CONVERT
mpt::winstring ToWin(const std::wstring &str);
static inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); }
inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); }
#endif
mpt::winstring ToWin(Charset from, const std::string &str);
static inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); }
inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::winstring ToWin(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
@ -399,11 +399,11 @@ mpt::winstring ToWin(const mpt::lstring &str);
// Convert to a MFC CString. The CString encoding depends on UNICODE.
// This should also be used when converting to TCHAR strings.
// If UNICODE is defined, this is a completely lossless operation.
static inline CString ToCString(const CString &str) { return str; }
inline CString ToCString(const CString &str) { return str; }
CString ToCString(const std::wstring &str);
static inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); }
inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); }
CString ToCString(Charset from, const std::string &str);
static inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); }
inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
CString ToCString(const mpt::lstring &str);
mpt::lstring ToLocale(const CString &str);
@ -476,24 +476,24 @@ using uchar = MPT_U8CHAR_TYPE;
#if !(MPT_WSTRING_CONVERT)
#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)"
#endif
static inline mpt::ustring ToUnicode(const std::wstring &str) { return str; }
static inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
static inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); }
static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
inline mpt::ustring ToUnicode(const std::wstring &str) { return str; }
inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); }
inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
static inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); }
inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); }
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); }
inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); }
#endif // MFC
#else // !MPT_USTRING_MODE_WIDE
static inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; }
inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; }
#if MPT_WSTRING_CONVERT
mpt::ustring ToUnicode(const std::wstring &str);
static inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); }
inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); }
#endif
mpt::ustring ToUnicode(Charset from, const std::string &str);
static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::ustring ToUnicode(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE

View File

@ -76,8 +76,8 @@ template <typename T> auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt:
template <typename T> auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString()); }
#endif
static inline std::string ToString(const std::string & x) { return x; }
static inline std::string ToString(const char * const & x) { return x; }
inline std::string ToString(const std::string & x) { return x; }
inline std::string ToString(const char * const & x) { return x; }
std::string ToString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if MPT_WSTRING_FORMAT
std::string ToString(const std::wstring & x) = delete; // Unknown encoding.
@ -108,7 +108,7 @@ std::string ToString(const long double & x);
// fallback to member function ToUString()
template <typename T> auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); }
static inline mpt::ustring ToUString(const mpt::ustring & x) { return x; }
inline mpt::ustring ToUString(const mpt::ustring & x) { return x; }
mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding.
mpt::ustring ToUString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
mpt::ustring ToUString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
@ -141,8 +141,8 @@ mpt::ustring ToUString(const long double & x);
std::wstring ToWString(const std::string & x) = delete; // Unknown encoding.
std::wstring ToWString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
static inline std::wstring ToWString(const std::wstring & x) { return x; }
static inline std::wstring ToWString(const wchar_t * const & x) { return x; }
inline std::wstring ToWString(const std::wstring & x) { return x; }
inline std::wstring ToWString(const wchar_t * const & x) { return x; }
std::wstring ToWString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#if MPT_USTRING_MODE_UTF8
std::wstring ToWString(const mpt::ustring & x);

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 05
#define VER_MINOR 06
#define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END

View File

@ -5,6 +5,20 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
### libopenmpt 0.5.4 (2020-11-29)
* AMS: An upper bound for uncompressed sample size is now established to
avoid memory exhaustion from malformed files.
* DMF: Support early format beta versions (in particular versions 1-4).
* MED: Also use octave wrapping in 8-channel mode for MMD0/MMD1 modules.
* MED: If 8-channel mode is activated, ignore BPM mode.
* MED: Emulate tempo commands F01 and F02 quirk.
* MED: Tempo commands below 32 BPM were interpreted as tempo slides.
* IMF: Instrument sample mapping was off by one octave, notable in the guitar
part of Astaris by Karsten Koch.
* pugixml: Update to v1.11 (2020-11-26).
### libopenmpt 0.5.3 (2020-10-25)
* [**Sec**] Possible hang if a MED file claimed to contain 256 songs. (r13704)

View File

@ -14,8 +14,6 @@
#include "libopenmpt_impl.hpp"
#include "libopenmpt_ext.hpp"
using namespace OpenMPT;
namespace openmpt {
class module_ext_impl

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 3
#define OPENMPT_API_VERSION_PATCH 4
/*! \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=3
LIBOPENMPT_VERSION_PATCH=4
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=3
LIBOPENMPT_LTVER_REVISION=4
LIBOPENMPT_LTVER_AGE=2

View File

@ -2,12 +2,12 @@
* load_dmf.cpp
* ------------
* Purpose: DMF module loader (X-Tracker by D-LUSiON).
* Notes : If it wasn't already outdated when the tracker was released, this would be a rather interesting
* Notes : If it wasn't already outdated when the tracker left beta state, this would be a rather interesting
* and in some parts even sophisticated format - effect columns are separated by effect type, an easy to
* understand BPM tempo mode, effect durations are always divided into a 256th row, vibrato effects are
* specified by period length and the same 8-Bit granularity is used for both volume and panning.
* Unluckily, this format does not offer any envelopes or multi-sample instruments, and bidi sample loops
* are missing as well, so it was already well behind FT2 and IT back then.
* are missing as well, so it was already well behind FT2 back then.
* Authors: Johannes Schultz (mostly based on DMF.TXT, DMF_EFFC.TXT, trial and error and some invaluable hints by Zatzen)
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
@ -66,16 +66,6 @@ struct DMFChunk
MPT_BINARY_STRUCT(DMFChunk, 8)
// Order list
struct DMFSequence
{
uint16le loopStart;
uint16le loopEnd;
// order list follows here ...
};
MPT_BINARY_STRUCT(DMFSequence, 4)
// Pattern header (global)
struct DMFPatterns
{
@ -151,15 +141,6 @@ struct DMFSampleHeader
MPT_BINARY_STRUCT(DMFSampleHeader, 16)
// Sample header tail (between head and tail, there might be the library name of the sample, depending on the DMF version)
struct DMFSampleHeaderTail
{
uint16le filler;
uint32le crc32;
};
MPT_BINARY_STRUCT(DMFSampleHeaderTail, 6)
// Pattern translation memory
struct DMFPatternSettings
@ -250,9 +231,7 @@ static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks)
static void ApplyEffectMemory(const ModCommand *m, ROWINDEX row, CHANNELINDEX numChannels, uint8 effect, uint8 &param)
{
if(effect == CMD_NONE || param == 0)
{
return;
}
const bool isTonePortaEffect = (effect == CMD_PORTAMENTOUP || effect == CMD_PORTAMENTODOWN || effect == CMD_TONEPORTAMENTO);
const bool isVolSlideEffect = (effect == CMD_VOLUMESLIDE || effect == CMD_TONEPORTAVOL || effect == CMD_VIBRATOVOL);
@ -309,7 +288,7 @@ static void ApplyEffectMemory(const ModCommand *m, ROWINDEX row, CHANNELINDEX nu
}
static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &settings, CSoundFile &sndFile)
static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion, DMFPatternSettings &settings, CSoundFile &sndFile)
{
// Pattern flags
enum PatternFlags
@ -330,7 +309,18 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett
file.Rewind();
DMFPatternHeader patHead;
if(fileVersion < 3)
{
patHead.numTracks = file.ReadUint8();
file.Skip(2); // not sure what this is, later X-Tracker versions just skip over it
patHead.numRows = file.ReadUint16LE();
patHead.patternLength = file.ReadUint32LE();
} else
{
file.ReadStruct(patHead);
}
if(fileVersion < 6)
patHead.beat = 0;
const ROWINDEX numRows = Clamp(ROWINDEX(patHead.numRows), ROWINDEX(1), MAX_PATTERN_ROWS);
const PATTERNINDEX pat = sndFile.Patterns.InsertAny(numRows);
@ -778,13 +768,9 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett
// Let's see if we can help the effect swapper by reducing some effect parameters to "continue" parameters.
if(useMem2)
{
ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect2, effectParam2);
}
if(useMem3)
{
ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect3, effectParam3);
}
// I guess this is close enough to "not retriggering the note"
if(slideNote && m->IsNote())
@ -925,19 +911,43 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
m_FileHistory.clear();
m_FileHistory.push_back(mptHistory);
// Go through all chunks now
ChunkReader chunkFile(file);
ChunkReader::ChunkList<DMFChunk> chunks = chunkFile.ReadChunks<DMFChunk>(1);
// Go through all chunks now... cannot use our standard IFF chunk reader here because early X-Tracker versions write some malformed chunk headers... fun code ahead!
ChunkReader::ChunkList<DMFChunk> chunks;
while(file.CanRead(sizeof(DMFChunk)))
{
DMFChunk chunkHeader;
file.Read(chunkHeader);
uint32 chunkLength = chunkHeader.length, chunkSkip = 0;
// When loop start was added to version 3, the chunk size was not updated...
if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 2)
chunkSkip = 2;
// ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5.
else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 4)
chunkSkip = 4;
// Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed.
// I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions).
// Since this is practically always the last chunk in the file, the following code is safe for those versions, though.
else if(fileHeader.version < 8 && chunkHeader.GetID() == DMFChunk::idSMPD)
chunkLength = uint32_max;
chunks.emplace_back(chunkHeader, file.ReadChunk(chunkLength));
file.Skip(chunkSkip);
}
FileReader chunk;
// Read order list
DMFSequence seqHeader;
chunk = chunks.GetChunk(DMFChunk::idSEQU);
if(!chunk.ReadStruct(seqHeader))
{
return false;
}
ReadOrderFromFile<uint16le>(Order(), chunk, (chunk.GetLength() - sizeof(DMFSequence)) / 2);
ORDERINDEX seqLoopStart = 0, seqLoopEnd = ORDERINDEX_MAX;
if(fileHeader.version >= 3)
seqLoopStart = chunk.ReadUint16LE();
if(fileHeader.version >= 4)
seqLoopEnd = chunk.ReadUint16LE();
// HIPOMATK.DMF has a loop end of 0, other v4 files have proper loop ends. Later X-Tracker versions import it as-is but it cannot be intentional.
// We just assume that this feature might have been buggy in early v4 versions and ignore the loop end in that case.
if(fileHeader.version == 4 && seqLoopEnd == 0)
seqLoopEnd = ORDERINDEX_MAX;
ReadOrderFromFile<uint16le>(Order(), chunk, chunk.BytesLeft() / 2);
LimitMax(seqLoopStart, Order().GetLastIndex());
LimitMax(seqLoopEnd, Order().GetLastIndex());
// Read patterns
chunk = chunks.GetChunk(DMFChunk::idPATT);
@ -951,10 +961,11 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
std::vector<FileReader> patternChunks(patHeader.numPatterns);
for(auto &patternChunk : patternChunks)
{
DMFPatternHeader header;
chunk.ReadStruct(header);
chunk.SkipBack(sizeof(header));
patternChunk = chunk.ReadChunk(sizeof(header) + header.patternLength);
const uint8 headerSize = fileHeader.version < 3 ? 9 : 8;
chunk.Skip(headerSize - sizeof(uint32le));
const uint32 patLength = chunk.ReadUint32LE();
chunk.SkipBack(headerSize);
patternChunk = chunk.ReadChunk(headerSize + patLength);
}
// Now go through the order list and load them.
@ -966,14 +977,14 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
// Create one pattern for each order item, as the same pattern can be played with different settings
if(pat < patternChunks.size())
{
pat = ConvertDMFPattern(patternChunks[pat], settings, *this);
pat = ConvertDMFPattern(patternChunks[pat], fileHeader.version, settings, *this);
}
}
// Write loop end if necessary
if(Order().IsValidPat(seqHeader.loopEnd) && (seqHeader.loopStart > 0 || seqHeader.loopEnd < Order().GetLastIndex()))
if(Order().IsValidPat(seqLoopEnd) && (seqLoopStart > 0 || seqLoopEnd < Order().GetLastIndex()))
{
PATTERNINDEX pat = Order()[seqHeader.loopEnd];
Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast<ModCommand::PARAM>(seqHeader.loopStart)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow());
PATTERNINDEX pat = Order()[seqLoopEnd];
Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast<ModCommand::PARAM>(seqLoopStart)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow());
}
}
@ -995,20 +1006,19 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
chunk.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[smp]);
const uint8 nameLength = (fileHeader.version < 2) ? 30 : chunk.ReadUint8();
chunk.ReadString<mpt::String::spacePadded>(m_szNames[smp], nameLength);
DMFSampleHeader sampleHeader;
ModSample &sample = Samples[smp];
chunk.ReadStruct(sampleHeader);
sampleHeader.ConvertToMPT(sample);
if(fileHeader.version >= 8)
{
// Read library name in version 8 files
if(fileHeader.version >= 8)
chunk.ReadString<mpt::String::spacePadded>(sample.filename, 8);
}
// We don't care for the checksum of the sample data...
chunk.Skip(sizeof(DMFSampleHeaderTail));
// Filler + CRC
chunk.Skip(fileHeader.version > 1 ? 6 : 2);
// Now read the sample data from the data chunk
FileReader sampleData = sampleDataChunk.ReadChunk(sampleDataChunk.ReadUint32LE());

View File

@ -123,10 +123,9 @@ struct IMFInstrument
if(smpNum)
{
static_assert(mpt::array_size<decltype(mptIns.Keyboard)>::size >= mpt::array_size<decltype(map)>::size);
for(size_t note = 0; note < std::size(map); note++)
for(size_t note = 0; note < std::min(std::size(map), std::size(mptIns.Keyboard) - 12u); note++)
{
mptIns.Keyboard[note] = firstSample + map[note];
mptIns.Keyboard[note + 12] = firstSample + map[note];
}
}

View File

@ -266,16 +266,20 @@ struct MMDInstrExt
// Below fields saved by >= V5
uint8be defaultPitch;
uint8be instrFlags;
uint16be longMidiPreset;
uint16be longMidiPreset; // Legacy MIDI program mode that doesn't use banks but a combination of two program change commands
// Below fields saved by >= V5.02
uint8be outputDevice;
uint8be reserved;
// Below fields saved by >= V7
uint32be loopStart;
uint32be loopLength;
// Not sure which version starts saving those but they are saved by MED Soundstudio for Windows
uint8 volume; // 0...127
uint8 outputPort; // Index into user-configurable device list (NOT WinAPI port index)
uint16le midiBank;
};
MPT_BINARY_STRUCT(MMDInstrExt, 18)
MPT_BINARY_STRUCT(MMDInstrExt, 22)
struct MMDInstrInfo
@ -350,9 +354,19 @@ struct MMDTag
MPT_BINARY_STRUCT(MMDTag, 8)
struct MMDDump
{
uint32be length;
uint32be dataPointer;
uint16be extLength; // If >= 20: name follows as char[20]
};
MPT_BINARY_STRUCT(MMDDump, 10)
static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat)
{
if(bpmMode)
if(bpmMode && !is8Ch)
{
// You would have thought that we could use modern tempo mode here.
// Alas, the number of ticks per row still influences the tempo. :(
@ -412,7 +426,14 @@ static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rows
} else if(m.param <= 0xF0)
{
m.command = CMD_TEMPO;
if(m.param < 0x03) // This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows.
m.param = 0x70;
else
m.param = mpt::saturate_round<ModCommand::PARAM>(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble());
#ifdef MODPLUG_TRACKER
if(m.param < 0x20)
m.param = 0x20;
#endif // MODPLUG_TRACKER
} else switch(m.command)
{
case 0xF1: // Play note twice
@ -447,6 +468,10 @@ static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rows
break;
}
break;
case 0x10: // MIDI message
m.command = CMD_MIDI;
m.param |= 0x80;
break;
case 0x11: // Slide pitch up
m.command = CMD_MODCMDEX;
m.param = 0x10 | std::min<uint8>(m.param, 0x0F);
@ -482,6 +507,16 @@ static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rows
m.command = CMD_MODCMDEX;
m.param = 0xB0 | std::min<uint8>(m.param, 0x0F);
break;
case 0x1C: // MIDI program
if(m.param > 0 && m.param <= 128)
{
m.command = CMD_MIDI;
m.param--;
} else
{
m.command = CMD_NONE;
}
break;
case 0x1D: // Pattern break (in hex)
m.command = CMD_PATTERNBREAK;
break;
@ -620,7 +655,7 @@ static bool ValidateHeader(const MMD0FileHeader &fileHeader)
|| fileHeader.songOffset < sizeof(MMD0FileHeader)
|| fileHeader.songOffset > uint32_max - 63 * sizeof(MMD0Sample) - sizeof(MMDSong)
|| fileHeader.blockArrOffset < sizeof(MMD0FileHeader)
|| fileHeader.sampleArrOffset < sizeof(MMD0FileHeader)
|| (fileHeader.sampleArrOffset > 0 && fileHeader.sampleArrOffset < sizeof(MMD0FileHeader))
|| fileHeader.expDataOffset > uint32_max - sizeof(MMD0Exp))
{
return false;
@ -633,7 +668,7 @@ static uint64 GetHeaderMinimumAdditionalSize(const MMD0FileHeader &fileHeader)
{
return std::max<uint64>({ fileHeader.songOffset + 63 * sizeof(MMD0Sample) + sizeof(MMDSong),
fileHeader.blockArrOffset,
fileHeader.sampleArrOffset,
fileHeader.sampleArrOffset ? fileHeader.sampleArrOffset : sizeof(MMD0FileHeader),
fileHeader.expDataOffset + sizeof(MMD0Exp) }) - sizeof(MMD0FileHeader);
}
@ -689,15 +724,21 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
// Start with the instruments, as those are shared between songs
std::vector<uint32be> instrOffsets;
if(fileHeader.sampleArrOffset)
{
file.Seek(fileHeader.sampleArrOffset);
file.ReadVector(instrOffsets, songHeader.numSamples);
} else if(songHeader.numSamples > 0)
{
return false;
}
m_nInstruments = m_nSamples = songHeader.numSamples;
// In MMD0 / MMD1, octave wrapping is only done in 4-channel modules (hardware mixing!), and not for synth instruments
// - It's required e.g. for automatic terminated to.mmd0
// - dissociate.mmd0 (8 channels) and starkelsesirap.mmd0 (synth) on the other hand don't need it
// In MMD0 / MMD1, octave wrapping is not done for synth instruments
// - It's required e.g. for automatic terminated to.mmd0 and you got to let the music.mmd1
// - starkelsesirap.mmd0 (synth instruments) on the other hand don't need it
// In MMD2 / MMD3, the mix flag is used instead.
const bool hardwareMixSamples = (version < 2 && m_nChannels == 4) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX));
const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX));
bool needInstruments = false;
bool anySynthInstrs = false;
@ -925,10 +966,10 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
ins.VolEnv.dwFlags.set(ENV_ENABLED);
needInstruments = true;
}
if(size > offsetof(MMDInstrExt, longMidiPreset))
{
ins.wMidiBank = instrExt.longMidiPreset;
}
if(size > offsetof(MMDInstrExt, volume))
ins.nGlobalVol = (instrExt.volume + 1u) / 2u;
if(size > offsetof(MMDInstrExt, midiBank))
ins.wMidiBank = instrExt.midiBank;
#ifndef NO_VST
if(ins.nMixPlug > 0)
{
@ -995,6 +1036,10 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
}
}
// Setup a program change macro for command 1C (even if MIDI plugin is disabled, as otherwise these commands may act as filter commands)
m_MidiCfg.ClearZxxMacros();
strcpy(m_MidiCfg.szMidiSFXExt[0], "Cc z");
file.Rewind();
PATTERNINDEX basePattern = 0;
for(SEQUENCEINDEX song = 0; song < numSongs; song++)
@ -1062,14 +1107,16 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|| !file.CanRead(songHeader.songLength * 2)
|| !file.ReadVector(sections, songHeader.songLength))
continue;
for(uint16 section : sections)
{
if(section > header.numPlaySeqs)
continue;
file.Seek(header.playSeqTableOffset + section * 4);
if(file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq)))
{
if(!file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq)))
continue;
MMD2PlaySeq playSeq;
file.ReadStruct(playSeq);
@ -1116,7 +1163,6 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
}
}
}
}
const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0;
const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0;
@ -1148,7 +1194,38 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
m_songMessage.Read(file, expData.annoLength - 1, SongMessage::leAutodetect);
}
if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset))
#ifndef NO_VST
// Read MIDI messages
if(expData.midiDumpOffset && file.Seek(expData.midiDumpOffset) && file.CanRead(8))
{
uint16 numDumps = std::min(file.ReadUint16BE(), static_cast<uint16>(std::size(m_MidiCfg.szMidiZXXExt)));
file.Skip(6);
if(file.CanRead(numDumps * 4))
{
std::vector<uint32be> dumpPointers;
file.ReadVector(dumpPointers, numDumps);
for(uint16 dump = 0; dump < numDumps; dump++)
{
if(!file.Seek(dumpPointers[dump]) || !file.CanRead(sizeof(MMDDump)))
continue;
MMDDump dumpHeader;
file.ReadStruct(dumpHeader);
if(!file.Seek(dumpHeader.dataPointer) || !file.CanRead(dumpHeader.length))
continue;
auto &macro = m_MidiCfg.szMidiZXXExt[dump];
auto length = std::min(static_cast<size_t>(dumpHeader.length), std::size(macro) / 2u);
for(size_t i = 0; i < length; i++)
{
const uint8 byte = file.ReadUint8(), high = byte >> 4, low = byte & 0x0F;
macro[i * 2] = high + (high < 0x0A ? '0' : 'A' - 0x0A);
macro[i * 2 + 1] = low + (low < 0x0A ? '0' : 'A' - 0x0A);
}
}
}
}
#endif
if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset) && file.CanRead(12))
{
file.Skip(6); // Next info file (unused) + reserved
if(file.ReadUint16BE() == 1) // ASCII text

View File

@ -430,16 +430,17 @@ struct MidiChannelState
}
}
uint8 GetRPN() const
void SetRPNRelative(int8 value)
{
switch(rpn)
{
case 0: // Pitch Bend Range
return pitchBendRange;
pitchBendRange = static_cast<uint8>(std::clamp(pitchBendRange + value, 1, 0x7F));
break;
case 2: // Coarse Tune
return transpose;
transpose = mpt::saturate_cast<int8>(transpose + value);
break;
}
return 0;
}
};
@ -979,7 +980,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
case MIDIEvents::MIDICC_DataButtonincrement:
case MIDIEvents::MIDICC_DataButtondecrement:
midiChnStatus[midiCh].SetRPN(midiChnStatus[midiCh].GetRPN() + ((data1 == MIDIEvents::MIDICC_DataButtonincrement) ? 1 : -1));
midiChnStatus[midiCh].SetRPNRelative((data1 == MIDIEvents::MIDICC_DataButtonincrement) ? 1 : -1);
break;
case MIDIEvents::MIDICC_NonRegisteredParameter_Fine:
@ -1350,7 +1351,6 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
}
}
#endif // MODPLUG_TRACKER
return true;
}

View File

@ -963,10 +963,11 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
// - Scan patterns to check if file could be a NoiseTracker file in disguise.
// In this case, the parameter of Dxx commands needs to be ignored.
// - Use the same code to find notes that would be out-of-range on Amiga.
// - Detect 7-bit panning.
// - Detect 7-bit panning and whether 8xx / E8x commands should be interpreted as panning at all.
bool onlyAmigaNotes = true;
bool fix7BitPanning = false;
uint8 maxPanning = 0; // For detecting 8xx-as-sync
const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30;
if(!isNoiseTracker)
{
bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning
@ -1003,7 +1004,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
}
}
}
fix7BitPanning = leftPanning && !extendedPanning;
fix7BitPanning = leftPanning && !extendedPanning && maxPanning >= ENABLE_MOD_PANNING_THRESHOLD;
}
file.Seek(1084);
@ -1140,7 +1141,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
m_playBehaviour.set(kMODOutOfRangeNoteDelay);
m_playBehaviour.set(kMODTempoOnSecondTick);
// Arbitrary threshold for deciding that 8xx effects are only used as sync markers
if(maxPanning < 0x30)
if(maxPanning < ENABLE_MOD_PANNING_THRESHOLD)
{
m_playBehaviour.set(kMODIgnorePanning);
if(fileHeader.restartPos != 0x7F)

View File

@ -311,4 +311,21 @@ void ModInstrument::Transpose(int8 amount)
}
uint8 ModInstrument::GetMIDIChannel(const CSoundFile &sndFile, CHANNELINDEX chn) const
{
if(chn >= std::size(sndFile.m_PlayState.Chn))
return 0;
// For mapped channels, return their pattern channel, modulo 16 (because there are only 16 MIDI channels)
const ModChannel &channel = sndFile.m_PlayState.Chn[chn];
if(nMidiChannel == MidiMappedChannel)
return static_cast<uint8>((channel.nMasterChn ? (channel.nMasterChn - 1u) : chn) % 16u);
else if(HasValidMIDIChannel())
return (nMidiChannel - MidiFirstChannel) % 16u;
else
return 0;
}
OPENMPT_NAMESPACE_END

View File

@ -21,6 +21,8 @@
OPENMPT_NAMESPACE_BEGIN
class CSoundFile;
// Instrument Nodes
struct EnvelopeNode
{
@ -147,6 +149,7 @@ struct ModInstrument
void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, uint8(0x7F)) | (enable ? 0x80 : 0x00); }
bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 17); }
uint8 GetMIDIChannel(const CSoundFile &sndFile, CHANNELINDEX chn) const;
void SetTuning(CTuning *pT)
{

View File

@ -100,6 +100,21 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
else
maxLength = Util::MaxValueOfType(maxLength);
LimitMax(sample.nLength, mpt::saturate_cast<SmpLength>(maxLength));
} else if(GetEncoding() == AMS)
{
if(fileSize <= 9)
return 0;
file.Skip(4); // Target sample size (we already know this)
SmpLength maxLength = std::min(file.ReadUint32LE(), mpt::saturate_cast<uint32>(fileSize));
file.SkipBack(8);
// In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85
if(Util::MaxValueOfType(maxLength) / 85 >= maxLength)
maxLength *= 85;
else
maxLength = Util::MaxValueOfType(maxLength);
LimitMax(sample.nLength, maxLength / (m_bitdepth / 8u));
}
if(sample.nLength < 1)
@ -153,8 +168,6 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
} else if(GetEncoding() == AMS && GetChannelFormat() == mono)
{
// AMS compressed samples
if(fileSize > 9)
{
file.Skip(4); // Target sample size (we already know this)
uint32 sourceSize = file.ReadUint32LE();
int8 packCharacter = file.ReadUint8();
@ -165,6 +178,13 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
bytesRead += sourceSize;
AMSUnpack(reinterpret_cast<const int8 *>(packedDataView.data()), packedDataView.size(), sample.samplev(), sample.GetSampleSizeInBytes(), packCharacter);
if(sample.uFlags[CHN_16BIT] && !mpt::endian_is_little())
{
auto p = sample.sample16();
for(SmpLength length = sample.nLength; length != 0; length--, p++)
{
*p = mpt::bit_cast<int16le>(*p);
}
}
} else if(GetEncoding() == PTM8Dto16 && GetChannelFormat() == mono && GetBitDepth() == 16)
{

View File

@ -4983,7 +4983,22 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *
{
// MIDI channel
isNibble = true;
data = GetBestMidiChannel(nChn);
data = 0xFF;
const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
if(plug > 0 && plug <= MAX_MIXPLUGINS)
{
auto midiPlug = dynamic_cast<const IMidiPlugin *>(m_MixPlugins[plug - 1u].pMixPlugin);
if(midiPlug)
data = midiPlug->GetMidiChannel(nChn);
}
if(data == 0xFF)
{
// Fallback if no plugin was found
if(pIns)
data = pIns->GetMIDIChannel(*this, nChn);
else
data = 0;
}
} else if(macro[pos] == 'n')
{
// Last triggered note
@ -5836,7 +5851,7 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI)
const CModSpecifications &specs = GetModSpecifications();
// Anything lower than the minimum tempo is considered to be a tempo slide
const TEMPO minTempo = (GetType() == MOD_TYPE_MDL) ? TEMPO(1, 0) : TEMPO(32, 0);
const TEMPO minTempo = (GetType() & (MOD_TYPE_MDL | MOD_TYPE_MED)) ? TEMPO(1, 0) : TEMPO(32, 0);
if(setFromUI)
{
@ -6263,31 +6278,6 @@ IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const
}
// Get the MIDI channel currently associated with a given tracker channel
uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX trackerChn) const
{
if(trackerChn >= std::size(m_PlayState.Chn))
{
return 0;
}
const ModChannel &chn = m_PlayState.Chn[trackerChn];
const ModInstrument *ins = chn.pModInstrument;
if(ins != nullptr)
{
if(ins->nMidiChannel == MidiMappedChannel)
{
// For mapped channels, return their pattern channel, modulo 16 (because there are only 16 MIDI channels)
return static_cast<uint8>((chn.nMasterChn ? (chn.nMasterChn - 1u) : trackerChn) % 16u);
} else if(ins->HasValidMIDIChannel())
{
return (ins->nMidiChannel - 1u) % 16u;
}
}
return 0;
}
#ifdef MODPLUG_TRACKER
void CSoundFile::HandlePatternTransitionEvents()
{

View File

@ -1170,7 +1170,6 @@ private:
public:
PLUGINDEX GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const;
uint8 GetBestMidiChannel(CHANNELINDEX nChn) const;
};

View File

@ -755,7 +755,13 @@ void IMidiPlugin::ApplyPitchWheelDepth(int32 &value, int8 pwd)
// Get the MIDI channel currently associated with a given tracker channel
uint8 IMidiPlugin::GetMidiChannel(CHANNELINDEX trackChannel) const
{
return m_SndFile.GetBestMidiChannel(trackChannel);
if(trackChannel >= std::size(m_SndFile.m_PlayState.Chn))
return 0;
if(auto ins = m_SndFile.m_PlayState.Chn[trackChannel].pModInstrument; ins != nullptr)
return ins->GetMIDIChannel(m_SndFile, trackChannel);
else
return 0;
}

View File

@ -266,14 +266,14 @@ public:
void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override;
bool IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) override;
// Get the MIDI channel currently associated with a given tracker channel
virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const;
protected:
// Plugin wants to send MIDI to OpenMPT
virtual void ReceiveMidi(uint32 midiCode);
virtual void ReceiveSysex(mpt::const_byte_span sysex);
// Get the MIDI channel currently associated with a given tracker channel
virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const;
// Converts a 14-bit MIDI pitch bend position to our internal pitch bend position representation
static constexpr int32 EncodePitchBendParam(int32 position) { return (position << vstPitchBendShift); }
// Converts the internal pitch bend position to a 14-bit MIDI pitch bend position