diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE index 57efb9f64..7c06acaeb 100644 --- a/Frameworks/OpenMPT/OpenMPT/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile index 8d5407f14..34d317460 100644 --- a/Frameworks/OpenMPT/OpenMPT/Makefile +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -264,6 +264,9 @@ endif ifeq ($(UNAME_S),FreeBSD) HOST_FLAVOUR=FREEBSD endif +ifeq ($(UNAME_S),OpenBSD) +HOST_FLAVOUR=OPENBSD +endif ifeq ($(UNAME_S),Haiku) HOST_FLAVOUR=HAIKU endif diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md index 63f589c45..20683b39d 100644 --- a/Frameworks/OpenMPT/OpenMPT/README.md +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -47,6 +47,14 @@ How to compile OpenMPT for, as Visual Studio only builds one architecture configuration at a time. + Please note that we do not support building with a later Visual Studio + installation with an earlier compiler version. This is because, while + later Visual Studio versions allow installing earlier compilers to be + available via the later version's environment, in this configuration, + the earlier compiler will still use the later C and C++ runtime's + headers and implementation, which significantly increases the matrix of + possible configurations to test. + - OpenMPT requires the compile host system to be Windows 8.1 (or later) amd64, or Windows 11 (or later) ARM64. diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index d9bf8ec8c..d7d177228 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=16293 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.0 -MPT_SVNDATE=2021-12-23T14:50:28.728256Z +MPT_SVNVERSION=16764 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.1 +MPT_SVNDATE=2022-01-30T16:49:19.812343Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk index b876e777b..757a8dad8 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk @@ -18,6 +18,12 @@ include build/make/config-clang.mk NO_LTDL?=1 NO_PORTAUDIOCPP?=1 +else ifeq ($(HOST_FLAVOUR),OPENBSD) + +NO_PORTAUDIOCPP?=1 +NO_PULSEAUDIO?=1 +include build/make/config-clang.mk + else ifeq ($(HOST_FLAVOUR),HAIKU) # In Haiku x86 32bit (but not 64bit), diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk index fc1ebbec6..61e10b1b5 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk @@ -1,4 +1,6 @@ +$(warning warning: CONFIG=generic is deprecated. Use CONFIG=standard instead.) + CC ?= cc CXX ?= c++ LD ?= c++ diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-haiku.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-haiku.mk index c0b96d64f..191dbb39e 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-haiku.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-haiku.mk @@ -1,2 +1,4 @@ +$(warning warning: CONFIG=haiku is deprecated. The OS is auto-detected.) + include config-defaults.mk diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-macosx.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-macosx.mk index c0b96d64f..278b395eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-macosx.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-macosx.mk @@ -1,2 +1,4 @@ +$(warning warning: CONFIG=macosx is deprecated. The OS is auto-detected.) + include config-defaults.mk diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-win9x.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-win9x.mk index 24bcf3a8c..2897ff1da 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-win9x.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-win9x.mk @@ -10,8 +10,8 @@ CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -DWIN32 -D_WIN32 -DWINVER=0x0410 -D_WIN32_WINDOWS=0x0410 -DMPT_BUILD_RETRO -CXXFLAGS += -mconsole -CFLAGS += -mconsole +CXXFLAGS += -mconsole -mthreads +CFLAGS += -mconsole -mthreads LDFLAGS += LDLIBS += -lm -lole32 -lrpcrt4 -lwinmm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk index a84218e4e..d1c579bae 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk @@ -10,8 +10,8 @@ CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -DWIN32 -D_WIN32 -CXXFLAGS += -municode -mconsole -CFLAGS += -municode -mconsole +CXXFLAGS += -municode -mconsole -mthreads +CFLAGS += -municode -mconsole -mthreads LDFLAGS += LDLIBS += -lm -lole32 -lrpcrt4 -lwinmm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk index 849c6c1a4..fc4f8eaf3 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk @@ -10,8 +10,8 @@ CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -DWIN32 -D_WIN32 -DWIN64 -D_WIN64 -CXXFLAGS += -municode -mconsole -CFLAGS += -municode -mconsole +CXXFLAGS += -municode -mconsole -mthreads +CFLAGS += -municode -mconsole -mthreads LDFLAGS += LDLIBS += -lm -lole32 -lrpcrt4 -lwinmm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk index 6572e5326..fdcd75a1e 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk @@ -10,8 +10,8 @@ CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -DWIN32 -D_WIN32 -DWIN64 -D_WIN64 -DWINAPI_FAMILY=0x2 -D_WIN32_WINNT=0x0602 -CXXFLAGS += -municode -mconsole -CFLAGS += -municode -mconsole +CXXFLAGS += -municode -mconsole -mthreads +CFLAGS += -municode -mconsole -mthreads LDFLAGS += LDLIBS += -lm -lole32 -lwinmm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk index 78597cbcd..350930021 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk @@ -10,8 +10,8 @@ CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -DWIN32 -D_WIN32 -DWINAPI_FAMILY=0x2 -D_WIN32_WINNT=0x0602 -CXXFLAGS += -municode -mconsole -CFLAGS += -municode -mconsole +CXXFLAGS += -municode -mconsole -mthreads +CFLAGS += -municode -mconsole -mthreads LDFLAGS += LDLIBS += -lm -lole32 -lrpcrt4 -lwinmm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 456c46c4d..32b444bcc 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "16293" -#define OPENMPT_VERSION_REVISION 16293 +#define OPENMPT_VERSION_SVNVERSION "16764" +#define OPENMPT_VERSION_REVISION 16764 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.0" -#define OPENMPT_VERSION_DATE "2021-12-23T14:50:28.728256Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.1" +#define OPENMPT_VERSION_DATE "2022-01-30T16:49:19.812343Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp index 1a602846f..189b777fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp @@ -49,23 +49,6 @@ mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); } #if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } #endif // MPT_WITH_MFC -#if MPT_USTRING_MODE_WIDE -mpt::ustring ToUString(const bool & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const signed char & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const unsigned char & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const signed short & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const unsigned short & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const signed int & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const unsigned int & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const signed long & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const unsigned long & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const signed long long & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const unsigned long long & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const float & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const double & x) { return mpt::format_value_default(x); } -mpt::ustring ToUString(const long double & x) { return mpt::format_value_default(x); } -#endif -#if MPT_USTRING_MODE_UTF8 mpt::ustring ToUString(const bool & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const signed char & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const unsigned char & x) { return mpt::format_value_default(x); } @@ -80,7 +63,6 @@ mpt::ustring ToUString(const unsigned long long & x) { return mpt::format_value_ mpt::ustring ToUString(const float & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const double & x) { return mpt::format_value_default(x); } mpt::ustring ToUString(const long double & x) { return mpt::format_value_default(x); } -#endif #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 @@ -122,23 +104,6 @@ std::string FormatValA(const float & x, const FormatSpec & f) { return mpt::form std::string FormatValA(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } std::string FormatValA(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -#if MPT_USTRING_MODE_WIDE -mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -#endif -#if MPT_USTRING_MODE_UTF8 mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple(x, f); } @@ -153,7 +118,6 @@ mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { re mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::format_simple(x, f); } -#endif #if MPT_WSTRING_FORMAT std::wstring FormatValW(const bool & x, const FormatSpec & f) { return mpt::format_simple(x, f); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index b98938fdd..e33b90aad 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -592,12 +592,12 @@ mpt::ustring GetFullCreditsString() "libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n" #endif "\n" - "Copyright \xC2\xA9 2004-2021 OpenMPT Project Developers and Contributors\n" + "Copyright \xC2\xA9 2004-2022 OpenMPT Project Developers and Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Developers:\n" - "Johannes Schultz (2008-2021)\n" - "J\xC3\xB6rn Heusipp (2012-2021)\n" + "Johannes Schultz (2008-2022)\n" + "J\xC3\xB6rn Heusipp (2012-2022)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" @@ -792,7 +792,7 @@ mpt::ustring GetFullCreditsString() mpt::ustring GetLicenseString() { return MPT_UTF8( - "Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors" "\n" + "Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 90cb2e29e..e1b312228 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 30 -#define VER_MINOR 01 +#define VER_MINOR 02 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE index 57efb9f64..7c06acaeb 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE index 57efb9f64..7c06acaeb 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 1b065d2cb..87a87a611 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,6 +5,35 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.6.1 (2022-01-30) + + * [**Bug**] Linking libmpg123 no longer fails on OpenBSD. + * [**Bug**] Possible hang with malformed DMF, DSM, MED, MUS, OKT and SymMOD + files containing 65536 or more patterns when destroying the module. + * [**Bug**] Avoid NaNs and infinite values with custom tunings and in the + I3DL2Reverb plugin. + + * The letter "z" is now evaluated in fixed MIDI macros (Z80...ZFF) the same + way as in Impulse Tracker. + * MOD: Loosened VBlank timing heuristics so that "frame of mind" by Dascon + plays correctly. + * MOD: Validate the contents of "hidden" patterns beyond the end of the order + list when the file size matches the expected size when only taken "official" + patterns into account. This fixes Shofixti Ditty.mod from Star Control 2 + while keeping other (partly broken) modules working. + * MED: Command 20 (reverse sample) is now only applied when it's next to a + note. + * S3M: Introducing the "Send OPL key-off when triggering notes" compatibility + setting broke retrigger for OPL notes again (they retriggered rather than + not retriggering). + * S3M: Retriggering a note no longer resets its pitch after a portamento. + * S3M: Partially implement retrigger behaviour for stopped notes in + SoundBlaster mode: Like in IT, it is not possible to retrigger a sample that + has already stopped playing. + * DIGI: Improve compatibility with E3x reverse sample command. + * DSym: Tempos < 32 were treated as tempo slides. + * SymMOD: Key-off command was not implemented properly. + ### libopenmpt 0.6.0 (2021-12-23) * [**New**] `MUS` files from Psycho Pinball and Micro Machines 2 are now @@ -14,6 +43,8 @@ is just a high-level summary. * [**New**] `FMT` files created with Davey W Taylor's FM Tracker are now supported. * [**New**] `DSYM` files created with Digital Symphony are now supported. + * [**New**] `STX` files (transitional format between Scream Tracker 2 and 3) + are now supported. * [**New**] TakeTracker MODs with `TDZ1` to `TDZ3` magic bytes are now supported. * [**New**] openmpt123: openmpt123 will now expand file wildcards passed on @@ -68,6 +99,7 @@ is just a high-level summary. song does not restart from the beginning even if the repeat count is not 0. * `openmpt::module::set_position_seconds()` accuracy has been improved for modules with pattern loops. + * Samples played at the wrong volume when rendering modules in mono. * IT: Portamentos in files with Linear Slides disabled are now more accurate. * IT: Pitch/Pan Separation was affected by note-off commands, and wasn't reset by panning commands like in Impulse Tracker. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md index 8eaf39d24..8dc9dfc31 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md @@ -11,6 +11,15 @@ Dependencies * Supported compilers for building libopenmpt: * **Microsoft Visual Studio 2017** or higher, running on a amd64 build system (other target systems are supported) + + Please note that we do not support building with a later Visual Studio + installation with an earlier compiler version. This is because, while + later Visual Studio versions allow installing earlier compilers to be + available via the later version's environment, in this configuration, + the earlier compiler will still use the later C and C++ runtime's + headers and implementation, which significantly increases the matrix of + possible configurations to test. + * **GCC 8.1** or higher * **Clang 7** or higher * **MinGW-W64 8.1** or higher (it is recommended to preferably use diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp index c9ac84d53..49ccbf349 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp @@ -260,7 +260,7 @@ static void config( HWND hwndParent ) { static void about( HWND hwndParent ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2021 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h index 4b307a828..32568a2b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h @@ -161,6 +161,8 @@ LIBOPENMPT_DEPRECATED static const int LIBOPENMPT_DEPRECATED_STRING_CONSTANT = 0 #else #define LIBOPENMPT_DEPRECATED_STRING( str ) str #endif +#else +#define LIBOPENMPT_DEPRECATED_STRING( str ) str #endif diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index 1c5a49029..09acb1aad 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -21,7 +21,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 6 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 0 +#define OPENMPT_API_VERSION_PATCH 1 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index f4c93f4ac..a627eeeff 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=6 -LIBOPENMPT_VERSION_PATCH=0 +LIBOPENMPT_VERSION_PATCH=1 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=3 -LIBOPENMPT_LTVER_REVISION=0 +LIBOPENMPT_LTVER_REVISION=1 LIBOPENMPT_LTVER_AGE=3 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc index 5fbf40032..cf3d3b6e4 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc @@ -192,7 +192,7 @@ BEGIN VALUE "FileDescription", VER_FILEDESC_STR VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", VER_FILENAME_STR - VALUE "LegalCopyright", "Copyright © 2004-2020 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque" + VALUE "LegalCopyright", "Copyright © 2004-2022 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque" VALUE "OriginalFilename", VER_FILENAME_STR VALUE "ProductName", "libopenmpt" VALUE "ProductVersion", VER_FILEVERSION_STR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp index 1572c673e..4221fe447 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp @@ -519,7 +519,7 @@ static void clear_current_timeinfo() { static void WINAPI openmpt_About( HWND win ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2021 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index 77ee762c1..a10ef828b 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -8,7 +8,7 @@ */ static const char * const license = -"Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors" "\n" +"Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -464,7 +464,7 @@ static std::string seconds_to_string( double time ) { static void show_info( std::ostream & log, bool verbose ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl; - log << "Copyright (c) 2013-2021 OpenMPT Project Developers and Contributors " << std::endl; + log << "Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors " << std::endl; if ( !verbose ) { log << std::endl; return; @@ -541,7 +541,7 @@ static void show_info( std::ostream & log, bool verbose ) { static void show_man_version( textout & log ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl; log << std::endl; - log << "Copyright (c) 2013-2021 OpenMPT Project Developers and Contributors " << std::endl; + log << "Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors " << std::endl; } static void show_short_version( textout & log ) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index 494680543..4df9c208d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -1047,6 +1047,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) instruments.clear(); DLSENVELOPE dlsEnv; int32 instrAttenuation = 0; + int16 instrFinetune = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; dlsEnv.wVolDecay = 0; @@ -1069,6 +1070,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) continue; for(const auto &gen : generators) { + const int16 value = static_cast(gen.genAmount); switch(gen.sfGenOper) { case SF2_GEN_ATTACKVOLENV: @@ -1096,7 +1098,13 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) keyRange = gen.genAmount; break; case SF2_GEN_ATTENUATION: - instrAttenuation = -static_cast(gen.genAmount); + instrAttenuation = -value; + break; + case SF2_GEN_COARSETUNE: + instrFinetune += value * 128; + break; + case SF2_GEN_FINETUNE: + instrFinetune += static_cast(Util::muldiv(static_cast(value), 128, 100)); break; #if 0 default: @@ -1128,7 +1136,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) DLSREGION globalZone{}; globalZone.uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note globalZone.tuning = 100; - globalZone.sFineTune = 0; + globalZone.sFineTune = instrFinetune; globalZone.nWaveLink = Util::MaxValueOfType(globalZone.nWaveLink); if(keyRange != 0xFFFF) { @@ -1187,7 +1195,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) } break; case SF2_GEN_UNITYNOTE: - if (value < 128) rgn.uUnityNote = (uint8)value; + if (value < 128) rgn.uUnityNote = static_cast(value); break; case SF2_GEN_ATTACKVOLENV: pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount); @@ -1583,7 +1591,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if(nRgn >= dlsIns.Regions.size()) { #ifdef DLSBANK_LOG - MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.nRegions)); + MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size())); #endif return false; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index e623f91ec..d9c3fde27 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -111,6 +111,9 @@ protected: public: CDLSBank(); + + bool operator==(const CDLSBank &other) const noexcept { return !mpt::PathString::CompareNoCase(m_szFileName, other.m_szFileName); } + static bool IsDLSBank(const mpt::PathString &filename); static uint32 MakeMelodicCode(uint32 bank, uint32 instr) { return ((bank << 16) | (instr));} static uint32 MakeDrumCode(uint32 rgn, uint32 instr) { return (0x80000000 | (rgn << 16) | (instr));} diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index f1eec1a99..65f768ec7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -345,7 +345,7 @@ void CSoundFile::CreateStereoMix(int count) //Look for plugins associated with this implicit tracker channel. #ifndef NO_PLUGINS - PLUGINDEX nMixPlugin = GetBestPlugin(m_PlayState.ChnMix[nChn], PrioritiseInstrument, RespectMutes); + PLUGINDEX nMixPlugin = GetBestPlugin(m_PlayState, m_PlayState.ChnMix[nChn], PrioritiseInstrument, RespectMutes); if ((nMixPlugin > 0) && (nMixPlugin <= MAX_MIXPLUGINS) && m_MixPlugins[nMixPlugin - 1].pMixPlugin != nullptr) { @@ -417,11 +417,16 @@ void CSoundFile::CreateStereoMix(int count) else if(m_SamplePlayLengths != nullptr) { // Detecting the longest play time for each sample for optimization + SmpLength pos = chn.position.GetUInt(); chn.position += chn.increment * nSmpCount; + if(!chn.increment.IsNegative()) + { + pos = chn.position.GetUInt(); + } size_t smp = std::distance(static_cast(static_cast::type>(Samples)), chn.pModSample); if(smp < m_SamplePlayLengths->size()) { - (*m_SamplePlayLengths)[smp] = std::max((*m_SamplePlayLengths)[smp], chn.position.GetUInt()); + (*m_SamplePlayLengths)[smp] = std::max((*m_SamplePlayLengths)[smp], pos); } } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp index 2d9e1ae59..e72184f94 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp @@ -535,6 +535,7 @@ uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const } mptSmp.Initialize(MOD_TYPE_IT); + mptSmp.SetDefaultCuePoints(); // For old IT/MPTM files mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp index 7ee9f1624..1f2e3f333 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp @@ -624,10 +624,10 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) for(uint32 i = 0; i < 32; i++) { uint32 param = (i * 127u) / 32u; - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i ]) = MPT_AFORMAT("F0F080{}")(mpt::afmt::HEX0<2>(param)); - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 32]) = MPT_AFORMAT("F0F081{}")(mpt::afmt::HEX0<2>(param)); - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 64]) = MPT_AFORMAT("F0F082{}")(mpt::afmt::HEX0<2>(param)); - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 96]) = MPT_AFORMAT("F0F083{}")(mpt::afmt::HEX0<2>(param)); + m_MidiCfg.Zxx[i ] = MPT_AFORMAT("F0F080{}")(mpt::afmt::HEX0<2>(param)); + m_MidiCfg.Zxx[i + 32] = MPT_AFORMAT("F0F081{}")(mpt::afmt::HEX0<2>(param)); + m_MidiCfg.Zxx[i + 64] = MPT_AFORMAT("F0F082{}")(mpt::afmt::HEX0<2>(param)); + m_MidiCfg.Zxx[i + 96] = MPT_AFORMAT("F0F083{}")(mpt::afmt::HEX0<2>(param)); } } #endif // NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp index d1f768afe..15ee8309e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp @@ -44,12 +44,11 @@ static void ReadDIGIPatternEntry(FileReader &file, ModCommand &m) switch(m.param & 0xF0) { case 0x30: - // E30 / E31: Play sample backwards (with some weird parameters that we won't support for now) - if(m.param <= 0x31) - { - m.command = CMD_S3MCMDEX; - m.param = 0x9F; - } + // E3x: Play sample backwards (E30 stops sample when it reaches the beginning, any other value plays it from the beginning including regular loop) + // The play direction is also reset if a new note is played on the other channel linked to this channel. + // The behaviour is rather broken when there is no note next to the ommand. + m.command = CMD_DIGIREVERSESAMPLE; + m.param &= 0x0F; break; case 0x40: // E40: Stop playing sample diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsym.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsym.cpp index 55eef8473..1ac1701c8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsym.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsym.cpp @@ -414,6 +414,9 @@ bool CSoundFile::ReadDSym(FileReader &file, ModLoadingFlags loadFlags) { m->command = CMD_TEMPO; m->param = mpt::saturate_cast(std::max(8, param + 4) / 8); +#ifdef MODPLUG_TRACKER + m->param = std::max(m->param, ModCommand::PARAM(0x20)); +#endif } else { m->command = CMD_NONE; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index 997472559..734912e26 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -1393,8 +1393,8 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c memcpy(itHeader.id, "IMPM", 4); mpt::String::WriteBuf(mpt::String::nullTerminated, itHeader.songname) = m_songName; - itHeader.highlight_minor = (uint8)std::min(m_nDefaultRowsPerBeat, ROWINDEX(uint8_max)); - itHeader.highlight_major = (uint8)std::min(m_nDefaultRowsPerMeasure, ROWINDEX(uint8_max)); + itHeader.highlight_minor = mpt::saturate_cast(m_nDefaultRowsPerBeat); + itHeader.highlight_major = mpt::saturate_cast(m_nDefaultRowsPerMeasure); if(GetType() == MOD_TYPE_MPT) { @@ -2470,7 +2470,10 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel ModSample &sample = Samples[smp]; for(auto &cue : sample.cues) { - cue = chunk.ReadUint32LE(); + if(chunk.CanRead(4)) + cue = chunk.ReadUint32LE(); + else + cue = MAX_SAMPLE_LENGTH; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp index 18f69bdfa..e6555f686 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp @@ -38,19 +38,39 @@ OPENMPT_NAMESPACE_BEGIN struct ITPModCommand { - uint8le note; - uint8le instr; - uint8le volcmd; - uint8le command; - uint8le vol; - uint8le param; + uint8 note; + uint8 instr; + uint8 volcmd; + uint8 command; + uint8 vol; + uint8 param; + operator ModCommand() const { + static constexpr VolumeCommand ITPVolCmds[] = + { + VOLCMD_NONE, VOLCMD_VOLUME, VOLCMD_PANNING, VOLCMD_VOLSLIDEUP, + VOLCMD_VOLSLIDEDOWN, VOLCMD_FINEVOLUP, VOLCMD_FINEVOLDOWN, VOLCMD_VIBRATOSPEED, + VOLCMD_VIBRATODEPTH, VOLCMD_PANSLIDELEFT, VOLCMD_PANSLIDERIGHT, VOLCMD_TONEPORTAMENTO, + VOLCMD_PORTAUP, VOLCMD_PORTADOWN, VOLCMD_PLAYCONTROL, VOLCMD_OFFSET, + }; + static constexpr EffectCommand ITPCommands[] = + { + CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, + CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, + CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, + CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_RETRIG, + CMD_SPEED, CMD_TEMPO, CMD_TREMOR, CMD_MODCMDEX, + CMD_S3MCMDEX, CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_GLOBALVOLUME, + CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_FINEVIBRATO, CMD_PANBRELLO, + CMD_XFINEPORTAUPDOWN, CMD_PANNINGSLIDE, CMD_SETENVPOSITION, CMD_MIDI, + CMD_SMOOTHMIDI, CMD_DELAYCUT, CMD_XPARAM, + }; ModCommand result; - result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast(note.get()) : static_cast(NOTE_NONE); + result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast(note) : static_cast(NOTE_NONE); result.instr = instr; - result.command = (command < MAX_EFFECTS) ? static_cast(command.get()) : CMD_NONE; - result.volcmd = (volcmd < MAX_VOLCMDS) ? static_cast(volcmd.get()) : VOLCMD_NONE; + result.volcmd = (volcmd < std::size(ITPVolCmds)) ? ITPVolCmds[volcmd] : VOLCMD_NONE; + result.command = (command < std::size(ITPCommands)) ? ITPCommands[command] : CMD_NONE; result.vol = vol; result.param = param; return result; @@ -281,8 +301,8 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) if(pat < numNamedPats) { char patName[32]; - pattNames.ReadString(patName, patNameLen); - Patterns[pat].SetName(patName); + if(pattNames.ReadString(patName, patNameLen)) + Patterns[pat].SetName(patName); } // Pattern data @@ -379,6 +399,11 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) } } + for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) + { + Samples[smp].SetDefaultCuePoints(); + } + // Song extensions if(code == MagicBE("MPTS")) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 73b089653..18fb120e0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -548,8 +548,11 @@ static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rows case 0x20: // Reverse sample + skip samples if(m.param == 0 && m.vol == 0) { - m.command = CMD_S3MCMDEX; - m.param = 0x9F; + if(m.IsNote()) + { + m.command = CMD_S3MCMDEX; + m.param = 0x9F; + } } else { // Skip given number of samples @@ -1041,7 +1044,7 @@ 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"); + m_MidiCfg.SFx[0] = "Cc z"; file.Rewind(); PATTERNINDEX basePattern = 0; @@ -1201,7 +1204,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) // Read MIDI messages if(expData.midiDumpOffset && file.Seek(expData.midiDumpOffset) && file.CanRead(8)) { - uint16 numDumps = std::min(file.ReadUint16BE(), static_cast(std::size(m_MidiCfg.szMidiZXXExt))); + uint16 numDumps = std::min(file.ReadUint16BE(), static_cast(m_MidiCfg.Zxx.size())); file.Skip(6); if(file.CanRead(numDumps * 4)) { @@ -1215,14 +1218,15 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) 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(dumpHeader.length), std::size(macro) / 2u); + std::array macro{}; + auto length = std::min(static_cast(dumpHeader.length), macro.size() / 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); } + m_MidiCfg.Zxx[dump] = std::string_view{macro.data(), length * 2}; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index f3c3da7b8..ea4ab724c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -303,6 +303,7 @@ struct MO3Sample void ConvertToMPT(ModSample &mptSmp, MODTYPE type, bool frequencyIsHertz) const { mptSmp.Initialize(); + mptSmp.SetDefaultCuePoints(); if(type & (MOD_TYPE_IT | MOD_TYPE_S3M)) { if(frequencyIsHertz) @@ -907,16 +908,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) for(uint32 i = 0; i < 16; i++) { if(fileHeader.sfxMacros[i]) - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiSFXExt[i]) = MPT_AFORMAT("F0F0{}z")(mpt::afmt::HEX0<2>(fileHeader.sfxMacros[i] - 1)); + m_MidiCfg.SFx[i] = MPT_AFORMAT("F0F0{}z")(mpt::afmt::HEX0<2>(fileHeader.sfxMacros[i] - 1)); else - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiSFXExt[i]) = ""; + m_MidiCfg.SFx[i] = ""; } for(uint32 i = 0; i < 128; i++) { if(fileHeader.fixedMacros[i][1]) - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i]) = MPT_AFORMAT("F0F0{}{}")(mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][1] - 1), mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][0].get())); + m_MidiCfg.Zxx[i] = MPT_AFORMAT("F0F0{}{}")(mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][1] - 1), mpt::afmt::HEX0<2>(fileHeader.fixedMacros[i][0].get())); else - mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i]) = ""; + m_MidiCfg.Zxx[i] = ""; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 42e3041bb..b1f47c70f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -209,7 +209,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co struct MODFileHeader { uint8be numOrders; - uint8be restartPos; + uint8be restartPos; // Tempo (early SoundTracker) or restart position (only PC trackers?) uint8be orderList[128]; }; @@ -585,6 +585,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN const size_t patternStartOffset = file.GetPosition(); const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset; + const size_t sizeWithOfficialPatterns = sizeWithoutPatterns + officialPatterns * numChannels * 256; if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == (file.GetLength() & ~1)) { @@ -595,8 +596,9 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN if(ValidateMODPatternData(file, 16, true)) numChannels = 8; file.Seek(patternStartOffset); - } else if(numPatterns != officialPatterns && validateHiddenPatterns) + } else if(numPatterns != officialPatterns && (validateHiddenPatterns || sizeWithOfficialPatterns == file.GetLength())) { + // 15-sample SoundTracker specifics: // Fix SoundTracker modules where "hidden" patterns should be ignored. // razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b) // and captain_fizz.mod (MD5 55bd89fe5a8e345df65438dbfc2df94e, SHA1 9e0e8b7dc67939885435ea8d3ff4be7704207a43) @@ -609,28 +611,21 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN // only play correctly if we ignore the hidden patterns. // Hence, we have a peek at the first hidden pattern and check if it contains a lot of illegal data. // If that is the case, we assume it's part of the sample data and only consider the "official" patterns. - file.Seek(patternStartOffset + officialPatterns * 1024); + + // 31-sample NoiseTracker / ProTracker specifics: + // Interestingly, (broken) variants of the ProTracker modules + // "killing butterfly" (MD5 bd676358b1dbb40d40f25435e845cf6b, SHA1 9df4ae21214ff753802756b616a0cafaeced8021), + // "quartex" by Reflex (MD5 35526bef0fb21cb96394838d94c14bab, SHA1 116756c68c7b6598dcfbad75a043477fcc54c96c), + // seem to have the "correct" file size when only taking the "official" patterns into account, but they only play + // correctly when also loading the inofficial patterns. + // On the other hand, "Shofixti Ditty.mod" from Star Control 2 (MD5 62b7b0819123400e4d5a7813eef7fc7d, SHA1 8330cd595c61f51c37a3b6f2a8559cf3fcaaa6e8) + // doesn't sound correct when taking the second "inofficial" pattern into account. + file.Seek(patternStartOffset + officialPatterns * numChannels * 256); if(!ValidateMODPatternData(file, 64, true)) numPatterns = officialPatterns; file.Seek(patternStartOffset); } -#ifdef MPT_BUILD_DEBUG - // Check if the "hidden" patterns in the order list are actually real, i.e. if they are saved in the file. - // OpenMPT did this check in the past, but no other tracker appears to do this. - // Interestingly, (broken) variants of the ProTracker modules - // "killing butterfly" (MD5 bd676358b1dbb40d40f25435e845cf6b, SHA1 9df4ae21214ff753802756b616a0cafaeced8021), - // "quartex" by Reflex (MD5 35526bef0fb21cb96394838d94c14bab, SHA1 116756c68c7b6598dcfbad75a043477fcc54c96c), - // seem to have the "correct" file size when only taking the "official" patterns into account, but they only play - // correctly when also loading the inofficial patterns. - // See also the above check for ambiguities with SoundTracker modules. - // Keep this assertion in the code to find potential other broken MODs. - if(numPatterns != officialPatterns && sizeWithoutPatterns + officialPatterns * numChannels * 256 == file.GetLength()) - { - MPT_ASSERT(false); - //numPatterns = officialPatterns; - } else -#endif if(numPatternsIllegal > numPatterns && sizeWithoutPatterns + numPatternsIllegal * numChannels * 256 == file.GetLength()) { // Even those illegal pattern indexes (> 128) appear to be valid... What a weird file! @@ -779,7 +774,7 @@ static bool CheckMODMagic(const char magic[4], MODMagicResult &result) CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, const uint64 *pfilesize) { - if(!file.CanRead(1080 + 4)) + if(!file.LengthIsAtLeast(1080 + 4)) { return ProbeWantMoreData; } @@ -1290,7 +1285,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) } #endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER - // Fix VBlank MODs. Arbitrary threshold: 9 minutes (enough for Guitar Slinger...). + // Fix VBlank MODs. Arbitrary threshold: 8 minutes (enough for "frame of mind" by Dascon...). // Basically, this just converts all tempo commands into speed commands // for MODs which are supposed to have VBlank timing (instead of CIA timing). // There is no perfect way to do this, since both MOD types look the same, @@ -1303,12 +1298,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) if(isMdKd && hasTempoCommands && !definitelyCIA) { const double songTime = GetLength(eNoAdjust).front().duration; - if(songTime >= 540.0) + if(songTime >= 480.0) { m_playBehaviour.set(kMODVBlankTiming); if(GetLength(eNoAdjust, GetLengthTarget(songTime)).front().targetReached) { - // This just makes things worse, song is at least as long as in CIA mode (e.g. in "Stary Hallway" by Neurodancer) + // This just makes things worse, song is at least as long as in CIA mode // Obviously we should keep using CIA timing then... m_playBehaviour.reset(kMODVBlankTiming); } else diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index b95586c07..6be8a513b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -1027,6 +1027,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { ModSample &mptSmp = Samples[i + 1]; mptSmp.Initialize(MOD_TYPE_IT); + mptSmp.SetDefaultCuePoints(); MT2Sample sampleHeader; sampleChunk.ReadStruct(sampleHeader); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp index 096073e7d..267feaf9c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp @@ -534,6 +534,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) const bool useGUS = gusAddresses > 1; m_playBehaviour.set(kST3PortaSampleChange, useGUS); m_playBehaviour.set(kST3SampleSwap, !useGUS); + m_playBehaviour.set(kITShortSampleRetrig, !useGUS); // Only half the truth but close enough for now m_modFormat.madeWithTracker += useGUS ? UL_(" (GUS)") : UL_(" (SB)"); // ST3's GUS driver doesn't use this value. Ignoring it fixes the balance between FM and PCM samples (e.g. in Rotagilla by Manwe) if(useGUS) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp index 68eefebdf..b232a5b6f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp @@ -621,8 +621,10 @@ struct SymInstrument } // This must be applied last because some sample processors are time-dependent and Symphonie would be doing this during playback instead + mptSmp.RemoveAllCuePoints(); if(type == Sustain && numRepetitions > 0 && loopLen > 0) { + mptSmp.cues[0] = loopStart + loopLen * (numRepetitions + 1u); mptSmp.nSustainStart = loopStart; // This is of purely informative value and not used for playback mptSmp.nSustainEnd = loopStart + loopLen; @@ -955,18 +957,18 @@ static bool ConvertDSP(const SymEvent event, MIDIMacroConfigData::Macro ¯o, const uint8 reso = static_cast(std::min(127, event.inst * 127 / 185)); if(type == 1) // lowpass filter - mpt::String::WriteAutoBuf(macro) = MPT_AFORMAT("F0F000{} F0F001{} F0F00200")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); + macro = MPT_AFORMAT("F0F000{} F0F001{} F0F00200")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); else if(type == 2) // highpass filter - mpt::String::WriteAutoBuf(macro) = MPT_AFORMAT("F0F000{} F0F001{} F0F00210")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); + macro = MPT_AFORMAT("F0F000{} F0F001{} F0F00210")(mpt::afmt::HEX0<2>(cutoff), mpt::afmt::HEX0<2>(reso)); else // no filter or unsupported filter type - mpt::String::WriteAutoBuf(macro) = "F0F0007F F0F00100"; + macro = "F0F0007F F0F00100"; return true; } else if(event.command == SymEvent::DSPEcho) { const uint8 type = (event.note < 5) ? event.note : 0; const uint8 length = (event.param < 128) ? event.param : 127; const uint8 feedback = (event.inst < 128) ? event.inst : 127; - mpt::String::WriteAutoBuf(macro) = MPT_AFORMAT("F0F080{} F0F081{} F0F082{}")(mpt::afmt::HEX0<2>(type), mpt::afmt::HEX0<2>(length), mpt::afmt::HEX0<2>(feedback)); + macro = MPT_AFORMAT("F0F080{} F0F081{} F0F082{}")(mpt::afmt::HEX0<2>(type), mpt::afmt::HEX0<2>(length), mpt::afmt::HEX0<2>(feedback)); return true; } else if(event.command == SymEvent::DSPDelay) { @@ -1218,7 +1220,7 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags) static_assert(MAX_SAMPLES >= MAX_INSTRUMENTS); m_nSamples = std::max(m_nSamples, m_nInstruments); - // Supporting this is probably rather useless, as the paths will always be Amiga paths. We just take the filename without path for now. + // Supporting this is probably rather useless, as the paths will always be full Amiga paths. We just take the filename without path for now. if(externalSamples) { #ifdef MPT_EXTERNAL_SAMPLES @@ -1452,7 +1454,8 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags) break; case SymEvent::KeyOff: - // TODO needs note + if(m.note == NOTE_NONE) + m.note = chnState.lastNote; m.volcmd = VOLCMD_OFFSET; m.vol = 1; break; @@ -1642,10 +1645,10 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags) { m.command = CMD_MIDI; m.param = macroMap[event]; - } else if(macroMap.size() < std::size(m_MidiCfg.szMidiZXXExt)) + } else if(macroMap.size() < m_MidiCfg.Zxx.size()) { uint8 param = static_cast(macroMap.size()); - if(ConvertDSP(event, m_MidiCfg.szMidiZXXExt[param], *this)) + if(ConvertDSP(event, m_MidiCfg.Zxx[param], *this)) { m.command = CMD_MIDI; m.param = macroMap[event] = 0x80 | param; @@ -1925,8 +1928,7 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags) { InitChannel(chn); ChnSettings[chn].nPan = (chn & 1) ? 256 : 0; - if(useDSP) - ChnSettings[chn].nMixPlugin = 1; // For MIDI macros controlling the echo DSP + ChnSettings[chn].nMixPlugin = useDSP ? 1 : 0; // For MIDI macros controlling the echo DSP } m_modFormat.formatName = U_("Symphonie"); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp index bc1766643..6869eb2a4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp @@ -9,10 +9,8 @@ #include "stdafx.h" -#include "../soundlib/MIDIEvents.h" #include "MIDIMacros.h" -#include "../common/mptStringBuffer.h" -#include "../common/misc_util.h" +#include "../soundlib/MIDIEvents.h" #ifdef MODPLUG_TRACKER #include "Sndfile.h" @@ -23,24 +21,25 @@ OPENMPT_NAMESPACE_BEGIN ParameteredMacro MIDIMacroConfig::GetParameteredMacroType(uint32 macroIndex) const { - const std::string macro = GetSafeMacro(szMidiSFXExt[macroIndex]); + const std::string macro = SFx[macroIndex].NormalizedString(); for(uint32 i = 0; i < kSFxMax; i++) { ParameteredMacro sfx = static_cast(i); if(sfx != kSFxCustom) { - if(macro.compare(CreateParameteredMacro(sfx)) == 0) return sfx; + if(macro == CreateParameteredMacro(sfx)) + return sfx; } } // Special macros with additional "parameter": - if (macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_end)) <= 0 && macro.size() == 5) + if(macro.size() == 5 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_end)) <= 0) return kSFxCC; - if (macro.compare(CreateParameteredMacro(kSFxPlugParam, 0)) >= 0 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0x17F)) <= 0 && macro.size() == 7) + if(macro.size() == 7 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0)) >= 0 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0x17F)) <= 0) return kSFxPlugParam; - return kSFxCustom; // custom / unknown + return kSFxCustom; // custom / unknown } @@ -54,19 +53,10 @@ FixedMacro MIDIMacroConfig::GetFixedMacroType() const if(zxx != kZxxCustom) { // Prepare macro pattern to compare - Macro macros[128]; - CreateFixedMacro(macros, zxx); - - bool found = true; - for(uint32 j = 0; j < 128; j++) - { - if(strncmp(macros[j], szMidiZXXExt[j], MACRO_LENGTH)) - { - found = false; - break; - } - } - if(found) return zxx; + decltype(Zxx) fixedMacros{}; + CreateFixedMacro(fixedMacros, zxx); + if(fixedMacros == Zxx) + return zxx; } } return kZxxCustom; // Custom setup @@ -77,17 +67,17 @@ void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, Parametere { switch(macroType) { - case kSFxUnused: mpt::String::WriteAutoBuf(parameteredMacro) = ""; break; - case kSFxCutoff: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F000z"; break; - case kSFxReso: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F001z"; break; - case kSFxFltMode: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F002z"; break; - case kSFxDryWet: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F003z"; break; - case kSFxCC: mpt::String::WriteAutoBuf(parameteredMacro) = MPT_AFORMAT("Bc{}z")(mpt::afmt::HEX0<2>(subType & 0x7F)); break; - case kSFxPlugParam: mpt::String::WriteAutoBuf(parameteredMacro) = MPT_AFORMAT("F0F{}z")(mpt::afmt::HEX0<3>(std::min(subType, 0x17F) + 0x80)); break; - case kSFxChannelAT: mpt::String::WriteAutoBuf(parameteredMacro) = "Dcz"; break; - case kSFxPolyAT: mpt::String::WriteAutoBuf(parameteredMacro) = "Acnz"; break; - case kSFxPitch: mpt::String::WriteAutoBuf(parameteredMacro) = "Ec00z"; break; - case kSFxProgChange: mpt::String::WriteAutoBuf(parameteredMacro) = "Ccz"; break; + case kSFxUnused: parameteredMacro = ""; break; + case kSFxCutoff: parameteredMacro = "F0F000z"; break; + case kSFxReso: parameteredMacro = "F0F001z"; break; + case kSFxFltMode: parameteredMacro = "F0F002z"; break; + case kSFxDryWet: parameteredMacro = "F0F003z"; break; + case kSFxCC: parameteredMacro = MPT_AFORMAT("Bc{}z")(mpt::afmt::HEX0<2>(subType & 0x7F)); break; + case kSFxPlugParam: parameteredMacro = MPT_AFORMAT("F0F{}z")(mpt::afmt::HEX0<3>(std::min(subType, 0x17F) + 0x80)); break; + case kSFxChannelAT: parameteredMacro = "Dcz"; break; + case kSFxPolyAT: parameteredMacro = "Acnz"; break; + case kSFxPitch: parameteredMacro = "Ec00z"; break; + case kSFxProgChange: parameteredMacro = "Ccz"; break; case kSFxCustom: default: MPT_ASSERT_NOTREACHED(); @@ -98,59 +88,59 @@ void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, Parametere std::string MIDIMacroConfig::CreateParameteredMacro(ParameteredMacro macroType, int subType) const { - Macro parameteredMacro; + Macro parameteredMacro{}; CreateParameteredMacro(parameteredMacro, macroType, subType); - return mpt::String::ReadAutoBuf(parameteredMacro); + return parameteredMacro; } // Create Zxx (Z80 - ZFF) from preset -void MIDIMacroConfig::CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const +void MIDIMacroConfig::CreateFixedMacro(std::array &fixedMacros, FixedMacro macroType) const { - for(uint32 i = 0; i < 128; i++) + for(uint32 i = 0; i < kZxxMacros; i++) { uint32 param = i; switch(macroType) { case kZxxUnused: - mpt::String::WriteAutoBuf(fixedMacros[i]) = ""; + fixedMacros[i] = ""; break; case kZxxReso4Bit: param = i * 8; if(i < 16) - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); else - mpt::String::WriteAutoBuf(fixedMacros[i]) = ""; + fixedMacros[i] = ""; break; case kZxxReso7Bit: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxCutoff: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F000{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F000{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxFltMode: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxResoFltMode: param = (i & 0x0F) * 8; if(i < 16) - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param)); else if(i < 32) - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param)); else - mpt::String::WriteAutoBuf(fixedMacros[i]) = ""; + fixedMacros[i] = ""; break; case kZxxChannelAT: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("Dc{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("Dc{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxPolyAT: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("Acn{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("Acn{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxPitch: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("Ec00{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("Ec00{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxProgChange: - mpt::String::WriteAutoBuf(fixedMacros[i]) = MPT_AFORMAT("Cc{}")(mpt::afmt::HEX0<2>(param)); + fixedMacros[i] = MPT_AFORMAT("Cc{}")(mpt::afmt::HEX0<2>(param)); break; case kZxxCustom: default: @@ -161,19 +151,14 @@ void MIDIMacroConfig::CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro mac } -#ifdef MODPLUG_TRACKER - bool MIDIMacroConfig::operator== (const MIDIMacroConfig &other) const { - for(auto left = begin(), right = other.begin(); left != end(); left++, right++) - { - if(strncmp(*left, *right, MACRO_LENGTH)) - return false; - } - return true; + return std::equal(begin(), end(), other.begin()); } +#ifdef MODPLUG_TRACKER + // Returns macro description including plugin parameter / MIDI CC information CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin) const { @@ -262,7 +247,7 @@ CString MIDIMacroConfig::GetFixedMacroName(FixedMacro macroType) const int MIDIMacroConfig::MacroToPlugParam(uint32 macroIndex) const { - const std::string macro = GetSafeMacro(szMidiSFXExt[macroIndex]); + const std::string macro = SFx[macroIndex].NormalizedString(); int code = 0; const char *param = macro.c_str(); @@ -281,7 +266,7 @@ int MIDIMacroConfig::MacroToPlugParam(uint32 macroIndex) const int MIDIMacroConfig::MacroToMidiCC(uint32 macroIndex) const { - const std::string macro = GetSafeMacro(szMidiSFXExt[macroIndex]); + const std::string macro = SFx[macroIndex].NormalizedString(); int code = 0; const char *param = macro.c_str(); @@ -297,7 +282,7 @@ int MIDIMacroConfig::MacroToMidiCC(uint32 macroIndex) const int MIDIMacroConfig::FindMacroForParam(PlugParamIndex param) const { - for(int macroIndex = 0; macroIndex < NUM_MACROS; macroIndex++) + for(int macroIndex = 0; macroIndex < kSFxMacros; macroIndex++) { if(GetParameteredMacroType(macroIndex) == kSFxPlugParam && MacroToPlugParam(macroIndex) == param) { @@ -314,41 +299,20 @@ int MIDIMacroConfig::FindMacroForParam(PlugParamIndex param) const // i.e. the configuration that is assumed when loading a file that has no macros embedded. bool MIDIMacroConfig::IsMacroDefaultSetupUsed() const { - const MIDIMacroConfig defaultConfig; - - // TODO - Global macros (currently not checked because they are not editable) - - // SF0: Z00-Z7F controls cutoff, all other parametered macros are unused - for(uint32 i = 0; i < NUM_MACROS; i++) - { - if(GetParameteredMacroType(i) != defaultConfig.GetParameteredMacroType(i)) - { - return false; - } - } - - // Z80-Z8F controls resonance - if(GetFixedMacroType() != defaultConfig.GetFixedMacroType()) - { - return false; - } - - return true; + return *this == MIDIMacroConfig{}; } // Reset MIDI macro config to default values. void MIDIMacroConfig::Reset() { - MemsetZero(szMidiGlb); - MemsetZero(szMidiSFXExt); - MemsetZero(szMidiZXXExt); + std::fill(begin(), end(), Macro{}); - strcpy(szMidiGlb[MIDIOUT_START], "FF"); - strcpy(szMidiGlb[MIDIOUT_STOP], "FC"); - strcpy(szMidiGlb[MIDIOUT_NOTEON], "9c n v"); - strcpy(szMidiGlb[MIDIOUT_NOTEOFF], "9c n 0"); - strcpy(szMidiGlb[MIDIOUT_PROGRAM], "Cc p"); + Global[MIDIOUT_START] = "FF"; + Global[MIDIOUT_STOP] = "FC"; + Global[MIDIOUT_NOTEON] = "9c n v"; + Global[MIDIOUT_NOTEOFF] = "9c n 0"; + Global[MIDIOUT_PROGRAM] = "Cc p"; // SF0: Z00-Z7F controls cutoff CreateParameteredMacro(0, kSFxCutoff); // Z80-Z8F controls resonance @@ -359,8 +323,8 @@ void MIDIMacroConfig::Reset() // Clear all Zxx macros so that they do nothing. void MIDIMacroConfig::ClearZxxMacros() { - MemsetZero(szMidiSFXExt); - MemsetZero(szMidiZXXExt); + std::fill(SFx.begin(), SFx.end(), Macro{}); + std::fill(Zxx.begin(), Zxx.end(), Macro{}); } @@ -369,27 +333,7 @@ void MIDIMacroConfig::Sanitize() { for(auto ¯o : *this) { - macro[MACRO_LENGTH - 1] = '\0'; - std::fill(std::find(std::begin(macro), std::end(macro), '\0'), std::end(macro), '\0'); - } -} - - -// Helper function for UpgradeMacros() -void MIDIMacroConfig::UpgradeMacroString(Macro ¯o) const -{ - for(auto &c : macro) - { - if(c >= 'a' && c <= 'f') // Both A-F and a-f were treated as hex constants - { - c = c - 'a' + 'A'; - } else if(c == 'K' || c == 'k') // Channel was K or k - { - c = 'c'; - } else if(c == 'X' || c == 'x' || c == 'Y' || c == 'y') // Those were pointless - { - c = 'z'; - } + macro.Sanitize(); } } @@ -399,15 +343,15 @@ void MIDIMacroConfig::UpgradeMacros() { for(auto ¯o : *this) { - UpgradeMacroString(macro); + macro.UpgradeLegacyMacro(); } } // Normalize by removing blanks and other unwanted characters from macro strings for internal usage. -std::string MIDIMacroConfig::GetSafeMacro(const Macro ¯o) const +std::string MIDIMacroConfig::Macro::NormalizedString() const { - std::string sanitizedMacro = macro; + std::string sanitizedMacro = *this; std::string::size_type pos; while((pos = sanitizedMacro.find_first_not_of("0123456789ABCDEFabchmnopsuvxyz")) != std::string::npos) @@ -419,4 +363,35 @@ std::string MIDIMacroConfig::GetSafeMacro(const Macro ¯o) const } +void MIDIMacroConfig::Macro::Sanitize() noexcept +{ + m_data.back() = '\0'; + const auto length = Length(); + std::fill(m_data.begin() + length, m_data.end(), '\0'); + for(size_t i = 0; i < length; i++) + { + if(m_data[i] < 32 || m_data[i] >= 127) + m_data[i] = ' '; + } +} + + +void MIDIMacroConfig::Macro::UpgradeLegacyMacro() noexcept +{ + for(auto &c : m_data) + { + if(c >= 'a' && c <= 'f') // Both A-F and a-f were treated as hex constants + { + c = c - 'a' + 'A'; + } else if(c == 'K' || c == 'k') // Channel was K or k + { + c = 'c'; + } else if(c == 'X' || c == 'x' || c == 'Y' || c == 'y') // Those were pointless + { + c = 'z'; + } + } +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h index 75beba566..efbe26891 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h @@ -18,8 +18,10 @@ OPENMPT_NAMESPACE_BEGIN enum { - NUM_MACROS = 16, // number of parametered macros - MACRO_LENGTH = 32, // max number of chars per macro + kGlobalMacros = 9, // Number of global macros + kSFxMacros = 16, // Number of parametered macros + kZxxMacros = 128, // Number of fixed macros + kMacroLength = 32, // Max number of chars per macro }; OPENMPT_NAMESPACE_END @@ -70,7 +72,7 @@ enum FixedMacro // Global macro types -enum +enum GlobalMacro { MIDIOUT_START = 0, MIDIOUT_STOP, @@ -86,19 +88,74 @@ enum struct MIDIMacroConfigData { - typedef char Macro[MACRO_LENGTH]; - // encoding is ASCII - Macro szMidiGlb[9]; // Global MIDI macros - Macro szMidiSFXExt[16]; // Parametric MIDI macros - Macro szMidiZXXExt[128]; // Fixed MIDI macros + struct Macro + { + public: + Macro &operator=(const Macro &other) = default; + Macro &operator=(const std::string_view other) noexcept + { + const size_t copyLength = std::min({m_data.size() - 1u, other.size(), other.find('\0')}); + std::copy(other.begin(), other.begin() + copyLength, m_data.begin()); + m_data[copyLength] = '\0'; + Sanitize(); + return *this; + } - Macro *begin() { return std::begin(szMidiGlb); } - const Macro *begin() const { return std::begin(szMidiGlb); } - Macro *end() { return std::end(szMidiZXXExt); } - const Macro *end() const { return std::end(szMidiZXXExt); } + bool operator==(const Macro &other) const noexcept + { + return m_data == other.m_data; // Don't care about data past null-terminator as operator= and Sanitize() ensure there is no data behind it. + } + bool operator!=(const Macro &other) const noexcept + { + return !(*this == other); + } + + operator mpt::span() const noexcept + { + return {m_data.data(), Length()}; + } + operator std::string_view() const noexcept + { + return {m_data.data(), Length()}; + } + operator std::string() const + { + return {m_data.data(), Length()}; + } + + MPT_CONSTEXPR20_FUN size_t Length() const noexcept + { + return static_cast(std::distance(m_data.begin(), std::find(m_data.begin(), m_data.end(), '\0'))); + } + + MPT_CONSTEXPR20_FUN void Clear() noexcept + { + m_data.fill('\0'); + } + + // Remove blanks and other unwanted characters from macro strings for internal usage. + std::string NormalizedString() const; + + void Sanitize() noexcept; + void UpgradeLegacyMacro() noexcept; + + private: + std::array m_data; + }; + + std::array Global; + std::array SFx; // Parametered macros for Z00...Z7F + std::array Zxx; // Fixed macros Z80...ZFF + + constexpr Macro *begin() noexcept {return Global.data(); } + constexpr const Macro *begin() const noexcept { return Global.data(); } + constexpr Macro *end() noexcept { return Zxx.data() + Zxx.size(); } + constexpr const Macro *end() const noexcept { return Zxx.data() + Zxx.size(); } }; -MPT_BINARY_STRUCT(MIDIMacroConfigData, 4896) // this is directly written to files, so the size must be correct! +// This is directly written to files, so the size must be correct! +MPT_BINARY_STRUCT(MIDIMacroConfigData::Macro, 32) +MPT_BINARY_STRUCT(MIDIMacroConfigData, 4896) class MIDIMacroConfig : public MIDIMacroConfigData { @@ -117,22 +174,23 @@ protected: public: void CreateParameteredMacro(uint32 macroIndex, ParameteredMacro macroType, int subType = 0) { - CreateParameteredMacro(szMidiSFXExt[macroIndex], macroType, subType); + if(macroIndex < std::size(SFx)) + CreateParameteredMacro(SFx[macroIndex], macroType, subType); } std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const; protected: - void CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const; + void CreateFixedMacro(std::array &fixedMacros, FixedMacro macroType) const; public: void CreateFixedMacro(FixedMacro macroType) { - CreateFixedMacro(szMidiZXXExt, macroType); + CreateFixedMacro(Zxx, macroType); } -#ifdef MODPLUG_TRACKER + bool operator==(const MIDIMacroConfig &other) const; + bool operator!=(const MIDIMacroConfig &other) const { return !(*this == other); } - bool operator== (const MIDIMacroConfig &other) const; - bool operator!= (const MIDIMacroConfig &other) const { return !(*this == other); } +#ifdef MODPLUG_TRACKER // Translate macro type or macro string to macro name CString GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin = nullptr) const; @@ -162,15 +220,6 @@ public: // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void UpgradeMacros(); - -protected: - - // Helper function for FixMacroFormat() - void UpgradeMacroString(Macro ¯o) const; - - // Remove blanks and other unwanted characters from macro strings for internal usage. - std::string GetSafeMacro(const Macro ¯o) const; - }; static_assert(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp index 05fd60c90..d5095f50b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp @@ -314,13 +314,9 @@ void ModInstrument::Transpose(int8 amount) } -uint8 ModInstrument::GetMIDIChannel(const CSoundFile &sndFile, CHANNELINDEX chn) const +uint8 ModInstrument::GetMIDIChannel(const ModChannel &channel, 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((channel.nMasterChn ? (channel.nMasterChn - 1u) : chn) % 16u); else if(HasValidMIDIChannel()) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h index 74eb071d7..68af431b2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN -class CSoundFile; +struct ModChannel; // Instrument Nodes struct EnvelopeNode @@ -150,7 +150,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; + uint8 GetMIDIChannel(const ModChannel &channel, CHANNELINDEX chn) const; void SetTuning(CTuning *pT) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp index 2b693bec3..9e9597cac 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp @@ -148,7 +148,7 @@ void ModSample::Initialize(MODTYPE type) rootNote = 0; filename = ""; - SetDefaultCuePoints(); + RemoveAllCuePoints(); } @@ -504,16 +504,29 @@ void ModSample::Transpose(double amount) } +// Check if the sample has any valid cue points +bool ModSample::HasAnyCuePoints() const +{ + if(uFlags[CHN_ADLIB]) + return false; + for(auto pt : cues) + { + if(pt < nLength) + return true; + } + return false; +} + + // Check if the sample's cue points are the default cue point set. bool ModSample::HasCustomCuePoints() const { - if(!uFlags[CHN_ADLIB]) + if(uFlags[CHN_ADLIB]) + return false; + for(SmpLength i = 0; i < std::size(cues); i++) { - for(SmpLength i = 0; i < std::size(cues); i++) - { - if(cues[i] != (i + 1) << 11) - return true; - } + if(cues[i] != (i + 1) << 11) + return true; } return false; } @@ -539,6 +552,13 @@ void ModSample::Set16BitCuePoints() } +void ModSample::RemoveAllCuePoints() +{ + if(!uFlags[CHN_ADLIB]) + cues.fill(MAX_SAMPLE_LENGTH); +} + + void ModSample::SetAdlib(bool enable, OPLPatch patch) { if(!enable && uFlags[CHN_ADLIB]) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h index ec8d213ac..07a2e1f4a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h @@ -160,11 +160,14 @@ struct ModSample // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) void Transpose(double amount); + // Check if the sample has any valid cue points + bool HasAnyCuePoints() const; // Check if the sample's cue points are the default cue point set. bool HasCustomCuePoints() const; void SetDefaultCuePoints(); // Set cue points so that they are suitable for regular offset command extension void Set16BitCuePoints(); + void RemoveAllCuePoints(); void SetAdlib(bool enable, OPLPatch patch = OPLPatch{{}}); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp index c9ccc267e..a038e1349 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp @@ -35,6 +35,20 @@ #include #include +#if MPT_OS_OPENBSD +// This is kind-of a hack. +// See . +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS +#endif +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif +#endif #include #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 453ba44c1..bc6232b55 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -64,10 +64,6 @@ public: uint8 vol = 0xFF; }; -#ifndef NO_PLUGINS - typedef std::map, uint16> PlugParamMap; - PlugParamMap plugParams; -#endif std::vector chnSettings; double elapsedTime; static constexpr uint32 IGNORE_CHANNEL = uint32_max; @@ -81,9 +77,8 @@ public: void Reset() { -#ifndef NO_PLUGINS - plugParams.clear(); -#endif + if(state->m_midiMacroEvaluationResults) + state->m_midiMacroEvaluationResults.emplace(); elapsedTime = 0.0; state->m_lTotalSampleCount = 0; state->m_nMusicSpeed = sndFile.m_nDefaultSpeed; @@ -295,6 +290,9 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } } + if(adjustMode & eAdjust) + playState.m_midiMacroEvaluationResults.emplace(); + // If samples are being synced, force them to resync if tick duration changes uint32 oldTickDuration = 0; bool breakToRow = false; @@ -469,9 +467,9 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(p->IsPcNote()) { #ifndef NO_PLUGINS - if((adjustMode & eAdjust) && p->instr > 0 && p->instr <= MAX_MIXPLUGINS) + if(playState.m_midiMacroEvaluationResults && p->instr > 0 && p->instr <= MAX_MIXPLUGINS) { - memory.plugParams[std::make_pair(p->instr, p->GetValueVolCol())] = p->GetValueEffectCol(); + playState.m_midiMacroEvaluationResults->pluginParameter[{static_cast(p->instr - 1), p->GetValueVolCol()}] = p->GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue); } #endif // NO_PLUGINS chn.rowCommand.Clear(); @@ -820,6 +818,17 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case CMD_PANBRELLO: Panbrello(chn, param); break; + + case CMD_MIDI: + case CMD_SMOOTHMIDI: + if(param < 0x80) + ProcessMIDIMacro(playState, nChn, false, m_MidiCfg.SFx[chn.nActiveMacro], chn.rowCommand.param, 0); + else + ProcessMIDIMacro(playState, nChn, false, m_MidiCfg.Zxx[param & 0x7F], chn.rowCommand.param, 0); + break; + + default: + break; } switch(chn.rowCommand.volcmd) @@ -925,6 +934,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod chn.nNewNote = chn.nLastNote; if(chn.nNewIns != 0) InstrumentChange(chn, chn.nNewIns, porta); NoteChange(chn, m.note, porta); + HandleDigiSamplePlayDirection(playState, nChn); memory.chnSettings[nChn].incChanged = true; if((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xD0 && paramLo < numTicks) @@ -1078,6 +1088,10 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } break; + case CMD_DIGIREVERSESAMPLE: + DigiBoosterSampleReverse(chn, m.param); + break; + case CMD_FINETUNE: case CMD_FINETUNE_SMOOTH: memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far @@ -1161,6 +1175,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { if(retval.targetReached || target.mode == GetLengthTarget::NoTarget) { + const auto midiMacroEvaluationResults = std::move(playState.m_midiMacroEvaluationResults); + playState.m_midiMacroEvaluationResults.reset(); // Target found, or there is no target (i.e. play whole song)... m_PlayState = std::move(playState); m_PlayState.ResetGlobalVolumeRamping(); @@ -1190,11 +1206,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } #ifndef NO_PLUGINS - // If there were any PC events, update plugin parameters to their latest value. + // If there were any PC events or MIDI macros updating plugin parameters, update plugin parameters to their latest value. std::bitset plugSetProgram; - for(const auto ¶m : memory.plugParams) + for(const auto &[plugParam, value] : midiMacroEvaluationResults->pluginParameter) { - PLUGINDEX plug = param.first.first - 1; + PLUGINDEX plug = plugParam.first; IMixPlugin *plugin = m_MixPlugins[plug].pMixPlugin; if(plugin != nullptr) { @@ -1204,7 +1220,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod plugSetProgram.set(plug); plugin->BeginSetProgram(); } - plugin->SetParameter(param.first.second, param.second / PlugParamValue(ModCommand::maxColumnValue)); + plugin->SetParameter(plugParam.second, value); } } if(plugSetProgram.any()) @@ -1217,6 +1233,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } } } + // Do the same for dry/wet ratios + for(const auto &[plug, dryWetRatio] : midiMacroEvaluationResults->pluginDryWetRatio) + { + m_MixPlugins[plug].fDryRatio = dryWetRatio; + } #endif // NO_PLUGINS } else if(adjustMode != eAdjustOnSuccess) { @@ -2243,7 +2264,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo IMixPlugin *pPlugin = nullptr; if(srcChn.HasMIDIOutput() && ModCommand::IsNote(srcChn.nNote)) // instro sends to a midi chan { - PLUGINDEX plugin = GetBestPlugin(nChn, PrioritiseInstrument, RespectMutes); + PLUGINDEX plugin = GetBestPlugin(m_PlayState, nChn, PrioritiseInstrument, RespectMutes); if(plugin > 0 && plugin <= MAX_MIXPLUGINS) { @@ -2860,6 +2881,7 @@ bool CSoundFile::ProcessEffects() } NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn); + HandleDigiSamplePlayDirection(m_PlayState, nChn); if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { chn.dwFlags.set(CHN_FASTVOLRAMP); @@ -3342,7 +3364,7 @@ bool CSoundFile::ProcessEffects() { SetFinetune(nChn, m_PlayState, cmd == CMD_FINETUNE_SMOOTH); #ifndef NO_PLUGINS - if(IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); plugin != nullptr) + if(IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); plugin != nullptr) plugin->MidiPitchBendRaw(chn.GetMIDIPitchBend(), nChn); #endif // NO_PLUGINS } @@ -3443,6 +3465,11 @@ bool CSoundFile::ProcessEffects() } break; #endif // NO_PLUGINS + + // Digi Booster sample reverse + case CMD_DIGIREVERSESAMPLE: + DigiBoosterSampleReverse(chn, static_cast(param)); + break; } if(m_playBehaviour[kST3EffectMemory] && param != 0) @@ -3823,7 +3850,7 @@ void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) if(pitchBend) { #ifndef NO_PLUGINS - IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); + IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { int8 pwd = 13; // Early OpenMPT legacy... Actually it's not *exactly* 13, but close enough... @@ -4774,23 +4801,96 @@ void CSoundFile::InvertLoop(ModChannel &chn) // Process a MIDI Macro. // Parameters: +// playState: The playback state to operate on. // nChn: Mod channel to apply macro on // isSmooth: If true, internal macros are interpolated between two rows -// macro: Actual MIDI Macro string -// param: Parameter for parametric macros (Z00 - Z7F) +// macro: MIDI Macro string to process +// param: Parameter for parametric macros (Zxx / \xx parameter) // plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected) -void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *macro, uint8 param, PLUGINDEX plugin) +void CSoundFile::ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const MIDIMacroConfigData::Macro ¯o, uint8 param, PLUGINDEX plugin) { - ModChannel &chn = m_PlayState.Chn[nChn]; - const ModInstrument *pIns = GetNumInstruments() ? chn.pModInstrument : nullptr; + playState.m_midiMacroScratchSpace.resize(macro.Length() + 1); + auto out = mpt::as_span(playState.m_midiMacroScratchSpace); + + ParseMIDIMacro(playState, nChn, isSmooth, macro, out, param, plugin); + + // Macro string has been parsed and translated, now send the message(s)... + uint32 outSize = static_cast(out.size()); + uint32 sendPos = 0; + uint8 runningStatus = 0; + while(sendPos < out.size()) + { + uint32 sendLen = 0; + if(out[sendPos] == 0xF0) + { + // SysEx start + if((outSize - sendPos >= 4) && (out[sendPos + 1] == 0xF0 || out[sendPos + 1] == 0xF1)) + { + // Internal macro (normal (F0F0) or extended (F0F1)), 4 bytes long + sendLen = 4; + } else + { + // SysEx message, find end of message + for(uint32 i = sendPos + 1; i < outSize; i++) + { + if(out[i] == 0xF7) + { + // Found end of SysEx message + sendLen = i - sendPos + 1; + break; + } + } + if(sendLen == 0) + { + // Didn't find end, so "invent" end of SysEx message + out[outSize++] = 0xF7; + sendLen = outSize - sendPos; + } + } + } else if(!(out[sendPos] & 0x80)) + { + // Missing status byte? Try inserting running status + if(runningStatus != 0) + { + sendPos--; + out[sendPos] = runningStatus; + } else + { + // No running status to re-use; skip this byte + sendPos++; + } + continue; + } else + { + // Other MIDI messages + sendLen = std::min(static_cast(MIDIEvents::GetEventLength(out[sendPos])), outSize - sendPos); + } + + if(sendLen == 0) + break; + + if(out[sendPos] < 0xF0) + { + runningStatus = out[sendPos]; + } + const auto midiMsg = out.subspan(sendPos, sendLen); + SendMIDIData(playState, nChn, isSmooth, midiMsg, plugin); + sendPos += sendLen; + } +} + + +void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, mpt::span &out, uint8 param, PLUGINDEX plugin) const +{ + ModChannel &chn = playState.Chn[nChn]; + const ModInstrument *pIns = chn.pModInstrument; - uint8 out[MACRO_LENGTH]; - uint32 outPos = 0; // output buffer position, which also equals the number of complete bytes const uint8 lastZxxParam = chn.lastZxxParam; // always interpolate based on original value in case z appears multiple times in macro string uint8 updateZxxParam = 0xFF; // avoid updating lastZxxParam immediately if macro contains both internal and external MIDI message - bool firstNibble = true; - for(uint32 pos = 0; pos < (MACRO_LENGTH - 1) && macro[pos]; pos++) + bool firstNibble = true; + size_t outPos = 0; // output buffer position, which also equals the number of complete bytes + for(size_t pos = 0; pos < macro.size() && outPos < out.size(); pos++) { bool isNibble = false; // did we parse a nibble or a byte value? uint8 data = 0; // data that has just been parsed @@ -4800,8 +4900,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * { isNibble = true; data = static_cast(macro[pos] - '0'); - } - else if(macro[pos] >= 'A' && macro[pos] <= 'F') + } else if(macro[pos] >= 'A' && macro[pos] <= 'F') { isNibble = true; data = static_cast(macro[pos] - 'A' + 0x0A); @@ -4811,19 +4910,19 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * isNibble = true; data = 0xFF; #ifndef NO_PLUGINS - const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); + const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS) { auto midiPlug = dynamic_cast(m_MixPlugins[plug - 1u].pMixPlugin); if(midiPlug) - data = midiPlug->GetMidiChannel(nChn); + data = midiPlug->GetMidiChannel(playState.Chn[nChn], nChn); } #endif // NO_PLUGINS if(data == 0xFF) { // Fallback if no plugin was found if(pIns) - data = pIns->GetMIDIChannel(*this, nChn); + data = pIns->GetMIDIChannel(playState.Chn[nChn], nChn); else data = 0; } @@ -4893,13 +4992,13 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * } else if(macro[pos] == 'z') { // Zxx parameter - data = param & 0x7F; + data = param; if(isSmooth && chn.lastZxxParam < 0x80 && (outPos < 3 || out[outPos - 3] != 0xF0 || out[outPos - 2] < 0xF0)) { // Interpolation for external MIDI messages - interpolation for internal messages // is handled separately to allow for more than 7-bit granularity where it's possible - data = static_cast(CalculateSmoothParamChange(lastZxxParam, data)); + data = static_cast(CalculateSmoothParamChange(playState, lastZxxParam, data)); chn.lastZxxParam = data; updateZxxParam = 0x80; } else if(updateZxxParam == 0xFF) @@ -4909,13 +5008,13 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * } else if(macro[pos] == 's') { // SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience) - uint32 startPos = outPos; + auto startPos = outPos; while(startPos > 0 && out[--startPos] != 0xF0); if(outPos - startPos < 5 || out[startPos] != 0xF0) { continue; } - for(uint32 p = startPos + 5; p != outPos; p++) + for(auto p = startPos + 5u; p != outPos; p++) { data += out[p]; } @@ -4940,7 +5039,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * firstNibble = !firstNibble; } else // parsed a byte (variable) { - if(!firstNibble) // From MIDI.TXT: '9n' is exactly the same as '09 n' or '9 n' -- so finish current byte first + if(!firstNibble) // From MIDI.TXT: '9n' is exactly the same as '09 n' or '9 n' -- so finish current byte first { outPos++; } @@ -4956,83 +5055,19 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * if(updateZxxParam < 0x80) chn.lastZxxParam = updateZxxParam; - // Macro string has been parsed and translated, now send the message(s)... - uint32 sendPos = 0; - uint8 runningStatus = 0; - while(sendPos < outPos) - { - uint32 sendLen = 0; - if(out[sendPos] == 0xF0) - { - // SysEx start - if((outPos - sendPos >= 4) && (out[sendPos + 1] == 0xF0 || out[sendPos + 1] == 0xF1)) - { - // Internal macro (normal (F0F0) or extended (F0F1)), 4 bytes long - sendLen = 4; - } else - { - // SysEx message, find end of message - for(uint32 i = sendPos + 1; i < outPos; i++) - { - if(out[i] == 0xF7) - { - // Found end of SysEx message - sendLen = i - sendPos + 1; - break; - } - } - if(sendLen == 0) - { - // Didn't find end, so "invent" end of SysEx message - out[outPos++] = 0xF7; - sendLen = outPos - sendPos; - } - } - } else if(!(out[sendPos] & 0x80)) - { - // Missing status byte? Try inserting running status - if(runningStatus != 0) - { - sendPos--; - out[sendPos] = runningStatus; - } else - { - // No running status to re-use; skip this byte - sendPos++; - } - continue; - } else - { - // Other MIDI messages - sendLen = std::min(static_cast(MIDIEvents::GetEventLength(out[sendPos])), outPos - sendPos); - } - - if(sendLen == 0) - break; - - if(out[sendPos] < 0xF0) - { - runningStatus = out[sendPos]; - } - uint32 bytesSent = SendMIDIData(nChn, isSmooth, out + sendPos, sendLen, plugin); - // If there's no error in the macro data (e.g. unrecognized internal MIDI macro), we have sendLen == bytesSent. - if(bytesSent > 0) - sendPos += bytesSent; - else - sendPos += sendLen; - } + out = out.first(outPos); } // Calculate smooth MIDI macro slide parameter for current tick. -float CSoundFile::CalculateSmoothParamChange(float currentValue, float param) const +float CSoundFile::CalculateSmoothParamChange(const PlayState &playState, float currentValue, float param) { - MPT_ASSERT(m_PlayState.TicksOnRow() > m_PlayState.m_nTickCount); - const uint32 ticksLeft = m_PlayState.TicksOnRow() - m_PlayState.m_nTickCount; + MPT_ASSERT(playState.TicksOnRow() > playState.m_nTickCount); + const uint32 ticksLeft = playState.TicksOnRow() - playState.m_nTickCount; if(ticksLeft > 1) { // Slide param - const float step = (param - currentValue) / (float)ticksLeft; + const float step = (param - currentValue) / static_cast(ticksLeft); return (currentValue + step); } else { @@ -5043,31 +5078,28 @@ float CSoundFile::CalculateSmoothParamChange(float currentValue, float param) co // Process exactly one MIDI message parsed by ProcessMIDIMacro. Returns bytes sent on success, 0 on (parse) failure. -uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, uint32 macroLen, PLUGINDEX plugin) +void CSoundFile::SendMIDIData(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, PLUGINDEX plugin) { - if(macroLen < 1) - { - return 0; - } + if(macro.size() < 1) + return; + + // Don't do anything that modifies state outside of the playState itself. + const bool localOnly = playState.m_midiMacroEvaluationResults.has_value(); if(macro[0] == 0xFA || macro[0] == 0xFC || macro[0] == 0xFF) { // Start Song, Stop Song, MIDI Reset - both interpreted internally and sent to plugins for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { - m_PlayState.Chn[chn].nCutOff = 0x7F; - m_PlayState.Chn[chn].nResonance = 0x00; + playState.Chn[chn].nCutOff = 0x7F; + playState.Chn[chn].nResonance = 0x00; } } - ModChannel &chn = m_PlayState.Chn[nChn]; - if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1)) + ModChannel &chn = playState.Chn[nChn]; + if(macro.size() == 4 && macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1)) { // Internal device. - if(macroLen < 4) - { - return 0; - } const bool isExtended = (macro[1] == 0xF1); const uint8 macroCode = macro[2]; const uint8 param = macro[3]; @@ -5076,36 +5108,26 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned { // F0.F0.00.xx: Set CutOff if(!isSmooth) - { chn.nCutOff = param; - } else - { - chn.nCutOff = mpt::saturate_round(CalculateSmoothParamChange(chn.nCutOff, param)); - } + else + chn.nCutOff = mpt::saturate_round(CalculateSmoothParamChange(playState, chn.nCutOff, param)); chn.nRestoreCutoffOnNewNote = 0; int cutoff = SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); - if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl && !localOnly) { // Cutoff doubles as modulator intensity for FM instruments m_opl->Volume(nChn, static_cast(cutoff / 4), true); } - - return 4; } else if(macroCode == 0x01 && !isExtended && param < 0x80) { // F0.F0.01.xx: Set Resonance if(!isSmooth) - { chn.nResonance = param; - } else - { - chn.nResonance = (uint8)CalculateSmoothParamChange((float)chn.nResonance, (float)param); - } + else + chn.nResonance = mpt::saturate_round(CalculateSmoothParamChange(playState, chn.nResonance, param)); chn.nRestoreResonanceOnNewNote = 0; SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); - - return 4; } else if(macroCode == 0x02 && !isExtended) { // F0.F0.02.xx: Set filter mode (high nibble determines filter mode) @@ -5114,54 +5136,45 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned chn.nFilterMode = static_cast(param >> 4); SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } - - return 4; #ifndef NO_PLUGINS } else if(macroCode == 0x03 && !isExtended) { // F0.F0.03.xx: Set plug dry/wet - const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); + PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80) { - const float newRatio = (0x7F - (param & 0x7F)) / 127.0f; - if(!isSmooth) - { - m_MixPlugins[plug - 1].fDryRatio = newRatio; - } else - { - m_MixPlugins[plug - 1].fDryRatio = CalculateSmoothParamChange(m_MixPlugins[plug - 1].fDryRatio, newRatio); - } + plug--; + const float newRatio = (127 - param) / 127.0f; + if(localOnly) + playState.m_midiMacroEvaluationResults->pluginDryWetRatio[plug] = newRatio; + else if(!isSmooth) + m_MixPlugins[plug].fDryRatio = newRatio; + else + m_MixPlugins[plug].fDryRatio = CalculateSmoothParamChange(playState, m_MixPlugins[plug].fDryRatio, newRatio); } - - return 4; } else if((macroCode & 0x80) || isExtended) { // F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx - const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); - const uint32 plugParam = isExtended ? (0x80 + macroCode) : (macroCode & 0x7F); - if(plug > 0 && plug <= MAX_MIXPLUGINS) + PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); + if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80) { - IMixPlugin *pPlugin = m_MixPlugins[plug - 1].pMixPlugin; - if(pPlugin && param < 0x80) + plug--; + IMixPlugin *pPlugin = m_MixPlugins[plug].pMixPlugin; + if(pPlugin) { - const float fParam = param / 127.0f; - if(!isSmooth) - { - pPlugin->SetParameter(plugParam, fParam); - } else - { - pPlugin->SetParameter(plugParam, CalculateSmoothParamChange(pPlugin->GetParameter(plugParam), fParam)); - } + const PlugParamIndex plugParam = isExtended ? (0x80 + macroCode) : (macroCode & 0x7F); + const PlugParamValue value = param / 127.0f; + if(localOnly) + playState.m_midiMacroEvaluationResults->pluginParameter[{plug, plugParam}] = value; + else if(!isSmooth) + pPlugin->SetParameter(plugParam, value); + else + pPlugin->SetParameter(plugParam, CalculateSmoothParamChange(playState, pPlugin->GetParameter(plugParam), value)); } } - - return 4; #endif // NO_PLUGINS } - - // If we reach this point, the internal macro was invalid. - - } else + } else if(!localOnly) { #ifndef NO_PLUGINS // Not an internal device. Pass on to appropriate plugin. @@ -5171,7 +5184,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned PLUGINDEX plug = 0; if(!chn.dwFlags[CHN_NOFX]) { - plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); + plug = (plugin != 0) ? plugin : GetBestPlugin(playState, nChn, PrioritiseChannel, EvenIfMuted); } if(plug > 0 && plug <= MAX_MIXPLUGINS) @@ -5181,12 +5194,12 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned { if(macro[0] == 0xF0) { - pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); + pPlugin->MidiSysexSend(mpt::byte_cast(macro)); } else { - uint32 len = std::min(static_cast(MIDIEvents::GetEventLength(macro[0])), macroLen); + size_t len = std::min(static_cast(MIDIEvents::GetEventLength(macro[0])), macro.size()); uint32 curData = 0; - memcpy(&curData, macro, len); + memcpy(&curData, macro.data(), len); pPlugin->MidiSend(curData); } } @@ -5195,11 +5208,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned #else MPT_UNREFERENCED_PARAMETER(plugin); #endif // NO_PLUGINS - - return macroLen; } - - return 0; } @@ -5348,12 +5357,43 @@ void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) c { chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.reset(CHN_LOOP); - chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. + chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - SmpLength(1)), 0); } } +void CSoundFile::DigiBoosterSampleReverse(ModChannel &chn, ModCommand::PARAM param) const +{ + if(chn.isFirstTick && chn.pModSample != nullptr && chn.pModSample->nLength > 0) + { + chn.dwFlags.set(CHN_PINGPONGFLAG); + chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. + chn.position.Set(chn.nLength - 1, 0); + chn.dwFlags.set(CHN_LOOP | CHN_PINGPONGLOOP, param > 0); + if(param > 0) + { + chn.nLoopStart = 0; + chn.nLoopEnd = chn.nLength; + // TODO: When the sample starts playing in forward direction again, the loop should be updated to the normal sample loop. + } + } +} + + +void CSoundFile::HandleDigiSamplePlayDirection(PlayState &state, CHANNELINDEX chn) const +{ + // Digi Booster mixes two channels into one Paula channel, and when a note is triggered on one of them it resets the reverse play flag on the other. + if(GetType() == MOD_TYPE_DIGI) + { + state.Chn[chn].dwFlags.reset(CHN_PINGPONGFLAG); + const CHANNELINDEX otherChn = chn ^ 1; + if(otherChn < GetNumChannels()) + state.Chn[otherChn].dwFlags.reset(CHN_PINGPONGFLAG); + } +} + + void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { // Retrig: bit 8 is set if it's the new XM retrig @@ -5480,7 +5520,9 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) uint32 note = chn.nNewNote; int32 oldPeriod = chn.nPeriod; // ST3 doesn't retrigger OPL notes - if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && (!chn.dwFlags[CHN_ADLIB] || GetType() != MOD_TYPE_S3M || m_playBehaviour[kOPLRealRetrig])) + // Test case: RetrigSlide.s3m + const bool oplRealRetrig = chn.dwFlags[CHN_ADLIB] && m_playBehaviour[kOPLRealRetrig]; + if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && (GetType() != MOD_TYPE_S3M || oplRealRetrig)) CheckNNA(nChn, 0, note, true); bool resetEnv = false; if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) @@ -5498,8 +5540,9 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) const auto oldPrevNoteOffset = chn.prevNoteOffset; chn.prevNoteOffset = 0; // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m) // IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase) - // Test case: retrig.it - NoteChange(chn, note, m_playBehaviour[kITRetrigger], resetEnv, false, nChn); + // Test cases: retrig.it, RetrigSlide.s3m + const bool itS3Mstyle = m_playBehaviour[kITRetrigger] || (GetType() == MOD_TYPE_S3M && chn.nLength && !oplRealRetrig); + NoteChange(chn, note, itS3Mstyle, resetEnv, false, nChn); if(!chn.rowCommand.instr) chn.prevNoteOffset = oldPrevNoteOffset; // XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present. @@ -5519,7 +5562,8 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) retrigCount = 0; // IT compatibility: see previous IT compatibility comment =) - if(m_playBehaviour[kITRetrigger]) chn.position.Set(0); + if(itS3Mstyle) + chn.position.Set(0); offset--; if(chn.pModSample != nullptr && offset >= 0 && offset <= static_cast(std::size(chn.pModSample->cues))) @@ -5757,7 +5801,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 | MOD_TYPE_MED)) ? TEMPO(1, 0) : TEMPO(32, 0); + const TEMPO minTempo = (GetType() & (MOD_TYPE_MDL | MOD_TYPE_MED | MOD_TYPE_MOD)) ? TEMPO(1, 0) : TEMPO(32, 0); if(setFromUI) { @@ -6093,7 +6137,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio } -PLUGINDEX CSoundFile::GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const +PLUGINDEX CSoundFile::GetBestPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const { if (nChn >= MAX_CHANNELS) //Check valid channel number { @@ -6105,23 +6149,23 @@ PLUGINDEX CSoundFile::GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, switch (priority) { case ChannelOnly: - plugin = GetChannelPlugin(nChn, respectMutes); + plugin = GetChannelPlugin(playState, nChn, respectMutes); break; case InstrumentOnly: - plugin = GetActiveInstrumentPlugin(nChn, respectMutes); + plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); break; case PrioritiseInstrument: - plugin = GetActiveInstrumentPlugin(nChn, respectMutes); + plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); if(!plugin || plugin > MAX_MIXPLUGINS) { - plugin = GetChannelPlugin(nChn, respectMutes); + plugin = GetChannelPlugin(playState, nChn, respectMutes); } break; case PrioritiseChannel: - plugin = GetChannelPlugin(nChn, respectMutes); + plugin = GetChannelPlugin(playState, nChn, respectMutes); if(!plugin || plugin > MAX_MIXPLUGINS) { - plugin = GetActiveInstrumentPlugin(nChn, respectMutes); + plugin = GetActiveInstrumentPlugin(playState.Chn[nChn], respectMutes); } break; } @@ -6130,9 +6174,9 @@ PLUGINDEX CSoundFile::GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, } -PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const +PLUGINDEX CSoundFile::GetChannelPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginMutePriority respectMutes) const { - const ModChannel &channel = m_PlayState.Chn[nChn]; + const ModChannel &channel = playState.Chn[nChn]; PLUGINDEX plugin; if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) || channel.dwFlags[CHN_NOFX]) @@ -6142,8 +6186,7 @@ PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority res { // If it looks like this is an NNA channel, we need to find the master channel. // This ensures we pick up the right ChnSettings. - // NB: nMasterChn == 0 means no master channel, so we need to -1 to get correct index. - if (nChn >= m_nChannels && channel.nMasterChn > 0) + if(channel.nMasterChn > 0) { nChn = channel.nMasterChn - 1; } @@ -6160,20 +6203,21 @@ PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority res } -PLUGINDEX CSoundFile::GetActiveInstrumentPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const +PLUGINDEX CSoundFile::GetActiveInstrumentPlugin(const ModChannel &chn, PluginMutePriority respectMutes) { // Unlike channel settings, pModInstrument is copied from the original chan to the NNA chan, // so we don't need to worry about finding the master chan. PLUGINDEX plug = 0; - if(m_PlayState.Chn[nChn].pModInstrument != nullptr) + if(chn.pModInstrument != nullptr) { - if(respectMutes == RespectMutes && m_PlayState.Chn[nChn].pModSample && m_PlayState.Chn[nChn].pModSample->uFlags[CHN_MUTE]) + // TODO this looks fishy. Shouldn't it check the mute status of the instrument itself?! + if(respectMutes == RespectMutes && chn.pModSample && chn.pModSample->uFlags[CHN_MUTE]) { plug = 0; } else { - plug = m_PlayState.Chn[nChn].pModInstrument->nMixPlug; + plug = chn.pModInstrument->nMixPlug; } } return plug; @@ -6183,10 +6227,10 @@ PLUGINDEX CSoundFile::GetActiveInstrumentPlugin(CHANNELINDEX nChn, PluginMutePri // Retrieve the plugin that is associated with the channel's current instrument. // No plugin is returned if the channel is muted or if the instrument doesn't have a MIDI channel set up, // As this is meant to be used with instrument plugins. -IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const +IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(const ModChannel &chn) const { #ifndef NO_PLUGINS - if(m_PlayState.Chn[chn].dwFlags[CHN_MUTE | CHN_SYNCMUTE]) + if(chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) { // Don't process portamento on muted channels. Note that this might have a side-effect // on other channels which trigger notes on the same MIDI channel of the same plugin, @@ -6194,9 +6238,9 @@ IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const return nullptr; } - if(m_PlayState.Chn[chn].HasMIDIOutput()) + if(chn.HasMIDIOutput()) { - const ModInstrument *pIns = m_PlayState.Chn[chn].pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; // Instrument sends to a MIDI channel if(pIns->nMixPlug != 0 && pIns->nMixPlug <= MAX_MIXPLUGINS) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index 3b6b8b60a..efe00452d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -70,6 +70,13 @@ mpt::ustring FileHistory::AsISO8601() const } +CSoundFile::PlayState::PlayState() +{ + std::fill(std::begin(Chn), std::end(Chn), ModChannel{}); + m_midiMacroScratchSpace.reserve(kMacroLength); // Note: If macros ever become variable-length, the scratch space needs to be at least one byte longer than the longest macro in the file for end-of-SysEx insertion to stay allocation-free in the mixer! +} + + ////////////////////////////////////////////////////////// // CSoundFile @@ -367,108 +374,107 @@ CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span containerItems; + MODCONTAINERTYPE packedContainerType = MOD_CONTAINERTYPE_NONE; + if(!(loadFlags & skipContainer)) { - -#ifndef NO_ARCHIVE_SUPPORT - CUnarchiver unarchiver(file); - if(!(loadFlags & skipContainer)) - { - if (unarchiver.ExtractBestFile(GetSupportedExtensions(true))) - { - file = unarchiver.GetOutputFile(); - } - } -#endif - - std::vector containerItems; - MODCONTAINERTYPE packedContainerType = MOD_CONTAINERTYPE_NONE; - if(!(loadFlags & skipContainer)) - { - ContainerLoadingFlags containerLoadFlags = (loadFlags == onlyVerifyHeader) ? ContainerOnlyVerifyHeader : ContainerUnwrapData; + ContainerLoadingFlags containerLoadFlags = (loadFlags == onlyVerifyHeader) ? ContainerOnlyVerifyHeader : ContainerUnwrapData; #if !defined(MPT_WITH_ANCIENT) - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_XPK; - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_PP20; - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_MMCMP; + if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_XPK; + if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_PP20; + if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_MMCMP; #endif // !MPT_WITH_ANCIENT - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_UMX; - if(packedContainerType != MOD_CONTAINERTYPE_NONE) + if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_UMX; + if(packedContainerType != MOD_CONTAINERTYPE_NONE) + { + if(loadFlags == onlyVerifyHeader) { - if(loadFlags == onlyVerifyHeader) - { - return true; - } - if(!containerItems.empty()) - { - // cppcheck false-positive - // cppcheck-suppress containerOutOfBounds - file = containerItems[0].file; - } + return true; + } + if(!containerItems.empty()) + { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + file = containerItems[0].file; } } - - if(loadFlags & skipModules) - { - return false; - } - - // Try all module format loaders - bool loaderSuccess = false; - for(const auto &format : ModuleFormatLoaders) - { - loaderSuccess = (this->*(format.loader))(file, loadFlags); - if(loaderSuccess) - break; - } - - if(!loaderSuccess) - { - m_nType = MOD_TYPE_NONE; - m_ContainerType = MOD_CONTAINERTYPE_NONE; - } - if(loadFlags == onlyVerifyHeader) - { - return loaderSuccess; - } - - if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE) - { - m_ContainerType = packedContainerType; - } - -#ifndef NO_ARCHIVE_SUPPORT - // Read archive comment if there is no song comment - if(m_songMessage.empty()) - { - m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); - } -#endif - - m_visitedRows.Initialize(true); - } + + if(loadFlags & skipModules) + { + return false; + } + + // Try all module format loaders + bool loaderSuccess = false; + for(const auto &format : ModuleFormatLoaders) + { + loaderSuccess = (this->*(format.loader))(file, loadFlags); + if(loaderSuccess) + break; + } + + if(!loaderSuccess) + { + m_nType = MOD_TYPE_NONE; + m_ContainerType = MOD_CONTAINERTYPE_NONE; + } + if(loadFlags == onlyVerifyHeader) + { + return loaderSuccess; + } + + if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE) + { + m_ContainerType = packedContainerType; + } + + m_visitedRows.Initialize(true); } else { // New song diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h index e3fdfe88b..01aacbc68 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h @@ -232,9 +232,7 @@ class CTuningCollection; using CTuningCollection = Tuning::CTuningCollection; struct CModSpecifications; class OPL; -#ifdef MODPLUG_TRACKER class CModDoc; -#endif // MODPLUG_TRACKER ///////////////////////////////////////////////////////////////////////// @@ -585,11 +583,17 @@ public: CHANNELINDEX ChnMix[MAX_CHANNELS]; // Index of channels in Chn to be actually mixed ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! - public: - PlayState() + struct MIDIMacroEvaluationResults { - std::fill(std::begin(Chn), std::end(Chn), ModChannel()); - } + std::map pluginDryWetRatio; + std::map, PlugParamValue> pluginParameter; + }; + + std::vector m_midiMacroScratchSpace; + std::optional m_midiMacroEvaluationResults; + + public: + PlayState(); void ResetGlobalVolumeRamping() { @@ -720,12 +724,13 @@ public: #ifdef MODPLUG_TRACKER // Get parent CModDoc. Can be nullptr if previewing from tree view, and is always nullptr if we're not actually compiling OpenMPT. CModDoc *GetpModDoc() const noexcept { return m_pModDoc; } +#endif // MODPLUG_TRACKER bool Create(FileReader file, ModLoadingFlags loadFlags = loadCompleteModule, CModDoc *pModDoc = nullptr); -#else - bool Create(FileReader file, ModLoadingFlags loadFlags); -#endif // MODPLUG_TRACKER +private: + bool CreateInternal(FileReader file, ModLoadingFlags loadFlags); +public: bool Destroy(); Enum GetType() const noexcept { return m_nType; } @@ -1096,6 +1101,8 @@ protected: void ProcessSampleOffset(ModChannel &chn, CHANNELINDEX nChn, const PlayState &playState) const; void SampleOffset(ModChannel &chn, SmpLength param) const; void ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const; + void DigiBoosterSampleReverse(ModChannel &chn, ModCommand::PARAM param) const; + void HandleDigiSamplePlayDirection(PlayState &state, CHANNELINDEX chn) const; void NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample); void PatternLoop(PlayState &state, ModChannel &chn, ModCommand::PARAM param) const; bool HandleNextRow(PlayState &state, const ModSequence &order, bool honorPatternLoop) const; @@ -1108,9 +1115,10 @@ protected: void GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSlide); void ProcessMacroOnChannel(CHANNELINDEX nChn); - void ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *macro, uint8 param = 0, PLUGINDEX plugin = 0); - float CalculateSmoothParamChange(float currentValue, float param) const; - uint32 SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, uint32 macroLen, PLUGINDEX plugin); + void ProcessMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const MIDIMacroConfigData::Macro ¯o, uint8 param = 0, PLUGINDEX plugin = 0); + void ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, mpt::span &out, uint8 param = 0, PLUGINDEX plugin = 0) const; + static float CalculateSmoothParamChange(const PlayState &playState, float currentValue, float param); + void SendMIDIData(PlayState &playState, CHANNELINDEX nChn, bool isSmooth, const mpt::span macro, PLUGINDEX plugin); void SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume); int SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier = 256) const; @@ -1236,12 +1244,12 @@ public: void ProcessStereoSeparation(long countChunk); private: - PLUGINDEX GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const; - PLUGINDEX GetActiveInstrumentPlugin(CHANNELINDEX, PluginMutePriority respectMutes) const; - IMixPlugin *GetChannelInstrumentPlugin(CHANNELINDEX chn) const; + PLUGINDEX GetChannelPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginMutePriority respectMutes) const; + static PLUGINDEX GetActiveInstrumentPlugin(const ModChannel &chn, PluginMutePriority respectMutes); + IMixPlugin *GetChannelInstrumentPlugin(const ModChannel &chn) const; public: - PLUGINDEX GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const; + PLUGINDEX GetBestPlugin(const PlayState &playState, CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const; }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 6d2b7a15f..6bf693d62 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -1704,7 +1704,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOT // Process MIDI vibrato for plugins: #ifndef NO_PLUGINS - IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); + IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { // If the Pitch Wheel Depth is configured correctly (so it's the same as the plugin's PWD), @@ -1727,7 +1727,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOT { // Stop MIDI vibrato for plugins: #ifndef NO_PLUGINS - IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); + IMixPlugin *plugin = GetChannelInstrumentPlugin(m_PlayState.Chn[nChn]); if(plugin != nullptr) { plugin->MidiVibrato(0, 0, nChn); @@ -2527,15 +2527,15 @@ void CSoundFile::ProcessMacroOnChannel(CHANNELINDEX nChn) if(nChn < GetNumChannels()) { // TODO evaluate per-plugin macros here - //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN]); - //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_VOLUME]); + //ProcessMIDIMacro(m_PlayState, nChn, false, m_MidiCfg.Global[MIDIOUT_PAN]); + //ProcessMIDIMacro(m_PlayState, nChn, false, m_MidiCfg.Global[MIDIOUT_VOLUME]); if((chn.rowCommand.command == CMD_MIDI && m_SongFlags[SONG_FIRSTTICK]) || chn.rowCommand.command == CMD_SMOOTHMIDI) { if(chn.rowCommand.param < 0x80) - ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[chn.nActiveMacro], chn.rowCommand.param); + ProcessMIDIMacro(m_PlayState, nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.SFx[chn.nActiveMacro], chn.rowCommand.param); else - ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(chn.rowCommand.param & 0x7F)], 0); + ProcessMIDIMacro(m_PlayState, nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.Zxx[chn.rowCommand.param & 0x7F], chn.rowCommand.param); } } } @@ -2561,7 +2561,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) } // Check instrument plugins - const PLUGINDEX nPlugin = GetBestPlugin(nChn, PrioritiseInstrument, RespectMutes); + const PLUGINDEX nPlugin = GetBestPlugin(m_PlayState, nChn, PrioritiseInstrument, RespectMutes); IMixPlugin *pPlugin = nullptr; if(nPlugin > 0 && nPlugin <= MAX_MIXPLUGINS) { @@ -2622,7 +2622,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) if(ModCommand::IsNote(note)) realNote = pIns->NoteMap[note - NOTE_MIN]; // Experimental VST panning - //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN], 0, nPlugin); + //ProcessMIDIMacro(nChn, false, m_MidiCfg.Global[MIDIOUT_PAN], 0, nPlugin); SendMIDINote(nChn, realNote, static_cast(velocity)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp index d87a75704..60f881feb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp @@ -67,7 +67,7 @@ constexpr CModSpecifications mptm_ = true, // Has artist name true, // Has default resampling true, // Fixed point tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#+*????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#+*?????????", // Supported Effects " vpcdabuh??gfe?o", // Supported Volume Column commands }; @@ -117,7 +117,7 @@ constexpr CModSpecifications mod_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCD?FF?E?????????????????????????", // Supported Effects + " 0123456789ABCD?FF?E??????????????????????????", // Supported Effects " ???????????????", // Supported Volume Column commands }; @@ -165,7 +165,7 @@ constexpr CModSpecifications xm_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK??XPL??????W???????", // Supported Effects + " 0123456789ABCDRFFTE???GHK??XPL??????W????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; @@ -213,7 +213,7 @@ constexpr CModSpecifications xmEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#??W???????", // Supported Effects + " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#??W????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; @@ -260,7 +260,7 @@ constexpr CModSpecifications s3m_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?U?????????? ???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?U?????????? ????????", // Supported Effects " vp?????????????", // Supported Volume Column commands }; @@ -308,7 +308,7 @@ constexpr CModSpecifications s3mEx_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ????????", // Supported Effects " vp?????????????", // Supported Volume Column commands }; @@ -355,7 +355,7 @@ constexpr CModSpecifications it_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z????? ????????", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; @@ -402,7 +402,7 @@ constexpr CModSpecifications itEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#?? ???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#?? ????????", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp index dbe485195..c99215c97 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp @@ -30,7 +30,7 @@ const EffectType effectTypes[] = EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, - EFFECT_TYPE_NORMAL, + EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; static_assert(std::size(effectTypes) == MAX_EFFECTS); @@ -84,7 +84,7 @@ void ModCommand::ExtendedMODtoS3MEffect() case 0x70: param = (param & 0x03) | 0x40; break; case 0x90: command = CMD_RETRIG; param = (param & 0x0F); break; case 0xA0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command = CMD_NONE; break; - case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command = CMD_NONE; break; + case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = 0xF0 | std::min(param, PARAM(0x0E)); } else command = CMD_NONE; break; case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } break; // this does different things in IT and ST3 case 0xD0: if(param == 0xD0) { command = CMD_NONE; } break; // ditto // rest are the same or handled elsewhere @@ -142,6 +142,11 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd // Apart from these special fixups, do a regular conversion from MOD. fromType = MOD_TYPE_MOD; } + if(command == CMD_DIGIREVERSESAMPLE && toType != MOD_TYPE_DIGI) + { + command = CMD_S3MCMDEX; + param = 0x9F; + } // helper variables const bool oldTypeIsMOD = (fromType == MOD_TYPE_MOD), oldTypeIsXM = (fromType == MOD_TYPE_XM), @@ -870,7 +875,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd } -bool ModCommand::IsContinousCommand(const CSoundFile& sndFile) const +bool ModCommand::IsContinousCommand(const CSoundFile &sndFile) const { switch(command) { @@ -1036,6 +1041,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd) CMD_VOLUMESLIDE, CMD_VIBRATOVOL, CMD_VOLUME, + CMD_DIGIREVERSESAMPLE, CMD_REVERSEOFFSET, CMD_OFFSETPERCENTAGE, CMD_OFFSET, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h index 7dfe24e8c..bf9049a9b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h @@ -107,6 +107,7 @@ enum EffectCommand : uint8 CMD_REVERSEOFFSET = 42, // PTM Nxx Revert sample + offset CMD_DBMECHO = 43, // DBM enable/disable echo CMD_OFFSETPERCENTAGE = 44, // PLM Percentage Offset + CMD_DIGIREVERSESAMPLE = 45, // DIGI reverse sample MAX_EFFECTS }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp index 802dddcef..ace4406d7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp @@ -28,10 +28,7 @@ void CPatternContainer::ClearPatterns() void CPatternContainer::DestroyPatterns() { - for(PATTERNINDEX i = 0; i < m_Patterns.size(); i++) - { - Remove(i); - } + m_Patterns.clear(); } @@ -67,7 +64,7 @@ PATTERNINDEX CPatternContainer::InsertAny(const ROWINDEX rows, bool respectQtyLi bool CPatternContainer::Insert(const PATTERNINDEX index, const ROWINDEX rows) { - if(rows > MAX_PATTERN_ROWS || rows == 0) + if(rows > MAX_PATTERN_ROWS || rows == 0 || index >= PATTERNINDEX_INVALID) return false; if(IsValidPat(index)) return false; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp index f44f10563..fd28e0830 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp @@ -775,13 +775,19 @@ void IMidiPlugin::ApplyPitchWheelDepth(int32 &value, int8 pwd) // Get the MIDI channel currently associated with a given tracker channel +uint8 IMidiPlugin::GetMidiChannel(const ModChannel &chn, CHANNELINDEX trackChannel) const +{ + if(auto ins = chn.pModInstrument; ins != nullptr) + return ins->GetMIDIChannel(chn, trackChannel); + else + return 0; +} + + uint8 IMidiPlugin::GetMidiChannel(CHANNELINDEX trackChannel) const { - 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); + if(trackChannel < std::size(m_SndFile.m_PlayState.Chn)) + return GetMidiChannel(m_SndFile.m_PlayState.Chn[trackChannel], trackChannel); else return 0; } @@ -884,7 +890,7 @@ void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vo uint8 high = static_cast(midiBank >> 7); uint8 low = static_cast(midiBank & 0x7F); - //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.szMidiGlb[MIDIOUT_BANKSEL], 0, m_nSlot + 1); + //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_BANKSEL], 0, m_nSlot + 1); MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Coarse, midiCh, high)); MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Fine, midiCh, low)); @@ -897,7 +903,7 @@ void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vo if(progChanged || (midiProg < 0x80 && bankChanged)) { channel.currentProgram = midiProg; - //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM], 0, m_nSlot + 1); + //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_PROGRAM], 0, m_nSlot + 1); MidiSend(MIDIEvents::ProgramChange(midiCh, midiProg)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h index 68c633aa8..8ee0aabe8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h @@ -25,6 +25,7 @@ OPENMPT_NAMESPACE_BEGIN struct VSTPluginLib; struct SNDMIXPLUGIN; struct ModInstrument; +struct ModChannel; class CSoundFile; class CModDoc; class CAbstractVstEditor; @@ -275,9 +276,11 @@ public: bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override; // Get the MIDI channel currently associated with a given tracker channel - virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const; + virtual uint8 GetMidiChannel(const ModChannel &chn, CHANNELINDEX trackChannel) const; protected: + uint8 GetMidiChannel(CHANNELINDEX trackChannel) const; + // Plugin wants to send MIDI to OpenMPT virtual void ReceiveMidi(uint32 midiCode); virtual void ReceiveSysex(mpt::const_byte_span sysex); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h index 61b51bd14..18ca43212 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h @@ -22,8 +22,8 @@ OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////// // Mix Plugins -typedef int32 PlugParamIndex; -typedef float PlugParamValue; +using PlugParamIndex = int32; +using PlugParamValue = float; struct SNDMIXPLUGINSTATE; struct SNDMIXPLUGIN; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp index 900cdd786..df1cfddc1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp @@ -625,6 +625,7 @@ float I3DL2Reverb::CalcDecayCoeffs(int32 index) c2 = (c23 - c22) / (c21 + c21); if(std::abs(c2) > 1.0f) c2 = (-c22 - c23) / (c21 + c21); + c2 = mpt::sanitize_nan(c2); } m_delayCoeffs[index][0] = c1; m_delayCoeffs[index][1] = c2; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp index 8f494a5f8..155620831 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp @@ -24,6 +24,10 @@ OPENMPT_NAMESPACE_BEGIN namespace Tuning { +static RATIOTYPE SanitizeGroupRatio(RATIOTYPE ratio) +{ + return std::clamp(std::abs(ratio), 1e-15f, 1e+07f); +} namespace CTuningS11n { @@ -257,7 +261,12 @@ RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE note) const { return s_DefaultFallbackRatio; } - return m_RatioTable[note - m_NoteMin]; + const auto ratio = m_RatioTable[note - m_NoteMin]; + if(ratio <= 1e-15f) + { + return s_DefaultFallbackRatio; + } + return ratio; } @@ -480,6 +489,17 @@ SerializationResult CTuning::InitDeserialize(std::istream &iStrm, mpt::Charset d UNOTEINDEXTYPE ratiotableSize = 0; ssb.ReadItem(ratiotableSize, "RTI4"); + m_GroupRatio = SanitizeGroupRatio(m_GroupRatio); + if(!std::isfinite(m_GroupRatio)) + { + return SerializationResult::Failure; + } + for(auto ratio : m_RatioTable) + { + if(!std::isfinite(ratio)) + return SerializationResult::Failure; + } + // If reader status is ok and m_NoteMin is somewhat reasonable, process data. if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_NoteMin >= -300 && m_NoteMin <= 300)) { @@ -683,6 +703,11 @@ SerializationResult CTuning::InitDeserializeOLD(std::istream &inStrm, mpt::Chars return SerializationResult::Failure; } } + for(auto ratio : m_RatioTable) + { + if(!std::isfinite(ratio)) + return SerializationResult::Failure; + } //Fineratios if(version <= 2) @@ -698,6 +723,11 @@ SerializationResult CTuning::InitDeserializeOLD(std::istream &inStrm, mpt::Chars return SerializationResult::Failure; } } + for(auto ratio : m_RatioTableFine) + { + if(!std::isfinite(ratio)) + return SerializationResult::Failure; + } m_FineStepCount = mpt::saturate_cast(m_RatioTableFine.size()); // m_NoteMin @@ -721,8 +751,8 @@ SerializationResult CTuning::InitDeserializeOLD(std::istream &inStrm, mpt::Chars //m_GroupRatio IEEE754binary32LE groupratio = IEEE754binary32LE(0.0f); mpt::IO::Read(inStrm, groupratio); - m_GroupRatio = groupratio; - if(m_GroupRatio < 0) + m_GroupRatio = SanitizeGroupRatio(groupratio); + if(!std::isfinite(m_GroupRatio)) { return SerializationResult::Failure; } diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt b/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt index 6b3eccca6..1adcc102b 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/bit.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/bit.hpp index 49bb15075..81418fc1a 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/bit.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/bit.hpp @@ -30,9 +30,9 @@ inline namespace MPT_INLINE_NS { -#if MPT_CXX_AT_LEAST(20) +#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(14, 0, 0) using std::bit_cast; -#else +#else // !C++20 // C++2a compatible bit_cast. // Not implementing constexpr because this is not easily possible pre C++20. template @@ -41,7 +41,7 @@ MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::i std::memcpy(&dst, &src, sizeof(Tdst)); return dst; } -#endif +#endif // C++20 @@ -79,11 +79,18 @@ constexpr bool endian_is_weird() noexcept { #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define MPT_PLATFORM_LITTLE_ENDIAN #endif +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) +#if __ORDER_BIG_ENDIAN__ != __ORDER_LITTLE_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define MPT_PLATFORM_BIG_ENDIAN +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define MPT_PLATFORM_LITTLE_ENDIAN +#endif +#endif #endif // fallback: #if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) -// taken from boost/detail/endian.hpp #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) @@ -92,19 +99,13 @@ constexpr bool endian_is_weird() noexcept { || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) #define MPT_PLATFORM_LITTLE_ENDIAN -#elif defined(__sparc) || defined(__sparc__) \ - || defined(_POWER) || defined(__powerpc__) \ - || defined(__ppc__) || defined(__hpux) || defined(__hppa) \ - || defined(_MIPSEB) || defined(_POWER) \ +#elif defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) \ || defined(__s390__) #define MPT_PLATFORM_BIG_ENDIAN -#elif defined(__i386__) || defined(__alpha__) \ - || defined(__ia64) || defined(__ia64__) \ - || defined(_M_IX86) || defined(_M_IA64) \ - || defined(_M_ALPHA) || defined(__amd64) \ - || defined(__amd64__) || defined(_M_AMD64) \ - || defined(__x86_64) || defined(__x86_64__) \ - || defined(_M_X64) || defined(__bfin__) +#elif defined(__i386__) || defined(_M_IX86) \ + || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__bfin__) #define MPT_PLATFORM_LITTLE_ENDIAN #endif #endif @@ -182,7 +183,7 @@ MPT_FORCEINLINE bool endian_is_weird() noexcept { -#if MPT_CXX_AT_LEAST(20) && !MPT_COMPILER_MSVC +#if MPT_CXX_AT_LEAST(20) && !MPT_COMPILER_MSVC && !MPT_CLANG_BEFORE(12, 0, 0) // Disabled for VS2022 for now because of // diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect.hpp index 6e06aff9c..c7b1c15c1 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect.hpp @@ -6,10 +6,10 @@ #include "mpt/base/detect_compiler.hpp" +#include "mpt/base/detect_libc.hpp" +#include "mpt/base/detect_libcxx.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" -#include "mpt/base/detect_libcxx.hpp" -#include "mpt/base/detect_libc.hpp" diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp index 5349cb6e1..78b2c88b4 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp @@ -146,7 +146,9 @@ #if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG #if (__cplusplus >= 202002) -#define MPT_CXX 20 +// Support for C++20 is lacking across all compilers. +// Only assume C++17 for non-MSVC, even when in C++20 mode. +#define MPT_CXX 17 #elif (__cplusplus >= 201703) #define MPT_CXX 17 #else diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libc.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libc.hpp index 462c4096f..4cc811e77 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libc.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libc.hpp @@ -7,7 +7,6 @@ #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" -#include "mpt/base/detect_quirks.hpp" #include @@ -18,7 +17,7 @@ #define MPT_LIBC_GENERIC 1 #elif MPT_COMPILER_GCC && (defined(__MINGW32__) || defined(__MINGW64__)) #define MPT_LIBC_MS 1 -#elif defined(__GNU_LIBRARY__) +#elif defined(__GLIBC__) #define MPT_LIBC_GLIBC 1 #elif MPT_COMPILER_MSVC #define MPT_LIBC_MS 1 diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libcxx.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libcxx.hpp index d4524de99..7f21564f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libcxx.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_libcxx.hpp @@ -7,7 +7,6 @@ #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" -#include "mpt/base/detect_quirks.hpp" #if MPT_CXX_AT_LEAST(20) #include diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/source_location.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/source_location.hpp index ba70e840e..90be04bc7 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/source_location.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/source_location.hpp @@ -9,7 +9,7 @@ #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" -#if MPT_CXX_AT_LEAST(20) +#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG #include #endif // C++20 @@ -19,7 +19,7 @@ namespace mpt { inline namespace MPT_INLINE_NS { -#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) +#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG using std::source_location; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/utility.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/utility.hpp index 733a54a85..7087a6a25 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/utility.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/utility.hpp @@ -23,7 +23,7 @@ namespace mpt { inline namespace MPT_INLINE_NS { -#if MPT_CXX_AT_LEAST(20) +#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0) using std::in_range; @@ -78,7 +78,7 @@ inline void reset(T & x) { -#if MPT_CXX_AT_LEAST(20) +#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0) using std::cmp_equal; using std::cmp_greater; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/check/libc.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/check/libc.hpp index d70e22736..89a978348 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/check/libc.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/check/libc.hpp @@ -30,7 +30,7 @@ MPT_WARNING("C stdlib does not provide math constants. Please #define _USE_MATH_ #endif #endif -#if !MPT_LIBC_MS +#if MPT_LIBC_GLIBC #if !defined(_FILE_OFFSET_BITS) #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_FILE_OFFSET_BITS MPT_WARNING("C stdlib may not provide 64bit std::FILE access. Please #define _FILE_OFFSET_BITS=64.") diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_floatingpoint.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_floatingpoint.hpp index bbd9253a9..8cd612589 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_floatingpoint.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_floatingpoint.hpp @@ -8,22 +8,22 @@ #include "mpt/base/detect.hpp" #if MPT_MSVC_AT_LEAST(2019, 4) || MPT_GCC_AT_LEAST(11, 1, 0) -#define MPT_FORMAT_CXX17_FLOAT 1 +#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 1 #else -#define MPT_FORMAT_CXX17_FLOAT 0 +#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 0 #endif -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include "mpt/base/algorithm.hpp" #endif #include "mpt/base/namespace.hpp" #include "mpt/format/helpers.hpp" #include "mpt/string_transcode/transcode.hpp" -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #endif -#if !MPT_FORMAT_CXX17_FLOAT +#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #include #include @@ -31,7 +31,7 @@ #include #endif #include -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 #include #endif #include @@ -42,7 +42,7 @@ namespace mpt { inline namespace MPT_INLINE_NS { -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 template ::value, bool> = true> inline Tstring to_chars_string(const T & x) { std::string str(1, '\0'); @@ -66,7 +66,7 @@ inline Tstring format_value_default(const T & x) { #endif -#if !MPT_FORMAT_CXX17_FLOAT +#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 template ::value, bool> = true> inline Tstring to_stream_string(const T & x) { using stream_char_type = typename mpt::select_format_char_type::type; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_integer.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_integer.hpp index fd50e83e3..8a3d0191d 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_integer.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/default_integer.hpp @@ -7,31 +7,31 @@ #include "mpt/base/detect.hpp" #if 1 -#define MPT_FORMAT_CXX17_INT 1 +#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 1 #else -#define MPT_FORMAT_CXX17_INT 0 +#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 0 #endif -#if MPT_FORMAT_CXX17_INT +#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include "mpt/base/algorithm.hpp" -#endif // MPT_FORMAT_CXX17_INT +#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include "mpt/base/namespace.hpp" #include "mpt/base/utility.hpp" #include "mpt/format/helpers.hpp" #include "mpt/string_transcode/transcode.hpp" -#if MPT_FORMAT_CXX17_INT +#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include -#endif // MPT_FORMAT_CXX17_INT -#if !MPT_FORMAT_CXX17_INT +#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 +#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include #include #include -#endif // !MPT_FORMAT_CXX17_INT +#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include -#if MPT_FORMAT_CXX17_INT +#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include -#endif // MPT_FORMAT_CXX17_INT +#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 #include @@ -40,7 +40,7 @@ namespace mpt { inline namespace MPT_INLINE_NS { -#if MPT_FORMAT_CXX17_INT +#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> inline Tstring to_chars_string(const T & x) { @@ -73,10 +73,10 @@ inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_chars_string::type>(x)); } -#endif // MPT_FORMAT_CXX17_INT +#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 -#if !MPT_FORMAT_CXX17_INT +#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> inline Tstring to_stream_string(const T & x) { @@ -98,7 +98,7 @@ inline Tstring format_value_default(const T & x) { return mpt::transcode(mpt::to_stream_string::type>(x)); } -#endif // !MPT_FORMAT_CXX17_INT +#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 template ::value, bool> = true> diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_floatingpoint.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_floatingpoint.hpp index 72cb73be7..3aa7ca4eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_floatingpoint.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_floatingpoint.hpp @@ -5,32 +5,41 @@ -#if MPT_FORMAT_CXX17_FLOAT +#include "mpt/base/detect.hpp" + +#if MPT_MSVC_AT_LEAST(2019, 4) || MPT_GCC_AT_LEAST(11, 1, 0) +#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 1 +#else +#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 0 +#endif + +#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include "mpt/base/algorithm.hpp" #endif #include "mpt/base/namespace.hpp" #include "mpt/format/default_floatingpoint.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_spec.hpp" +#include "mpt/string/types.hpp" #include "mpt/string_transcode/transcode.hpp" -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif -#if !MPT_FORMAT_CXX17_FLOAT +#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #include #endif -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif -#if !MPT_FORMAT_CXX17_FLOAT +#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #include #include #endif #include -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 #include #endif #include @@ -42,7 +51,7 @@ inline namespace MPT_INLINE_NS { -#if MPT_FORMAT_CXX17_FLOAT +#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 template @@ -128,7 +137,7 @@ inline Tstring format_simple(const T & x, const format_simple_spec & f) { } -#else // !MPT_FORMAT_CXX17_FLOAT +#else // !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 template @@ -229,7 +238,7 @@ inline Tstring format_simple(const T & x, const format_simple_spec & format) { -#endif // MPT_FORMAT_CXX17_FLOAT +#endif // MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_integer.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_integer.hpp index 53ca96cdf..d7997e7ec 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_integer.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/format/simple_integer.hpp @@ -9,6 +9,7 @@ #include "mpt/base/namespace.hpp" #include "mpt/format/helpers.hpp" #include "mpt/format/simple_spec.hpp" +#include "mpt/string/types.hpp" #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/string/utility.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/string/utility.hpp index e3463163c..e2238a09f 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/string/utility.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/string/utility.hpp @@ -5,6 +5,7 @@ +#include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include "mpt/detect/mfc.hpp" diff --git a/Frameworks/OpenMPT/OpenMPT/src/openmpt/soundbase/SampleFormat.hpp b/Frameworks/OpenMPT/OpenMPT/src/openmpt/soundbase/SampleFormat.hpp index 94ee89829..3e8738ae4 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/openmpt/soundbase/SampleFormat.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/openmpt/soundbase/SampleFormat.hpp @@ -47,14 +47,137 @@ private: static MPT_CONSTEXPRINLINE SampleFormat::Enum Sanitize(T x) noexcept { using uT = typename std::make_unsigned::type; - uT ux = static_cast(x); - if(ux == static_cast(-8)) + uT val = static_cast(x); + if(!val) { - ux = 8 + 1; + return SampleFormat::Enum::Invalid; + } + if(val == static_cast(-8)) + { + val = 8 + 1; } // float|64|32|16|8|?|?|unsigned - ux &= 0b11111001; - return static_cast(ux); + val &= 0b1'1111'00'1; + auto is_float = [](uT val) -> bool + { + return (val & 0b1'0000'00'0) ? true : false; + }; + auto is_unsigned = [](uT val) -> bool + { + return (val & 0b0'0000'00'1) ? true : false; + }; + auto has_size = [](uT val) -> bool + { + return (val & 0b0'1111'00'0) ? true : false; + }; + auto unique_size = [](uT val) -> bool + { + val &= 0b0'1111'00'0; + if(val == 0b0'0001'00'0) + { + return true; + } else if(val == 0b0'0010'00'0) + { + return true; + } else if(val == 0b0'0011'00'0) + { + return true; + } else if(val == 0b0'0100'00'0) + { + return true; + } else if(val == 0b0'1000'00'0) + { + return true; + } else + { + return false; + } + }; + auto get_size = [](uT val) -> std::size_t + { + val &= 0b0'1111'00'0; + if(val == 0b0'0001'00'0) + { + return 1; + } else if(val == 0b0'0010'00'0) + { + return 2; + } else if(val == 0b0'0011'00'0) + { + return 3; + } else if(val == 0b0'0100'00'0) + { + return 4; + } else if(val == 0b0'1000'00'0) + { + return 8; + } else + { + return 2; // default to 16bit + } + }; + if(!has_size(val)) + { + if(is_unsigned(val) && is_float(val)) + { + val = mpt::to_underlying(Enum::Invalid); + } else if(is_unsigned(val)) + { + val = mpt::to_underlying(Enum::Unsigned8); + } else if(is_float(val)) + { + val = mpt::to_underlying(Enum::Float32); + } else + { + val = mpt::to_underlying(Enum::Invalid); + } + } else if(!unique_size(val)) + { + // order of size check matters + if((val & 0b0'0011'00'0) == 0b0'0011'00'0) + { + val = mpt::to_underlying(Enum::Int24); + } else if(val & 0b0'0010'00'0) + { + val = mpt::to_underlying(Enum::Int16); + } else if(val & 0b0'0100'00'0) + { + if(is_float(val)) + { + val = mpt::to_underlying(Enum::Float32); + } else + { + val = mpt::to_underlying(Enum::Int32); + } + } else if(val & 0b0'1000'00'0) + { + val = mpt::to_underlying(Enum::Float64); + } else if(val & 0b0'0001'00'0) + { + if(is_unsigned(val)) + { + val = mpt::to_underlying(Enum::Unsigned8); + } else + { + val = mpt::to_underlying(Enum::Int8); + } + } + } else + { + if(is_unsigned(val) && (get_size(val) > 1)) + { + val &= ~0b0'0000'00'1; // remove unsigned + } + if(is_float(val) && (get_size(val) < 4)) + { + val &= ~0b1'0000'00'0; // remove float + } + if(!is_float(val) && (get_size(val) == 8)) + { + val |= 0b1'0000'00'0; // add float + } + } + return static_cast(val); } public: @@ -95,42 +218,59 @@ public: MPT_CONSTEXPRINLINE bool IsValid() const noexcept { - return value != SampleFormat::Invalid; + return false + || (value == SampleFormat::Unsigned8) + || (value == SampleFormat::Int8) + || (value == SampleFormat::Int16) + || (value == SampleFormat::Int24) + || (value == SampleFormat::Int32) + || (value == SampleFormat::Float32) + || (value == SampleFormat::Float64); } MPT_CONSTEXPRINLINE bool IsUnsigned() const noexcept { - return IsValid() && (value == SampleFormat::Unsigned8); + return false + || (value == SampleFormat::Unsigned8); } MPT_CONSTEXPRINLINE bool IsFloat() const noexcept { - return IsValid() && ((value == SampleFormat::Float32) || (value == SampleFormat::Float64)); + return false + || (value == SampleFormat::Float32) + || (value == SampleFormat::Float64); } MPT_CONSTEXPRINLINE bool IsInt() const noexcept { - return IsValid() && ((value != SampleFormat::Float32) && (value != SampleFormat::Float64)); + return false + || (value == SampleFormat::Unsigned8) + || (value == SampleFormat::Int8) + || (value == SampleFormat::Int16) + || (value == SampleFormat::Int24) + || (value == SampleFormat::Int32); } MPT_CONSTEXPRINLINE uint8 GetSampleSize() const noexcept { - return !IsValid() ? 0 : (value == SampleFormat::Unsigned8) ? 1 - : (value == SampleFormat::Int8) ? 1 - : (value == SampleFormat::Int16) ? 2 - : (value == SampleFormat::Int24) ? 3 - : (value == SampleFormat::Int32) ? 4 - : (value == SampleFormat::Float32) ? 4 - : (value == SampleFormat::Float64) ? 8 - : 0; + return !IsValid() ? 0 + : (value == SampleFormat::Unsigned8) ? 1 + : (value == SampleFormat::Int8) ? 1 + : (value == SampleFormat::Int16) ? 2 + : (value == SampleFormat::Int24) ? 3 + : (value == SampleFormat::Int32) ? 4 + : (value == SampleFormat::Float32) ? 4 + : (value == SampleFormat::Float64) ? 8 + : 0; } MPT_CONSTEXPRINLINE uint8 GetBitsPerSample() const noexcept { - return !IsValid() ? 0 : (value == SampleFormat::Unsigned8) ? 8 - : (value == SampleFormat::Int8) ? 8 - : (value == SampleFormat::Int16) ? 16 - : (value == SampleFormat::Int24) ? 24 - : (value == SampleFormat::Int32) ? 32 - : (value == SampleFormat::Float32) ? 32 - : (value == SampleFormat::Float64) ? 64 - : 0; + return !IsValid() ? 0 + : (value == SampleFormat::Unsigned8) ? 8 + : (value == SampleFormat::Int8) ? 8 + : (value == SampleFormat::Int16) ? 16 + : (value == SampleFormat::Int24) ? 24 + : (value == SampleFormat::Int32) ? 32 + : (value == SampleFormat::Float32) ? 32 + : (value == SampleFormat::Float64) ? 64 + : 0; } MPT_CONSTEXPRINLINE operator SampleFormat::Enum() const noexcept diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.cpp b/Frameworks/OpenMPT/OpenMPT/test/test.cpp index 7c2867af5..5f226885a 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/test.cpp @@ -38,6 +38,7 @@ #include "openmpt/soundbase/SampleConvert.hpp" #include "openmpt/soundbase/SampleDecode.hpp" #include "openmpt/soundbase/SampleEncode.hpp" +#include "openmpt/soundbase/SampleFormat.hpp" #include "../soundlib/SampleCopy.h" #include "../soundlib/SampleNormalize.h" #include "../soundlib/ModSampleCopy.h" @@ -841,6 +842,101 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(CModSpecifications::ExtensionToType("s2m"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType(""), MOD_TYPE_NONE); + // invalid + VERIFY_EQUAL(SampleFormat::FromInt(0), SampleFormat::Invalid); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'11'0), SampleFormat::Invalid); + + // correct + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); + + // no size + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'0), SampleFormat::Invalid); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0000'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0000'00'1), SampleFormat::Invalid); + + // invalid unsigned + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); + + // invalid float + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); + + // bogus size + + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'0), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'0), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0100'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0101'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'0111'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1000'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1001'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1100'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1101'00'1), SampleFormat::Int32); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b0'1111'00'1), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'0), SampleFormat::Int8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'0), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'0), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'0), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'0), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'0), SampleFormat::Int24); + + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0001'00'1), SampleFormat::Unsigned8); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0100'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0101'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'0111'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1000'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1001'00'1), SampleFormat::Float64); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1010'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1011'00'1), SampleFormat::Int24); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1100'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1101'00'1), SampleFormat::Float32); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1110'00'1), SampleFormat::Int16); + VERIFY_EQUAL(SampleFormat::FromInt(0b1'1111'00'1), SampleFormat::Int24); + }