diff -Nru libopenmpt-0.4.3/common/CompilerDetect.h libopenmpt-0.4.5/common/CompilerDetect.h --- libopenmpt-0.4.3/common/CompilerDetect.h 2018-11-16 08:31:43.000000000 +0000 +++ libopenmpt-0.4.5/common/CompilerDetect.h 2019-05-23 12:31:48.000000000 +0000 @@ -58,7 +58,11 @@ #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1916) +#if (_MSC_VER >= 1921) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,1) +#elif (_MSC_VER >= 1920) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,0) +#elif (_MSC_VER >= 1916) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,9) #elif (_MSC_VER >= 1915) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,8) diff -Nru libopenmpt-0.4.3/common/mptAlloc.cpp libopenmpt-0.4.5/common/mptAlloc.cpp --- libopenmpt-0.4.3/common/mptAlloc.cpp 2019-02-02 21:11:45.000000000 +0000 +++ libopenmpt-0.4.5/common/mptAlloc.cpp 2019-04-24 06:58:50.000000000 +0000 @@ -56,7 +56,7 @@ aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment) { - #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_GCC && defined(__GLIBCXX__) && (defined(__MINGW32__) || defined(__MINGW64__))) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN std::size_t space = count * size; void* mem = std::aligned_alloc(alignment, space); if(!mem) @@ -103,7 +103,7 @@ void aligned_free(aligned_raw_memory raw) { - #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_GCC && defined(__GLIBCXX__) && (defined(__MINGW32__) || defined(__MINGW64__))) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN std::free(raw.mem); #elif MPT_COMPILER_MSVC _aligned_free(raw.mem); diff -Nru libopenmpt-0.4.3/common/mptExceptionText.h libopenmpt-0.4.5/common/mptExceptionText.h --- libopenmpt-0.4.3/common/mptExceptionText.h 2018-07-16 12:07:14.000000000 +0000 +++ libopenmpt-0.4.5/common/mptExceptionText.h 2019-03-27 10:06:44.000000000 +0000 @@ -28,7 +28,7 @@ namespace mpt { -template T get_exception_text_impl(const std::exception & e) noexcept +template T get_exception_text_impl(const std::exception & e) { if(e.what() && (std::strlen(e.what()) > 0)) { @@ -42,28 +42,28 @@ } } -template inline T get_exception_text(const std::exception & e) noexcept +template inline T get_exception_text(const std::exception & e) { return mpt::get_exception_text_impl(e); } -template <> inline std::string get_exception_text(const std::exception & e) noexcept +template <> inline std::string get_exception_text(const std::exception & e) { return mpt::get_exception_text_impl(e); } #if defined(MPT_ENABLE_CHARSET_LOCALE) -template <> inline mpt::lstring get_exception_text(const std::exception & e) noexcept +template <> inline mpt::lstring get_exception_text(const std::exception & e) { return mpt::ToLocale(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); } #endif #if MPT_WSTRING_FORMAT -template <> inline std::wstring get_exception_text(const std::exception & e) noexcept +template <> inline std::wstring get_exception_text(const std::exception & e) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); } #endif #if MPT_USTRING_MODE_UTF8 -template <> inline mpt::ustring get_exception_text(const std::exception & e) noexcept +template <> inline mpt::ustring get_exception_text(const std::exception & e) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); } diff -Nru libopenmpt-0.4.3/common/mptPathString.cpp libopenmpt-0.4.5/common/mptPathString.cpp --- libopenmpt-0.4.3/common/mptPathString.cpp 2018-12-06 14:29:05.000000000 +0000 +++ libopenmpt-0.4.5/common/mptPathString.cpp 2019-03-11 12:37:56.000000000 +0000 @@ -224,6 +224,14 @@ { if(fname) *fname = mpt::PathString::FromNative(p); if(ext) *ext = mpt::PathString(); + } else if(last_dot == 0) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); + } else if(p == PL_(".") || p == PL_("..")) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); } else { if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot)); diff -Nru libopenmpt-0.4.3/common/mptStringBuffer.h libopenmpt-0.4.5/common/mptStringBuffer.h --- libopenmpt-0.4.3/common/mptStringBuffer.h 2018-07-16 12:07:14.000000000 +0000 +++ libopenmpt-0.4.5/common/mptStringBuffer.h 2019-03-23 14:50:45.000000000 +0000 @@ -185,7 +185,6 @@ , mode(mode) { MPT_STATIC_ASSERT(sizeof(Tchar) == 1); - MPT_ASSERT(size > 0); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -218,7 +217,6 @@ , mode(mode) { MPT_STATIC_ASSERT(sizeof(Tchar) == 1); - MPT_ASSERT(size > 0); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -400,6 +398,8 @@ buffer[size - 1] = 0; } +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) + template void SetNullTerminator(wchar_t (&buffer)[size]) { @@ -413,6 +413,8 @@ buffer[size - 1] = 0; } +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + // Remove any chars after the first null char template @@ -633,6 +635,11 @@ } +#if MPT_GCC_AT_LEAST(8,1,0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif + // Copy from a char array to a fixed size char array. template void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) @@ -676,6 +683,10 @@ dest.assign(src); } +#if MPT_GCC_AT_LEAST(8,1,0) +#pragma GCC diagnostic pop +#endif + #if MPT_COMPILER_MSVC #pragma warning(pop) diff -Nru libopenmpt-0.4.3/common/mptString.h libopenmpt-0.4.5/common/mptString.h --- libopenmpt-0.4.3/common/mptString.h 2018-09-15 13:33:26.000000000 +0000 +++ libopenmpt-0.4.5/common/mptString.h 2019-03-04 10:45:18.000000000 +0000 @@ -245,10 +245,17 @@ #define MPT_LITERAL(x) x #define MPT_STRING(x) std::string( x ) +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) #define MPT_WCHAR_TYPE wchar_t #define MPT_WCHAR(x) L ## x #define MPT_WLITERAL(x) L ## x #define MPT_WSTRING(x) std::wstring( L ## x ) +#else // MPT_COMPILER_QUIRK_NO_WCHAR +#define MPT_WCHAR_TYPE char32_t +#define MPT_WCHAR(x) U ## x +#define MPT_WLITERAL(x) U ## x +#define MPT_WSTRING(x) std::u32string( U ## x ) +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR template diff -Nru libopenmpt-0.4.3/common/versionNumber.h libopenmpt-0.4.5/common/versionNumber.h --- libopenmpt-0.4.3/common/versionNumber.h 2019-02-10 20:01:40.000000000 +0000 +++ libopenmpt-0.4.5/common/versionNumber.h 2019-05-26 19:24:15.000000000 +0000 @@ -20,7 +20,7 @@ //Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 28 -#define VER_MINOR 03 +#define VER_MINOR 05 #define VER_MINORMINOR 00 //Numerical value of the version. diff -Nru libopenmpt-0.4.3/configure libopenmpt-0.4.5/configure --- libopenmpt-0.4.3/configure 2019-02-11 05:40:08.000000000 +0000 +++ libopenmpt-0.4.5/configure 2019-05-27 06:50:02.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libopenmpt 0.4.3+release.autotools. +# Generated by GNU Autoconf 2.69 for libopenmpt 0.4.5+release.autotools. # # Report bugs to . # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='libopenmpt' PACKAGE_TARNAME='libopenmpt' -PACKAGE_VERSION='0.4.3+release.autotools' -PACKAGE_STRING='libopenmpt 0.4.3+release.autotools' +PACKAGE_VERSION='0.4.5+release.autotools' +PACKAGE_STRING='libopenmpt 0.4.5+release.autotools' PACKAGE_BUGREPORT='https://bugs.openmpt.org/' PACKAGE_URL='https://lib.openmpt.org/' @@ -1485,7 +1485,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libopenmpt 0.4.3+release.autotools to adapt to many kinds of systems. +\`configure' configures libopenmpt 0.4.5+release.autotools to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1556,7 +1556,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libopenmpt 0.4.3+release.autotools:";; + short | recursive ) echo "Configuration of libopenmpt 0.4.5+release.autotools:";; esac cat <<\_ACEOF @@ -1756,7 +1756,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libopenmpt configure 0.4.3+release.autotools +libopenmpt configure 0.4.5+release.autotools generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2246,7 +2246,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libopenmpt $as_me 0.4.3+release.autotools, which was +It was created by libopenmpt $as_me 0.4.5+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3117,7 +3117,7 @@ # Define the identity of the package. PACKAGE='libopenmpt' - VERSION='0.4.3+release.autotools' + VERSION='0.4.5+release.autotools' cat >>confdefs.h <<_ACEOF @@ -17281,13 +17281,13 @@ -$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.3\"" >>confdefs.h +$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.5\"" >>confdefs.h -$as_echo "#define MPT_SVNVERSION \"11358\"" >>confdefs.h +$as_echo "#define MPT_SVNVERSION \"11626\"" >>confdefs.h -$as_echo "#define MPT_SVNDATE \"2019-02-11T05:32:52.673531Z\"" >>confdefs.h +$as_echo "#define MPT_SVNDATE \"2019-05-27T06:47:50.022790Z\"" >>confdefs.h $as_echo "#define MPT_PACKAGE true" >>confdefs.h @@ -23527,7 +23527,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libopenmpt $as_me 0.4.3+release.autotools, which was +This file was extended by libopenmpt $as_me 0.4.5+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23594,7 +23594,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libopenmpt config.status 0.4.3+release.autotools +libopenmpt config.status 0.4.5+release.autotools configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru libopenmpt-0.4.3/configure.ac libopenmpt-0.4.5/configure.ac --- libopenmpt-0.4.3/configure.ac 2019-02-11 05:39:59.000000000 +0000 +++ libopenmpt-0.4.5/configure.ac 2019-05-27 06:49:52.000000000 +0000 @@ -1,4 +1,4 @@ -AC_INIT([libopenmpt], [0.4.3+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) +AC_INIT([libopenmpt], [0.4.5+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) AC_PREREQ([2.68]) AC_CONFIG_MACRO_DIR([m4]) @@ -27,9 +27,9 @@ AC_SUBST([LIBOPENMPT_LTVER_REVISION]) AC_SUBST([LIBOPENMPT_LTVER_AGE]) -AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.3"], [svn version]) -AC_DEFINE([MPT_SVNVERSION], ["11358"], [svn version]) -AC_DEFINE([MPT_SVNDATE], ["2019-02-11T05:32:52.673531Z"], [svn date]) +AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.5"], [svn version]) +AC_DEFINE([MPT_SVNVERSION], ["11626"], [svn version]) +AC_DEFINE([MPT_SVNDATE], ["2019-05-27T06:47:50.022790Z"], [svn date]) AC_DEFINE([MPT_PACKAGE], [true], [is package]) diff -Nru libopenmpt-0.4.3/debian/changelog libopenmpt-0.4.5/debian/changelog --- libopenmpt-0.4.3/debian/changelog 2019-02-15 09:30:30.000000000 +0000 +++ libopenmpt-0.4.5/debian/changelog 2019-07-15 22:46:33.000000000 +0000 @@ -1,3 +1,9 @@ +libopenmpt (0.4.5-1) unstable; urgency=medium + + * New upstream release. + + -- James Cowgill Mon, 15 Jul 2019 23:46:33 +0100 + libopenmpt (0.4.3-1) unstable; urgency=medium * New upstream release. diff -Nru libopenmpt-0.4.3/doc/contributing.md libopenmpt-0.4.5/doc/contributing.md --- libopenmpt-0.4.3/doc/contributing.md 1970-01-01 00:00:00.000000000 +0000 +++ libopenmpt-0.4.5/doc/contributing.md 2018-02-10 00:09:26.000000000 +0000 @@ -0,0 +1,43 @@ + +Contributing +============ + +OpenMPT, libopenmpt, openmpt123, xmp-openmpt, in_openmpt and foo_openmpt are +developed in the Subversion repository at +[https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/](https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/). +Patches can be provided either against this Subversion repository or against our +GitHub mirror at +[https://github.com/OpenMPT/openmpt/](https://github.com/OpenMPT/openmpt/). + +We do not have a developer mailing list. Discussions about new features or +problems can happen at: + * [Issue Tracker](https://bugs.openmpt.org/), preferred for specific bug + reports or bug fixes and feature development discussion + * [Forum](https://forum.openmpt.org/), preferred for long-term discussion of + new features or specific questions about development + * [IRC channel (`EsperNET/#modplug`)](irc://irc.esper.net:5555/#modplug), + preferred for shorter questions + * [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please + only use for rather tiny fixes, see below + +For patch submissions, please also see +[OpenMPT style guide](openmpt_styleguide.md) and +[libopenmpt style guide](libopenmpt_styleguide.md). + +### Contributing via GitHub + +As OpenMPT is developed in a Subversion repository and the GitHub repository is +just mirrored from that, we cannot directly take pull requests via GitHub. We +recognize that, especially for tiny bug fixes, the burden to choose a different +way than GitHub for contributing can be too high. Thus, we will of course react, +give feedback, and take patches also via GitHub pull requests. However, as the +GitHub repository is strictly downstream from our Subversion repository (and +this will not change, due to considerable complications when synchronizing this +two-way), we cannot directly merge pull requests on GitHub. We will merge +contributions to our Subversion repository, which will then in turn be mirrored +to GitHub automatically, after which we will close the pull request. Authorship +attribution in git relies on the email address used in the commit header, which +is not how it usually works in Subversion. We will thus add an additional line +to the commit message in the form of `Patch-by: John Doe `. If +you prefer to be attributed with your nickname and/or without your email +address, that would also be fine for us. diff -Nru libopenmpt-0.4.3/doc/libopenmpt_styleguide.md libopenmpt-0.4.5/doc/libopenmpt_styleguide.md --- libopenmpt-0.4.3/doc/libopenmpt_styleguide.md 1970-01-01 00:00:00.000000000 +0000 +++ libopenmpt-0.4.5/doc/libopenmpt_styleguide.md 2018-02-05 08:32:49.000000000 +0000 @@ -0,0 +1,104 @@ + +Coding conventions +------------------ + + +### libopenmpt + +**Note:** +**This applies to `libopenmpt/` and `openmpt123/` directories only.** +**Use OpenMPT style otherwise.** + +The code generally tries to follow these conventions, but they are not +strictly enforced and there are valid reasons to diverge from these +conventions. Using common sense is recommended. + + - In general, the most important thing is to keep style consistent with + directly surrounding code. + - Use C++ std types when possible, prefer `std::size_t` and `std::int32_t` + over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`) + - Qualify namespaces explicitly, do not use `using`. + Members of `namespace openmpt` can be named without full namespace + qualification. + - Prefer the C++ version in `namespace std` if the same functionality is + provided by the C standard library as well. Also, include the C++ + version of C standard library headers (e.g. use `` instead of + ``. + - Do not use ANY locale-dependant C functions. For locale-dependant C++ + functionaly (especially iostream), always imbue the + `std::locale::classic()` locale. + - Prefer kernel_style_names over CamelCaseNames. + - If a folder (or one of its parent folders) contains .clang-format, + use clang-format v3.5 for indenting C++ and C files, otherwise: + - `{` are placed at the end of the opening line. + - Enclose even single statements in curly braces. + - Avoid placing single statements on the same line as the `if`. + - Opening parentheses are separated from keywords with a space. + - Opening parentheses are not separated from function names. + - Place spaces around operators and inside parentheses. + - Align `:` and `,` when inheriting or initializing members in a + constructor. + - The pointer `*` is separated from both the type and the variable name. + - Use tabs for identation, spaces for formatting. + Tabs should only appear at the very beginning of a line. + Do not assume any particular width of the TAB character. If width is + important for formatting reasons, use spaces. + - Use empty lines at will. + - API documentation is done with doxygen. + Use general C doxygen for the C API. + Use QT-style doxygen for the C++ API. + +#### libopenmpt indentation example + +~~~~{.cpp} +namespace openmpt { + +// This is totally meaningless code and just illustrates indentation. + +class foo + : public base + , public otherbase +{ + +private: + + std::int32_t x; + std::int16_t y; + +public: + + foo() + : x(0) + , y(-1) + { + return; + } + + int bar() const; + +}; // class foo + +int foo::bar() const { + + for ( int i = 0; i < 23; ++i ) { + switch ( x ) { + case 2: + something( y ); + break; + default: + something( ( y - 1 ) * 2 ); + break; + } + } + if ( x == 12 ) { + return -1; + } else if ( x == 42 ) { + return 1; + } + return 42; + +} + +} // namespace openmpt +~~~~ + diff -Nru libopenmpt-0.4.3/doc/module_formats.md libopenmpt-0.4.5/doc/module_formats.md --- libopenmpt-0.4.3/doc/module_formats.md 1970-01-01 00:00:00.000000000 +0000 +++ libopenmpt-0.4.5/doc/module_formats.md 2018-09-09 11:28:35.000000000 +0000 @@ -0,0 +1,106 @@ +How to add support for new module formats +========================================= + +This document describes the basics of writing a new module loader and related +work that has to be done. We will not discuss in detail how to write the loader, +have a look at existing loaders to get an idea how they work in general. + +General hints +------------- +* We strive for quality over quantity. The goal is not to support as many module + formats as possible, but to support them as well as possible. +* Write defensive code. Guard against out-of-bound values, division by zero and + similar stuff. libopenmpt is constantly fuzz-tested to catch any crashes, but + of course we want our code to be reliable from the start. +* Every format should have its own `MODTYPE` flag, unless it can be reasonably + represented as a subset of another format (like Ice Tracker ICE files being + a subset of ProTracker MOD). +* When reading binary structs from the file, use our data types with defined + endianness, which can be found in `common/Endianness.h`: + * Big-Endian: (u)int8/16/32/64be, float32be, float64be + * Little-Endian: (u)int8/16/32/64le, float32le, float64le + + Entire structs containing integers with defined endianness can be read in one + go if they are tagged with `MPT_BINARY_STRUCT` (see existing loaders for an + example). +* `m_nChannels` **MUST NOT** be changed after a pattern has been created, as + existing patterns will be interpreted incorrectly. For module formats that + support per-pattern channel amounts, the maximum number of channels must be + determined beforehand. +* Strings can be safely handled using: + * `FileReader::ReadString` and friends for reading them directly from a file + * `mpt::String::Read` for reading them from a struct or char array, + * `mpt::String::Copy` for copying between char arrays or `std::string`. + + "Read" functions take care of string padding (zero / space padding), so those + should be used when extracting strings from files. "Copy" should only be used + on strings that have previously been read using the "Read" functions. + If the target is a char array rather than a `std::string`, these will take + care of properly null-terminating the target char array, and prevent reading + past the end of a (supposedly null-terminated) source char array. +* Do not use non-const static variables in your loader. Loaders need to be + thread-safe for libopenmpt. +* `FileReader` instances may be used to treat a portion of another file as its + own independent file (through `FileReader::ReadChunk`). This can be useful + with "embedded files" such as WAV or Ogg samples. Container formats are + another good example for this usage. +* Samples *either* use middle-C frequency *or* finetune + transpose. For the few + weird formats that use both, it may make sense to translate everything into + middle-C frequency. +* Add the new `MODTYPE` to `CSoundFile::UseFinetuneAndTranspose` if applicable, + and see if any effect handlers in `soundlib/Snd_fx.cpp` need to know the new + `MODTYPE`. +* Do not rely on hard-coded magic numbers. For example, when comparing if an + index is valid for a given array, do not hard-code the array size but rather + use `mpt::size` or, for ensuring that char arrays are null-terminated, + `mpt::String::SetNullTerminator`. Similarly, do not assume any specific + quantities for OpenMPT's constants like MAX_SAMPLES, MAX_PATTERN_ROWS, etc. + These may change at any time. +* Pay attention to off-by-one errors when comparing against MAX_SAMPLES and + MAX_INSTRUMENTS, since sample and instrument numbers are 1-based. +* Placement of the loader function in `CSoundFile::Create` depends on various + factors. In general, module formats that have very bad magic numbers (and thus + might cause other formats to get mis-interpreted) should be placed at the + bottom of the list. Two notable examples are 669 files, where the first two + bytes of the file are "if" (which may e.g. cause a song title starting with + "if ..." in various other formats to be interpreted as a 669 module), and of + course Ultimate SoundTracker modules, which have no magic bytes at all. +* Avoid use of functions tagged with MPT_DEPRECATED. + +Probing +------- +libopenmpt provides fast probing functions that can be used by library users +to quickly check if a file is most likely playable with libopenmpt, even if only +a fraction of the file is available (e.g. when streaming from the internet). + +In order to satisfy these requirements, probing functions should do as little +work as possible (e.g. only parse the header of the file), but as much as +required to tell with some certainty that the file is really of a certain mod +format. However, probing functions should not rely on having access to more than +the first `CSoundFile::ProbeRecommendedSize` bytes of the file. + +* Probing functions **must not** allocate any memory on the heap. +* Probing functions **must not** return ProbeFailure or ProbeWantMoreData for + any file that would normally be accepted by the loader. In particular, this + means that any header checks must not be any more aggressive than they would + be in the real loader (hence it is a good idea to not copy-paste this code but + rather put it in a separate function), and the minimum additional size passed + to `CSoundFile::ProbeAdditionalSize` must not be higher than the biggest size + that would cause a hard failure (i.e. returning `false`) in the module loader. +* Probing functions **may** return ProbeSuccess for files that would be rejected + by a loader after a more thorough inspection. For example, probing functions + do not need to verify that all required chunks of an IFF-like file format are + actually present, if the header makes it obvious enough that the file is + highly likely to be a module. + +Adding loader to the build systems and various other locations +-------------------------------------------------------------- +Apart from writing the module loader itself, there are a couple of other places +that need to be updated: +* Add loader file to `build/android_ndk/Android.mk`. +* Add loader file to `build/autotools/Makefile.am`. +* Run `build/regenerate_vs_projects.sh` / `build/regenerate_vs_projects.cmd` + (depending on your platform) +* Add file extension to `installer/filetypes.iss` (in four places). +* Add file extension to `CTrackApp::OpenModulesDialog` in `mptrack/Mptrack.cpp`. +* Add format information to `soundlib/Tables.cpp`. diff -Nru libopenmpt-0.4.3/Doxyfile.in libopenmpt-0.4.5/Doxyfile.in --- libopenmpt-0.4.3/Doxyfile.in 2019-02-11 05:39:59.000000000 +0000 +++ libopenmpt-0.4.5/Doxyfile.in 2019-05-27 06:49:52.000000000 +0000 @@ -784,9 +784,12 @@ README.md \ libopenmpt/dox/dependencies.md \ libopenmpt/dox/packaging.md \ + doc/contributing.md \ + doc/libopenmpt_styleguide.md \ libopenmpt/dox/tests.md \ libopenmpt/dox/changelog.md \ libopenmpt/dox/todo.md \ + doc/module_formats.md \ libopenmpt/libopenmpt.hpp \ libopenmpt/libopenmpt.h \ libopenmpt/libopenmpt_stream_callbacks_buffer.h \ diff -Nru libopenmpt-0.4.3/libopenmpt/dox/changelog.md libopenmpt-0.4.5/libopenmpt/dox/changelog.md --- libopenmpt-0.4.3/libopenmpt/dox/changelog.md 2019-02-11 05:32:46.000000000 +0000 +++ libopenmpt-0.4.5/libopenmpt/dox/changelog.md 2019-05-27 06:47:45.000000000 +0000 @@ -5,6 +5,28 @@ For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.4.5 (2019-05-27) + + * [**Sec**] Possible crash during playback due out-of-bounds read in XM and + MT2 files (r11608). + + * Breaking out of a sustain loop through Note-Off sometimes didn't continue in + the regular sample loop. + * Seeking did not stop notes playing with XM Key Off (Kxx) effect. + +### libopenmpt 0.4.4 (2019-04-07) + + * [**Bug**] Channel VU meters were swapped. + + * Startrekker: Clamp speed to 31 ticks per row. + * MTM: Ignore unused Exy commands on import. Command E5x (Set Finetune) is now + applied correctly. + * MOD: Sample swapping was always enabled since it has been separated from the + ProTracker 1/2 compatibility flag. Now it is always enabled for Amiga-style + modules and otherwise the old heuristic is used again. + + * stb_vorbis: Update to v1.16 (2019-03-05). + ### libopenmpt 0.4.3 (2019-02-11) * [**Sec**] Possible crash due to null-pointer access when doing a portamento diff -Nru libopenmpt-0.4.3/libopenmpt/dox/index.dox libopenmpt-0.4.5/libopenmpt/dox/index.dox --- libopenmpt-0.4.3/libopenmpt/dox/index.dox 2018-01-27 17:01:36.000000000 +0000 +++ libopenmpt-0.4.5/libopenmpt/dox/index.dox 2019-02-28 13:37:59.000000000 +0000 @@ -11,8 +11,11 @@ * - \ref md_README "README" * - \ref dependencies "Dependencies" * - \ref packaging "Packaging" + * - \ref md_doc_contributing "Contributing" + * - \ref md_doc_libopenmpt_styleguide "Style Guide" * - \ref tests "Tests" * - \ref changelog "Changelog" + * - \ref md_doc_module_formats "Implementing new Module Formats" * - \ref todo "TODO" * \subsection toc_apis APIs * - libopenmpt diff -Nru libopenmpt-0.4.3/libopenmpt/libopenmpt_version.h libopenmpt-0.4.5/libopenmpt/libopenmpt_version.h --- libopenmpt-0.4.3/libopenmpt/libopenmpt_version.h 2019-02-11 05:32:46.000000000 +0000 +++ libopenmpt-0.4.5/libopenmpt/libopenmpt_version.h 2019-05-27 06:47:45.000000000 +0000 @@ -19,7 +19,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 4 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 3 +#define OPENMPT_API_VERSION_PATCH 5 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff -Nru libopenmpt-0.4.3/libopenmpt/libopenmpt_version.mk libopenmpt-0.4.5/libopenmpt/libopenmpt_version.mk --- libopenmpt-0.4.3/libopenmpt/libopenmpt_version.mk 2019-02-11 05:32:46.000000000 +0000 +++ libopenmpt-0.4.5/libopenmpt/libopenmpt_version.mk 2019-05-27 06:47:45.000000000 +0000 @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=4 -LIBOPENMPT_VERSION_PATCH=3 +LIBOPENMPT_VERSION_PATCH=5 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=3 +LIBOPENMPT_LTVER_REVISION=5 LIBOPENMPT_LTVER_AGE=1 diff -Nru libopenmpt-0.4.3/Makefile.am libopenmpt-0.4.5/Makefile.am --- libopenmpt-0.4.3/Makefile.am 2018-11-03 08:00:04.000000000 +0000 +++ libopenmpt-0.4.5/Makefile.am 2019-02-28 13:37:59.000000000 +0000 @@ -6,6 +6,9 @@ EXTRA_DIST += LICENSE EXTRA_DIST += README.md EXTRA_DIST += Doxyfile.in +EXTRA_DIST += doc/contributing.md +EXTRA_DIST += doc/libopenmpt_styleguide.md +EXTRA_DIST += doc/module_formats.md EXTRA_DIST += libopenmpt/dox/changelog.md EXTRA_DIST += libopenmpt/dox/dependencies.md EXTRA_DIST += libopenmpt/dox/index.dox diff -Nru libopenmpt-0.4.3/Makefile.in libopenmpt-0.4.5/Makefile.in --- libopenmpt-0.4.3/Makefile.in 2019-02-11 05:40:09.000000000 +0000 +++ libopenmpt-0.4.5/Makefile.in 2019-05-27 06:50:03.000000000 +0000 @@ -1227,12 +1227,13 @@ ACLOCAL_AMFLAGS = -I m4 --install EXTRA_DIST = m4/emptydir libopenmpt/libopenmpt.pc.in \ libmodplug/libmodplug.pc.in LICENSE README.md Doxyfile.in \ - libopenmpt/dox/changelog.md libopenmpt/dox/dependencies.md \ - libopenmpt/dox/index.dox libopenmpt/dox/packaging.md \ - libopenmpt/dox/quickstart.md libopenmpt/dox/tests.md \ - libopenmpt/dox/todo.md libopenmpt/libopenmpt_version.mk \ - test/test.xm test/test.s3m test/test.mptm man/openmpt123.1 \ - examples/.clang-format \ + doc/contributing.md doc/libopenmpt_styleguide.md \ + doc/module_formats.md libopenmpt/dox/changelog.md \ + libopenmpt/dox/dependencies.md libopenmpt/dox/index.dox \ + libopenmpt/dox/packaging.md libopenmpt/dox/quickstart.md \ + libopenmpt/dox/tests.md libopenmpt/dox/todo.md \ + libopenmpt/libopenmpt_version.mk test/test.xm test/test.s3m \ + test/test.mptm man/openmpt123.1 examples/.clang-format \ libopenmpt/bindings/freebasic/libopenmpt.bi MOSTLYCLEANFILES = $(DX_CLEANFILES) dist_doc_DATA = LICENSE README.md diff -Nru libopenmpt-0.4.3/man/openmpt123.1 libopenmpt-0.4.5/man/openmpt123.1 --- libopenmpt-0.4.3/man/openmpt123.1 2019-02-11 05:39:58.000000000 +0000 +++ libopenmpt-0.4.5/man/openmpt123.1 2019-05-27 06:49:51.000000000 +0000 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH OPENMPT123 "1" "February 2019" "openmpt123 v0.4.3" "User Commands" +.TH OPENMPT123 "1" "May 2019" "openmpt123 v0.4.5" "User Commands" .SH NAME openmpt123 - command line module music player based on libopenmpt .SH SYNOPSIS diff -Nru libopenmpt-0.4.3/README.md libopenmpt-0.4.5/README.md --- libopenmpt-0.4.3/README.md 2018-10-13 11:53:55.000000000 +0000 +++ libopenmpt-0.4.5/README.md 2019-02-28 12:58:57.000000000 +0000 @@ -1,3 +1,8 @@ + +README +====== + + OpenMPT and libopenmpt ====================== diff -Nru libopenmpt-0.4.3/soundbase/SampleFormat.h libopenmpt-0.4.5/soundbase/SampleFormat.h --- libopenmpt-0.4.3/soundbase/SampleFormat.h 2018-07-16 12:07:14.000000000 +0000 +++ libopenmpt-0.4.5/soundbase/SampleFormat.h 2019-04-16 08:01:42.000000000 +0000 @@ -29,11 +29,11 @@ }; template struct SampleFormatTraits; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatUnsigned8; }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt16; }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt24; }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt32; }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatFloat32; }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } }; template struct SampleFormatToType; template<> struct SampleFormatToType { typedef uint8 type; }; diff -Nru libopenmpt-0.4.3/soundlib/AudioReadTarget.h libopenmpt-0.4.5/soundlib/AudioReadTarget.h --- libopenmpt-0.4.3/soundlib/AudioReadTarget.h 2018-09-15 13:07:34.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/AudioReadTarget.h 2019-04-16 08:01:42.000000000 +0000 @@ -40,7 +40,7 @@ , outputBuffer(buffer) , outputBuffers(buffers) { - MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat).IsValid()); + MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat()).IsValid()); } std::size_t GetRenderedCount() const { return countRendered; } public: @@ -48,7 +48,7 @@ { // Convert to output sample format and optionally perform dithering and clipping if needed - const SampleFormat sampleFormat = SampleFormatTraits::sampleFormat; + const SampleFormat sampleFormat = SampleFormatTraits::sampleFormat(); if(sampleFormat.IsInt()) { diff -Nru libopenmpt-0.4.3/soundlib/InstrumentExtensions.cpp libopenmpt-0.4.5/soundlib/InstrumentExtensions.cpp --- libopenmpt-0.4.3/soundlib/InstrumentExtensions.cpp 2018-08-24 17:25:50.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/InstrumentExtensions.cpp 2019-03-08 20:52:17.000000000 +0000 @@ -120,9 +120,9 @@ PSE. PanEnv.nSustainEnd; PTTL pitchToTempoLock; PTTF pitchToTempoLock (fractional part); -PVEH nPluginVelocityHandling; -PVOH nPluginVolumeHandling; -R... nResampling; +PVEH pluginVelocityHandling; +PVOH pluginVolumeHandling; +R... resampling; RP.. [EXT] nRestartPos; RPB. [EXT] nRowsPerBeat; RPM. [EXT] nRowsPerMeasure; @@ -314,12 +314,12 @@ WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MagicBE("PiE[") ) WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) - WRITE_MPTHEADER_trunc_member( nResampling , uint16 , MagicBE("R...") ) + WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") ) WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) - WRITE_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") ) - WRITE_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") ) + WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) + WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MagicLE("PTTF") ) WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) @@ -373,9 +373,9 @@ WritePropertyIfNeeded(*this, &ModInstrument::nMidiChannel, MagicBE("MC.."), sizeof(ModInstrument::nMidiChannel), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nMidiProgram, MagicBE("MP.."), sizeof(ModInstrument::nMidiProgram), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::wMidiBank, MagicBE("MB.."), sizeof(ModInstrument::wMidiBank), f, numInstruments); - WritePropertyIfNeeded(*this, &ModInstrument::nResampling, MagicBE("R..."), sizeof(ModInstrument::nResampling), f, numInstruments); - WritePropertyIfNeeded(*this, &ModInstrument::nPluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::nPluginVelocityHandling), f, numInstruments); - WritePropertyIfNeeded(*this, &ModInstrument::nPluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::nPluginVolumeHandling), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::resampling, MagicBE("R..."), sizeof(ModInstrument::resampling), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::pluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::pluginVelocityHandling), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::pluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::pluginVolumeHandling), f, numInstruments); if(!(GetType() & MOD_TYPE_XM)) { @@ -575,16 +575,15 @@ GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") ) GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") ) GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") ) - GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") ) - GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") ) + GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") ) + GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") ) GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) - GET_MPTHEADER_sized_member( nResampling , uint32 , MagicBE("R...") ) GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) GET_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) - GET_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") ) - GET_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") ) + GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) + GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") ) GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") ) @@ -592,6 +591,14 @@ GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") ) GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") ) GET_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") ) + case MagicBE("R..."): + { + // Resampling has been written as various sizes including uint16 and uint32 in the past + uint32 tmp = file.ReadTruncatedIntLE(fsize); + if(Resampling::IsKnownMode(tmp)) + input->resampling = static_cast(tmp); + result = true; + } break; case MagicBE("PTTL"): { // Integer part of pitch/tempo lock diff -Nru libopenmpt-0.4.3/soundlib/Load_mod.cpp libopenmpt-0.4.5/soundlib/Load_mod.cpp --- libopenmpt-0.4.3/soundlib/Load_mod.cpp 2018-12-21 13:11:03.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Load_mod.cpp 2019-04-06 13:49:58.000000000 +0000 @@ -345,23 +345,23 @@ // Synthesized StarTrekker instruments struct AMInstrument { - char am[2]; // "AM" + char am[2]; // "AM" char zero[4]; - uint16be startLevel; // Start level - uint16be attack1Level; // Attack 1 level - uint16be attack1Speed; // Attack 1 speed - uint16be attack2Level; // Attack 2 level - uint16be attack2Speed; // Attack 2 speed - uint16be sustainLevel; // Sustain level - uint16be decaySpeed; // Decay speed - uint16be sustainTime; // Sustain time - uint16be nt; // ? - uint16be releaseSpeed; // Release speed - uint16be waveform; // Waveform - int16be pitchFall; // Pitch fall - uint16be vibAmp; // Vibrato amplitude - uint16be vibSpeed; // Vibrato speed - uint16be octave; // Base frequency + uint16be startLevel; // Start level + uint16be attack1Level; // Attack 1 level + uint16be attack1Speed; // Attack 1 speed + uint16be attack2Level; // Attack 2 level + uint16be attack2Speed; // Attack 2 speed + uint16be sustainLevel; // Sustain level + uint16be decaySpeed; // Decay speed + uint16be sustainTime; // Sustain time + uint16be nt; // ? + uint16be releaseSpeed; // Release speed + uint16be waveform; // Waveform + int16be pitchFall; // Pitch fall + uint16be vibAmp; // Vibrato amplitude + uint16be vibSpeed; // Vibrato speed + uint16be octave; // Base frequency void ConvertToMPT(ModSample &sample, ModInstrument &ins, mpt::fast_prng &rng) const { @@ -376,15 +376,16 @@ sample.RelativeTone = static_cast(-12 * octave); if(sample.AllocateSample()) { + int8 *p = sample.sample8(); for(SmpLength i = 0; i < sample.nLength; i++) { switch(waveform) { default: - case 0: sample.sample8()[i] = ModSinusTable[i * 2]; break; // Sine - case 1: sample.sample8()[i] = static_cast(-128 + i * 8); break; // Saw - case 2: sample.sample8()[i] = i < 16 ? -128 : 127; break; // Square - case 3: sample.sample8()[i] = mpt::random(rng); break; // Noise + case 0: p[i] = ModSinusTable[i * 2]; break; // Sine + case 1: p[i] = static_cast(-128 + i * 8); break; // Saw + case 2: p[i] = i < 16 ? -128 : 127; break; // Square + case 3: p[i] = mpt::random(rng); break; // Noise } } } @@ -492,7 +493,7 @@ // Check if header magic equals a given string. -static bool IsMagic(const char *magic1, const char *magic2) +static bool IsMagic(const char *magic1, const char (&magic2)[5]) { return std::memcmp(magic1, magic2, 4) == 0; } @@ -1023,11 +1024,15 @@ if(m.command || m.param) { - // No support for Startrekker assembly macros if(isStartrekker && m.command == 0x0E) { + // No support for Startrekker assembly macros m.command = CMD_NONE; m.param = 0; + } else if(isStartrekker && m.command == 0x0F && m.param > 0x1F) + { + // Startrekker caps speed at 31 ticks per row + m.param = 0x1F; } ConvertModCommand(m); } @@ -1281,13 +1286,13 @@ // Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829) enum STVersions { - UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski) - UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski) - ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.) - ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.) - ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.) - MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters) - ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.) + UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski) + UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski) + ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.) + ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.) + ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.) + MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters) + ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.) }; @@ -1521,8 +1526,7 @@ if(fileHeader.restartPos != 0x78) { // Convert to CIA timing - //m_nDefaultTempo = TEMPO(((709378.92 / 50.0) * 125.0) / ((240 - fileHeader.restartPos) * 122.0)); - m_nDefaultTempo.Set((709379 / ((240 - fileHeader.restartPos) * 122)) * 125 / 50); + m_nDefaultTempo = TEMPO((709379.0 * 125.0 / 50.0) / ((240 - fileHeader.restartPos) * 122.0)); if(minVersion > UST1_80) { // D.O.C. SoundTracker IX re-introduced the variable tempo after some other versions dropped it. diff -Nru libopenmpt-0.4.3/soundlib/Load_mt2.cpp libopenmpt-0.4.5/soundlib/Load_mt2.cpp --- libopenmpt-0.4.3/soundlib/Load_mt2.cpp 2018-12-06 14:29:05.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Load_mt2.cpp 2019-03-08 20:52:17.000000000 +0000 @@ -1060,7 +1060,7 @@ { if(drumSample[drum] == i && Instruments[drumMap[drum]] != nullptr) { - Instruments[drumMap[drum]]->nResampling = SRCMODE_NEAREST; + Instruments[drumMap[drum]]->resampling = SRCMODE_NEAREST; } } } @@ -1105,7 +1105,7 @@ mptSmp.nFineTune = group.pitch; if(sampleNoInterpolation[sample - 1]) { - mptIns->nResampling = SRCMODE_NEAREST; + mptIns->resampling = SRCMODE_NEAREST; } if(sampleSynchronized[sample - 1]) { diff -Nru libopenmpt-0.4.3/soundlib/Load_mtm.cpp libopenmpt-0.4.5/soundlib/Load_mtm.cpp --- libopenmpt-0.4.3/soundlib/Load_mtm.cpp 2018-12-06 14:29:05.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Load_mtm.cpp 2019-02-28 20:53:19.000000000 +0000 @@ -58,8 +58,8 @@ LimitMax(mptSmp.nLoopEnd, mptSmp.nLength); if(mptSmp.nLoopStart + 4 >= mptSmp.nLoopEnd) mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; if(mptSmp.nLoopEnd) mptSmp.uFlags.set(CHN_LOOP); - mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, mptSmp.nFineTune); + mptSmp.nFineTune = finetune; // Uses MOD units but allows the full int8 range rather than just -8...+7 so we keep the value as-is and convert it during playback + mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, finetune * 16); if(attribute & 0x01) { @@ -204,11 +204,25 @@ { // No 8xx panning in MultiTracker, only E8x cmd = param = 0; + } else if(cmd == 0x0E) + { + // MultiTracker does not support these commands + switch(param & 0xF0) + { + case 0x00: + case 0x30: + case 0x40: + case 0x60: + case 0x70: + case 0xF0: + cmd = param = 0; + break; + } } - m->command = cmd; - m->param = param; if(cmd != 0 || param != 0) { + m->command = cmd; + m->param = param; ConvertModCommand(*m); #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MTM, MOD_TYPE_S3M, *this); diff -Nru libopenmpt-0.4.3/soundlib/ModChannel.h libopenmpt-0.4.5/soundlib/ModChannel.h --- libopenmpt-0.4.3/soundlib/ModChannel.h 2018-11-02 00:00:49.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/ModChannel.h 2019-05-24 20:38:19.000000000 +0000 @@ -28,8 +28,8 @@ struct EnvInfo { FlagSet flags; - uint32 nEnvPosition; - int32 nEnvValueAtReleaseJump; + uint32 nEnvPosition = 0; + int32 nEnvValueAtReleaseJump = NOT_YET_RELEASED; void Reset() { @@ -87,7 +87,7 @@ CHANNELINDEX nMasterChn; ModCommand rowCommand; // 8-bit members - uint8 resamplingMode; + ResamplingMode resamplingMode; uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote uint8 nRestoreCutoffOnNewNote; // ditto uint8 nNote; diff -Nru libopenmpt-0.4.3/soundlib/ModInstrument.cpp libopenmpt-0.4.5/soundlib/ModInstrument.cpp --- libopenmpt-0.4.3/soundlib/ModInstrument.cpp 2018-09-15 13:07:34.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/ModInstrument.cpp 2019-03-08 20:52:17.000000000 +0000 @@ -163,13 +163,13 @@ nMixPlug = 0; nVolRampUp = 0; - nResampling = SRCMODE_DEFAULT; + resampling = SRCMODE_DEFAULT; nCutSwing = 0; nResSwing = 0; nFilterMode = FLTMODE_UNCHANGED; pitchToTempoLock.Set(0); - nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; - nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; + pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; + pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; pTuning = CSoundFile::GetDefaultTuning(); diff -Nru libopenmpt-0.4.3/soundlib/ModInstrument.h libopenmpt-0.4.5/soundlib/ModInstrument.h --- libopenmpt-0.4.3/soundlib/ModInstrument.h 2018-08-24 17:25:50.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/ModInstrument.h 2019-03-08 20:52:17.000000000 +0000 @@ -64,46 +64,46 @@ // Instrument Struct struct ModInstrument { - uint32 nFadeOut; // Instrument fadeout speed - uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) - uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. - - uint16 nVolRampUp; // Default sample ramping up, 0 = use global default - - uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send. - uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send. - uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). - uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader) - int8 midiPWD; // MIDI Pitch Wheel Depth in semitones - - FlagSet dwFlags; // Instrument flags - NewNoteAction nNNA; // New note action - DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action) - DuplicateNoteAction nDNA; // Duplicate note action - uint8 nPanSwing; // Random panning factor (0...64) - uint8 nVolSwing; // Random volume factor (0...100) - uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set - uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set - - int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) - uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1) - - PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) - uint8 nCutSwing; // Random cutoff factor (0...64) - uint8 nResSwing; // Random resonance factor (0...64) - InstrFilterMode nFilterMode; // Default filter mode - uint8 nPluginVelocityHandling; // How to deal with plugin velocity (PLUGIN_VELOCITYHANDLING_* constants) - uint8 nPluginVolumeHandling; // How to deal with plugin volume (PLUGIN_VOLUMEHANDLING_* constants) - TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) - uint32 nResampling; // Resampling mode (SRCMODE_* constants) - CTuning *pTuning; // sample tuning assigned to this instrument - - InstrumentEnvelope VolEnv; // Volume envelope data - InstrumentEnvelope PanEnv; // Panning envelope data - InstrumentEnvelope PitchEnv; // Pitch / filter envelope data + uint32 nFadeOut; // Instrument fadeout speed + uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) + uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. + + uint16 nVolRampUp; // Default sample ramping up, 0 = use global default + + uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send. + uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send. + uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). + uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader) + int8 midiPWD; // MIDI Pitch Wheel Depth in semitones + + FlagSet dwFlags; // Instrument flags + NewNoteAction nNNA; // New note action + DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action) + DuplicateNoteAction nDNA; // Duplicate note action + uint8 nPanSwing; // Random panning factor (0...64) + uint8 nVolSwing; // Random volume factor (0...100) + uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set + uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set + + int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) + uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1) + + PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) + uint8 nCutSwing; // Random cutoff factor (0...64) + uint8 nResSwing; // Random resonance factor (0...64) + InstrFilterMode nFilterMode; // Default filter mode + PlugVelocityHandling pluginVelocityHandling; // How to deal with plugin velocity + PlugVolumeHandling pluginVolumeHandling; // How to deal with plugin volume + ResamplingMode resampling; // Resampling mode + TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) + CTuning *pTuning; // sample tuning assigned to this instrument + + InstrumentEnvelope VolEnv; // Volume envelope data + InstrumentEnvelope PanEnv; // Panning envelope data + InstrumentEnvelope PitchEnv; // Pitch / filter envelope data - uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5. - SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1 + uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5. + SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1 char name[MAX_INSTRUMENTNAME]; char filename[MAX_INSTRUMENTFILENAME]; diff -Nru libopenmpt-0.4.3/soundlib/ModSample.cpp libopenmpt-0.4.5/soundlib/ModSample.cpp --- libopenmpt-0.4.3/soundlib/ModSample.cpp 2018-09-14 20:12:15.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/ModSample.cpp 2019-03-19 20:54:13.000000000 +0000 @@ -113,6 +113,11 @@ if(!CSoundFile::SupportsOPL(toType) && uFlags[CHN_ADLIB]) { SetAdlib(false); + } else if(toType == MOD_TYPE_S3M && uFlags[CHN_ADLIB]) + { + // No support for OPL3 waveforms in S3M + adlib[8] &= 0x03; + adlib[9] &= 0x03; } } diff -Nru libopenmpt-0.4.3/soundlib/ModSequence.cpp libopenmpt-0.4.5/soundlib/ModSequence.cpp --- libopenmpt-0.4.3/soundlib/ModSequence.cpp 2018-05-23 07:33:52.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/ModSequence.cpp 2019-04-07 15:31:28.000000000 +0000 @@ -342,27 +342,31 @@ Reporting::Confirm("The order list contains separator items.\nThe new format supports multiple sequences, do you want to convert those separate tracks into multiple song sequences?", "Order list conversion", false, true) == cnfYes) { - ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed(); + ORDERINDEX length = m_Sequences[0].GetLength(); for(ORDERINDEX ord = 0; ord < length; ord++) { // End of subsong? if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) { - // remove all separator patterns between current and next subsong first - while(ord < length && !m_sndFile.Patterns.IsValidIndex(m_Sequences[0][ord])) + // Remove all separator patterns between current and next subsong first + while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) { m_Sequences[0][ord] = GetInvalidPatIndex(); ord++; modified = true; } - if(ord >= length) break; - ORDERINDEX startOrd = ord; - modified = true; + if(ord >= length) + break; + + const SEQUENCEINDEX newSeq = AddSequence(false); + if(newSeq == SEQUENCEINDEX_INVALID) + break; - SEQUENCEINDEX newSeq = AddSequence(false); + const ORDERINDEX startOrd = ord; m_Sequences[newSeq].reserve(length - startOrd); + modified = true; - // now, move all following orders to the new sequence + // Now, move all following orders to the new sequence while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) { PATTERNINDEX copyPat = m_Sequences[0][ord]; @@ -370,7 +374,7 @@ m_Sequences[0][ord] = GetInvalidPatIndex(); ord++; - // is this a valid pattern? adjust pattern jump commands, if necessary. + // Is this a valid pattern? adjust pattern jump commands, if necessary. if(m_sndFile.Patterns.IsValidPat(copyPat)) { for(auto &m : m_sndFile.Patterns[copyPat]) @@ -382,6 +386,7 @@ } } } + ord--; } } SetSequence(0); diff -Nru libopenmpt-0.4.3/soundlib/SampleFormatFLAC.cpp libopenmpt-0.4.5/soundlib/SampleFormatFLAC.cpp --- libopenmpt-0.4.3/soundlib/SampleFormatFLAC.cpp 2018-12-22 23:46:00.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/SampleFormatFLAC.cpp 2019-03-22 17:40:56.000000000 +0000 @@ -196,8 +196,8 @@ if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6)) { mpt::ustring sampleName; - mpt::String::Read(sampleName, client.sndFile.GetCharsetInternal(), tag + 6, length - 6); - mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(mpt::CharsetUTF8, sampleName)); + mpt::String::Read(sampleName, mpt::CharsetUTF8, tag + 6, length - 6); + mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(client.sndFile.GetCharsetInternal(), sampleName)); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11)) { uint32 sampleRate = ConvertStrTo(tag + 11); diff -Nru libopenmpt-0.4.3/soundlib/SampleIO.cpp libopenmpt-0.4.5/soundlib/SampleIO.cpp --- libopenmpt-0.4.3/soundlib/SampleIO.cpp 2018-08-24 17:25:50.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/SampleIO.cpp 2019-03-31 12:10:53.000000000 +0000 @@ -1018,8 +1018,7 @@ MPT_ASSERT(len == numSamples); if(sample.uFlags[CHN_16BIT]) { - const int16 *const pSample16 = sample.sample16(); - const int16 *p = pSample16; + const int16 *p = sample.sample16(); int s_old = 0; const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; for(SmpLength j = 0; j < numSamples; j++) @@ -1028,7 +1027,7 @@ p++; if(sample.uFlags[CHN_STEREO]) { - s_new = (s_new + (static_cast(*p)) + 1) / 2; + s_new = (s_new + mpt::rshift_signed(*p, 8) + 1) / 2; p++; } if(GetEncoding() == deltaPCM) diff -Nru libopenmpt-0.4.3/soundlib/Snd_defs.h libopenmpt-0.4.5/soundlib/Snd_defs.h --- libopenmpt-0.4.3/soundlib/Snd_defs.h 2019-01-28 22:02:51.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Snd_defs.h 2019-03-08 20:52:17.000000000 +0000 @@ -366,14 +366,14 @@ }; // Plugin velocity handling options -enum PLUGVELOCITYHANDLING +enum PlugVelocityHandling : uint8 { PLUGIN_VELOCITYHANDLING_CHANNEL = 0, PLUGIN_VELOCITYHANDLING_VOLUME }; // Plugin volumecommand handling options -enum PLUGVOLUMEHANDLING +enum PlugVolumeHandling : uint8 { PLUGIN_VOLUMEHANDLING_MIDI = 0, PLUGIN_VOLUMEHANDLING_DRYWET, @@ -514,6 +514,7 @@ kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled + kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation) // Add new play behaviours here. diff -Nru libopenmpt-0.4.3/soundlib/Sndfile.cpp libopenmpt-0.4.5/soundlib/Sndfile.cpp --- libopenmpt-0.4.3/soundlib/Sndfile.cpp 2019-01-28 22:02:51.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Sndfile.cpp 2019-03-06 16:42:34.000000000 +0000 @@ -711,7 +711,7 @@ { #ifdef ENABLE_ASM #ifndef NO_REVERB - if(!(GetProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB; + if(!(GetRealProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB; #endif #endif m_MixerSettings.DSPMask = DSPMask; @@ -1163,7 +1163,6 @@ break; case MOD_TYPE_MOD: - playBehaviour.set(kMODSampleSwap); playBehaviour.set(kRowDelayWithNoteDelay); break; diff -Nru libopenmpt-0.4.3/soundlib/Snd_fx.cpp libopenmpt-0.4.5/soundlib/Snd_fx.cpp --- libopenmpt-0.4.3/soundlib/Snd_fx.cpp 2019-02-01 21:17:25.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Snd_fx.cpp 2019-05-24 21:22:03.000000000 +0000 @@ -1025,7 +1025,8 @@ if(m.note == NOTE_KEYOFF || m.note == NOTE_NOTECUT || (m.note == NOTE_FADE && GetNumInstruments()) || ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xC0 && paramLo < numTicks) - || (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks)) + || (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks) + || m.command == CMD_KEYOFF) { stopNote = true; } @@ -1452,7 +1453,6 @@ if(returnAfterVolumeAdjust) return; - // Instrument adjust chn.nNewIns = 0; @@ -4487,6 +4487,15 @@ { chn.nFineTune = MOD2XMFineTune(param); if(chn.nPeriod && chn.rowCommand.IsNote()) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + } else if(GetType() == MOD_TYPE_MTM) + { + if(chn.rowCommand.IsNote() && chn.pModSample != nullptr) + { + // Effect is permanent in MultiTracker + const_cast(chn.pModSample)->nFineTune = param; + chn.nFineTune = param; + if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + } } else if(chn.rowCommand.IsNote()) { chn.nFineTune = MOD2XMFineTune(param - 8); @@ -5578,7 +5587,7 @@ if(chn.position.GetUInt() > chn.nLength) { // Test case: SusAfterLoop.it - chn.position.Set(chn.position.GetInt() - chn.nLength + chn.nLoopStart); + chn.position.Set(chn.nLoopStart + ((chn.position.GetInt() - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart))); } } else { @@ -5657,7 +5666,7 @@ // ProTracker sets the tempo after the first tick. // Note: The case of one tick per row is handled in ProcessRow() instead. // Test case: TempoChange.mod -#if MPT_MSVC_AT_LEAST(2017,8) +#if MPT_MSVC_AT_LEAST(2017,8) && MPT_MSVC_BEFORE(2019,0) // Work-around MSVC getting confused about deduced const input type in noexcept operator inside noexcept condition. m_PlayState.m_nMusicTempo.SetRaw(std::min(param.GetRaw(), specs.GetTempoMax().GetRaw())); #else @@ -5837,15 +5846,18 @@ return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u)); //8363 * freq[note%12] / nC5Speed * 2^(5-note/12) } - } else if (GetType() == MOD_TYPE_XM) + } else if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM)) { if (note < 12) note = 12; note -= 12; - // FT2 Compatibility: The lower three bits of the finetune are truncated. - // Test case: Finetune-Precision.xm - if(m_playBehaviour[kFT2FinetunePrecision]) + if(GetType() == MOD_TYPE_MTM) + { + nFineTune *= 16; + } else if(m_playBehaviour[kFT2FinetunePrecision]) { + // FT2 Compatibility: The lower three bits of the finetune are truncated. + // Test case: Finetune-Precision.xm nFineTune &= ~7; } @@ -5892,7 +5904,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPeriodFrac) const { if (!period) return 0; - if (GetType() == MOD_TYPE_XM) + if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM)) { if(m_playBehaviour[kFT2Periods]) { diff -Nru libopenmpt-0.4.3/soundlib/Sndmix.cpp libopenmpt-0.4.5/soundlib/Sndmix.cpp --- libopenmpt-0.4.3/soundlib/Sndmix.cpp 2019-02-09 22:38:18.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/Sndmix.cpp 2019-05-24 20:38:19.000000000 +0000 @@ -1036,7 +1036,8 @@ // if we are in the release portion of the envelope, // rescale envelope factor so that it is proportional to the release point // and release envelope beginning. - if(chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED) + if(pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET + && chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED) { int envValueAtReleaseJump = chn.VolEnv.nEnvValueAtReleaseJump; int envValueAtReleaseNode = pIns->VolEnv[pIns->VolEnv.nReleaseNode].value * 4; @@ -2315,12 +2316,12 @@ if (chn.pCurrentSample || (chn.HasMIDIOutput() && !chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE])) { // Update VU-Meter (nRealVolume is 14-bit) - uint32 vul = (chn.nRealVolume * chn.nRealPan) / (1 << 14); + uint32 vul = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14); if (vul > 127) vul = 127; if (chn.nLeftVU > 127) chn.nLeftVU = (uint8)vul; vul /= 2; if (chn.nLeftVU < vul) chn.nLeftVU = (uint8)vul; - uint32 vur = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14); + uint32 vur = (chn.nRealVolume * chn.nRealPan) / (1 << 14); if (vur > 127) vur = 127; if (chn.nRightVU > 127) chn.nRightVU = (uint8)vur; vur /= 2; @@ -2394,13 +2395,13 @@ //if (chn.nNewRightVol > 0xFFFF) chn.nNewRightVol = 0xFFFF; //if (chn.nNewLeftVol > 0xFFFF) chn.nNewLeftVol = 0xFFFF; - if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->nResampling)) + if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->resampling)) { // For defined resampling modes, use per-instrument resampling mode if set - chn.resamplingMode = static_cast(chn.pModInstrument->nResampling); + chn.resamplingMode = chn.pModInstrument->resampling; } else if(Resampling::IsKnownMode(m_nResampling)) { - chn.resamplingMode = static_cast(m_nResampling); + chn.resamplingMode = m_nResampling; } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) { // Enforce Amiga resampler for Amiga modules @@ -2408,7 +2409,7 @@ } else { // Default to global mixer settings - chn.resamplingMode = static_cast(m_Resampler.m_Settings.SrcMode); + chn.resamplingMode = m_Resampler.m_Settings.SrcMode; } if(chn.increment.IsUnity() && !(chn.dwFlags[CHN_VIBRATO] || chn.nAutoVibDepth || chn.resamplingMode == SRCMODE_AMIGA)) @@ -2540,11 +2541,13 @@ if(note != NOTE_NONE) { int32 velocity = static_cast(4 * defaultVolume); - switch(pIns->nPluginVelocityHandling) + switch(pIns->pluginVelocityHandling) { case PLUGIN_VELOCITYHANDLING_CHANNEL: velocity = chn.nVolume; - break; + break; + default: + break; } int32 swing = chn.nVolSwing; @@ -2560,23 +2563,23 @@ SendMIDINote(nChn, realNote, static_cast(velocity)); } + const bool processVolumeAlsoOnNote = (pIns->pluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME); + const bool hasNote = m_playBehaviour[kMIDIVolumeOnNoteOffBug] ? (note != NOTE_NONE) : ModCommand::IsNote(note); - const bool processVolumeAlsoOnNote = (pIns->nPluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME); - - if((hasVolCommand && !note) || (note && processVolumeAlsoOnNote)) + if((hasVolCommand && !hasNote) || (hasNote && processVolumeAlsoOnNote)) { - switch(pIns->nPluginVolumeHandling) + switch(pIns->pluginVolumeHandling) { case PLUGIN_VOLUMEHANDLING_DRYWET: if(hasVolCommand) pPlugin->SetDryRatio(2 * vol); else pPlugin->SetDryRatio(2 * defaultVolume); break; - case PLUGIN_VOLUMEHANDLING_MIDI: if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(127u, 2u * vol), nChn); else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(127u, 2u * defaultVolume)), nChn); break; - + default: + break; } } } diff -Nru libopenmpt-0.4.3/soundlib/UpgradeModule.cpp libopenmpt-0.4.5/soundlib/UpgradeModule.cpp --- libopenmpt-0.4.3/soundlib/UpgradeModule.cpp 2019-01-28 22:02:51.000000000 +0000 +++ libopenmpt-0.4.5/soundlib/UpgradeModule.cpp 2019-03-08 20:52:17.000000000 +0000 @@ -413,6 +413,18 @@ } } + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 03, 04)) + { + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if (Instruments[i] != nullptr) + { + if(Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_MIDI || Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_DRYWET) + { + m_playBehaviour.set(kMIDIVolumeOnNoteOffBug); + break; + } + } + } + Patterns.ForEachModCommand(UpgradePatternData(*this)); // Convert compatibility flags diff -Nru libopenmpt-0.4.3/test/test.cpp libopenmpt-0.4.5/test/test.cpp --- libopenmpt-0.4.3/test/test.cpp 2019-01-19 22:34:55.000000000 +0000 +++ libopenmpt-0.4.5/test/test.cpp 2019-03-11 12:37:56.000000000 +0000 @@ -2598,6 +2598,41 @@ VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileExt(), P_(".txt")); VERIFY_EQUAL(P_("\\directory\\file.txt").GetFullFileName(), P_("file.txt")); + VERIFY_EQUAL(P_(".").GetDrive(), P_("")); + VERIFY_EQUAL(P_(".").GetDir(), P_("")); + VERIFY_EQUAL(P_(".").GetPath(), P_("")); + VERIFY_EQUAL(P_(".").GetFileName(), P_(".")); + VERIFY_EQUAL(P_(".").GetFileExt(), P_("")); + VERIFY_EQUAL(P_(".").GetFullFileName(), P_(".")); + + VERIFY_EQUAL(P_("..").GetDrive(), P_("")); + VERIFY_EQUAL(P_("..").GetDir(), P_("")); + VERIFY_EQUAL(P_("..").GetPath(), P_("")); + VERIFY_EQUAL(P_("..").GetFileName(), P_("..")); + VERIFY_EQUAL(P_("..").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("..").GetFullFileName(), P_("..")); + + VERIFY_EQUAL(P_("dir\\.").GetDrive(), P_("")); + VERIFY_EQUAL(P_("dir\\.").GetDir(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\.").GetPath(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\.").GetFileName(), P_(".")); + VERIFY_EQUAL(P_("dir\\.").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("dir\\.").GetFullFileName(), P_(".")); + + VERIFY_EQUAL(P_("dir\\..").GetDrive(), P_("")); + VERIFY_EQUAL(P_("dir\\..").GetDir(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\..").GetPath(), P_("dir\\")); + VERIFY_EQUAL(P_("dir\\..").GetFileName(), P_("..")); + VERIFY_EQUAL(P_("dir\\..").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("dir\\..").GetFullFileName(), P_("..")); + + VERIFY_EQUAL(P_(".txt").GetDrive(), P_("")); + VERIFY_EQUAL(P_(".txt").GetDir(), P_("")); + VERIFY_EQUAL(P_(".txt").GetPath(), P_("")); + VERIFY_EQUAL(P_(".txt").GetFileName(), P_(".txt")); + VERIFY_EQUAL(P_(".txt").GetFileExt(), P_("")); + VERIFY_EQUAL(P_(".txt").GetFullFileName(), P_(".txt")); + VERIFY_EQUAL(P_("C:tmp.txt").GetDrive(), P_("C:")); VERIFY_EQUAL(P_("C:tmp.txt").GetDir(), P_("")); VERIFY_EQUAL(P_("C:tmp.txt").GetPath(), P_("C:")); @@ -3079,7 +3114,7 @@ VERIFY_EQUAL_NONCONT(pIns->nPPC, NOTE_MIDDLEC - 1); VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); - VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_SINC8LP); + VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); @@ -3105,8 +3140,8 @@ VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(0, 0)); - VERIFY_EQUAL_NONCONT(pIns->nPluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); - VERIFY_EQUAL_NONCONT(pIns->nPluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); + VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); + VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); for(size_t i = sndFile.GetModSpecifications().noteMin; i < sndFile.GetModSpecifications().noteMax; i++) { @@ -3372,7 +3407,7 @@ VERIFY_EQUAL_NONCONT(pIns->nPPC, (NOTE_MIDDLEC - NOTE_MIN) + 6); // F#5 VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); - VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_SINC8LP); + VERIFY_EQUAL_NONCONT(pIns->resampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); @@ -3399,8 +3434,8 @@ VERIFY_EQUAL_NONCONT(pIns->pitchToTempoLock, TEMPO(130, 2000)); - VERIFY_EQUAL_NONCONT(pIns->nPluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); - VERIFY_EQUAL_NONCONT(pIns->nPluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); + VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME); + VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI); for(size_t i = 0; i < NOTE_MAX; i++) {