Update libopenmpt to version 0.5.4
parent
b375f06faa
commit
24231ecdbb
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) ?
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include "libopenmpt_impl.hpp"
|
||||
#include "libopenmpt_ext.hpp"
|
||||
|
||||
using namespace OpenMPT;
|
||||
|
||||
namespace openmpt {
|
||||
|
||||
class module_ext_impl
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ¶m)
|
||||
{
|
||||
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());
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¯o = 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -1170,7 +1170,6 @@ private:
|
|||
|
||||
public:
|
||||
PLUGINDEX GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const;
|
||||
uint8 GetBestMidiChannel(CHANNELINDEX nChn) const;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue