summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cowgill <jcowgill@debian.org>2019-07-15 23:43:22 +0100
committerJames Cowgill <jcowgill@debian.org>2019-07-15 23:43:22 +0100
commit1eb7116a256029ed6f5ce8d0ffac280adcbe8c35 (patch)
treeb8867e88955ad6c7195e6998c261c4e69664d054
parent2690ce9c090be73f3ba565e624bece14ced24878 (diff)
parentdc825bfd49ccba2594b05f2926234839182ed70b (diff)
Update upstream source from tag 'upstream/0.4.5'
Update to upstream version '0.4.5' with Debian dir dd5e0f3a68c5a79fc4c224020a9b8cedcbd36e32
-rw-r--r--Doxyfile.in3
-rw-r--r--Makefile.am3
-rw-r--r--Makefile.in13
-rw-r--r--README.md5
-rw-r--r--common/CompilerDetect.h6
-rw-r--r--common/mptAlloc.cpp4
-rw-r--r--common/mptExceptionText.h12
-rw-r--r--common/mptPathString.cpp8
-rw-r--r--common/mptString.h7
-rw-r--r--common/mptStringBuffer.h15
-rw-r--r--common/versionNumber.h2
-rwxr-xr-xconfigure26
-rw-r--r--configure.ac8
-rw-r--r--doc/contributing.md43
-rw-r--r--doc/libopenmpt_styleguide.md104
-rw-r--r--doc/module_formats.md106
-rw-r--r--libopenmpt/dox/changelog.md22
-rw-r--r--libopenmpt/dox/index.dox3
-rw-r--r--libopenmpt/libopenmpt_version.h2
-rw-r--r--libopenmpt/libopenmpt_version.mk4
-rw-r--r--man/openmpt123.12
-rw-r--r--soundbase/SampleFormat.h10
-rw-r--r--soundlib/AudioReadTarget.h4
-rw-r--r--soundlib/InstrumentExtensions.cpp35
-rw-r--r--soundlib/Load_mod.cpp66
-rw-r--r--soundlib/Load_mt2.cpp4
-rw-r--r--soundlib/Load_mtm.cpp22
-rw-r--r--soundlib/ModChannel.h6
-rw-r--r--soundlib/ModInstrument.cpp6
-rw-r--r--soundlib/ModInstrument.h80
-rw-r--r--soundlib/ModSample.cpp5
-rw-r--r--soundlib/ModSequence.cpp23
-rw-r--r--soundlib/SampleFormatFLAC.cpp4
-rw-r--r--soundlib/SampleIO.cpp5
-rw-r--r--soundlib/Snd_defs.h5
-rw-r--r--soundlib/Snd_fx.cpp30
-rw-r--r--soundlib/Sndfile.cpp3
-rw-r--r--soundlib/Sndmix.cpp33
-rw-r--r--soundlib/UpgradeModule.cpp12
-rw-r--r--test/test.cpp47
40 files changed, 607 insertions, 191 deletions
diff --git a/Doxyfile.in b/Doxyfile.in
index 8e34b66..15ab574 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -784,9 +784,12 @@ INPUT = libopenmpt/dox/index.dox \
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 --git a/Makefile.am b/Makefile.am
index 90a056d..f199bc2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,6 +6,9 @@ EXTRA_DIST += libmodplug/libmodplug.pc.in
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 --git a/Makefile.in b/Makefile.in
index 737ebf1..bd5fae5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1227,12 +1227,13 @@ top_srcdir = @top_srcdir@
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 --git a/README.md b/README.md
index 9c51819..a9d8d0a 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,8 @@
+
+README
+======
+
+
OpenMPT and libopenmpt
======================
diff --git a/common/CompilerDetect.h b/common/CompilerDetect.h
index e04565d..0e7e366 100644
--- a/common/CompilerDetect.h
+++ b/common/CompilerDetect.h
@@ -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 --git a/common/mptAlloc.cpp b/common/mptAlloc.cpp
index 2f072ae..362bd12 100644
--- a/common/mptAlloc.cpp
+++ b/common/mptAlloc.cpp
@@ -56,7 +56,7 @@ void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &sp
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 @@ aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::
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 --git a/common/mptExceptionText.h b/common/mptExceptionText.h
index 3566463..67fc443 100644
--- a/common/mptExceptionText.h
+++ b/common/mptExceptionText.h
@@ -28,7 +28,7 @@ OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
-template <typename T> T get_exception_text_impl(const std::exception & e) noexcept
+template <typename T> T get_exception_text_impl(const std::exception & e)
{
if(e.what() && (std::strlen(e.what()) > 0))
{
@@ -42,28 +42,28 @@ template <typename T> T get_exception_text_impl(const std::exception & e) noexce
}
}
-template <typename T> inline T get_exception_text(const std::exception & e) noexcept
+template <typename T> inline T get_exception_text(const std::exception & e)
{
return mpt::get_exception_text_impl<T>(e);
}
-template <> inline std::string get_exception_text<std::string>(const std::exception & e) noexcept
+template <> inline std::string get_exception_text<std::string>(const std::exception & e)
{
return mpt::get_exception_text_impl<std::string>(e);
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
-template <> inline mpt::lstring get_exception_text<mpt::lstring>(const std::exception & e) noexcept
+template <> inline mpt::lstring get_exception_text<mpt::lstring>(const std::exception & e)
{
return mpt::ToLocale(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl<std::string>(e));
}
#endif
#if MPT_WSTRING_FORMAT
-template <> inline std::wstring get_exception_text<std::wstring>(const std::exception & e) noexcept
+template <> inline std::wstring get_exception_text<std::wstring>(const std::exception & e)
{
return mpt::ToWide(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl<std::string>(e));
}
#endif
#if MPT_USTRING_MODE_UTF8
-template <> inline mpt::ustring get_exception_text<mpt::ustring>(const std::exception & e) noexcept
+template <> inline mpt::ustring get_exception_text<mpt::ustring>(const std::exception & e)
{
return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl<std::string>(e));
}
diff --git a/common/mptPathString.cpp b/common/mptPathString.cpp
index 541eb19..09876dd 100644
--- a/common/mptPathString.cpp
+++ b/common/mptPathString.cpp
@@ -224,6 +224,14 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname
{
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 --git a/common/mptString.h b/common/mptString.h
index d998f08..1e09e72 100644
--- a/common/mptString.h
+++ b/common/mptString.h
@@ -245,10 +245,17 @@ bool IsUTF8(const std::string &str);
#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 <mpt::Charset charset_tag>
diff --git a/common/mptStringBuffer.h b/common/mptStringBuffer.h
index cb1dc42..f0b7e59 100644
--- a/common/mptStringBuffer.h
+++ b/common/mptStringBuffer.h
@@ -185,7 +185,6 @@ public:
, mode(mode)
{
MPT_STATIC_ASSERT(sizeof(Tchar) == 1);
- MPT_ASSERT(size > 0);
}
StringModeBufRefImpl(const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl(StringModeBufRefImpl &&) = default;
@@ -218,7 +217,6 @@ public:
, mode(mode)
{
MPT_STATIC_ASSERT(sizeof(Tchar) == 1);
- MPT_ASSERT(size > 0);
}
StringModeBufRefImpl(const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl(StringModeBufRefImpl &&) = default;
@@ -400,6 +398,8 @@ namespace String
buffer[size - 1] = 0;
}
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+
template <size_t size>
void SetNullTerminator(wchar_t (&buffer)[size])
{
@@ -413,6 +413,8 @@ namespace String
buffer[size - 1] = 0;
}
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
// Remove any chars after the first null char
template <size_t size>
@@ -633,6 +635,11 @@ namespace String
}
+#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 <size_t destSize>
void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits<size_t>::max())
@@ -676,6 +683,10 @@ namespace String
dest.assign(src);
}
+#if MPT_GCC_AT_LEAST(8,1,0)
+#pragma GCC diagnostic pop
+#endif
+
#if MPT_COMPILER_MSVC
#pragma warning(pop)
diff --git a/common/versionNumber.h b/common/versionNumber.h
index 64d7080..62f7a9c 100644
--- a/common/versionNumber.h
+++ b/common/versionNumber.h
@@ -20,7 +20,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 28
-#define VER_MINOR 03
+#define VER_MINOR 05
#define VER_MINORMINOR 00
//Numerical value of the version.
diff --git a/configure b/configure
index 3b1ceda..83faeb6 100755
--- a/configure
+++ b/configure
@@ -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 <https://bugs.openmpt.org/>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# 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 @@ if test "$ac_init_help" = "long"; then
# 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 @@ fi
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 @@ fi
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 @@ cat >config.log <<_ACEOF
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 @@ fi
# 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 @@ LIBOPENMPT_LTVER_AGE=1
-$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 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# 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 @@ _ACEOF
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 --git a/configure.ac b/configure.ac
index e34b3db..697c124 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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_CURRENT])
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 --git a/doc/contributing.md b/doc/contributing.md
new file mode 100644
index 0000000..ee18940
--- /dev/null
+++ b/doc/contributing.md
@@ -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 <user@example.com>`. If
+you prefer to be attributed with your nickname and/or without your email
+address, that would also be fine for us.
diff --git a/doc/libopenmpt_styleguide.md b/doc/libopenmpt_styleguide.md
new file mode 100644
index 0000000..2a0a4fb
--- /dev/null
+++ b/doc/libopenmpt_styleguide.md
@@ -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 `<cstdio>` instead of
+ `<stdio.h>`.
+ - 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 --git a/doc/module_formats.md b/doc/module_formats.md
new file mode 100644
index 0000000..f93c6dc
--- /dev/null
+++ b/doc/module_formats.md
@@ -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 --git a/libopenmpt/dox/changelog.md b/libopenmpt/dox/changelog.md
index 75441c0..8aee41c 100644
--- a/libopenmpt/dox/changelog.md
+++ b/libopenmpt/dox/changelog.md
@@ -5,6 +5,28 @@ Changelog {#changelog}
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 --git a/libopenmpt/dox/index.dox b/libopenmpt/dox/index.dox
index d771321..17f8d7d 100644
--- a/libopenmpt/dox/index.dox
+++ b/libopenmpt/dox/index.dox
@@ -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 --git a/libopenmpt/libopenmpt_version.h b/libopenmpt/libopenmpt_version.h
index a99c9b3..787d442 100644
--- a/libopenmpt/libopenmpt_version.h
+++ b/libopenmpt/libopenmpt_version.h
@@ -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 --git a/libopenmpt/libopenmpt_version.mk b/libopenmpt/libopenmpt_version.mk
index 72706d4..6bcc6d8 100644
--- a/libopenmpt/libopenmpt_version.mk
+++ b/libopenmpt/libopenmpt_version.mk
@@ -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 --git a/man/openmpt123.1 b/man/openmpt123.1
index 7ba7639..a23663f 100644
--- a/man/openmpt123.1
+++ b/man/openmpt123.1
@@ -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 --git a/soundbase/SampleFormat.h b/soundbase/SampleFormat.h
index dbe34cf..c2ca79f 100644
--- a/soundbase/SampleFormat.h
+++ b/soundbase/SampleFormat.h
@@ -29,11 +29,11 @@ enum SampleFormatEnum
};
template<typename Tsample> struct SampleFormatTraits;
-template<> struct SampleFormatTraits<uint8> { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatUnsigned8; };
-template<> struct SampleFormatTraits<int16> { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt16; };
-template<> struct SampleFormatTraits<int24> { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt24; };
-template<> struct SampleFormatTraits<int32> { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatInt32; };
-template<> struct SampleFormatTraits<float> { static MPT_CONSTEXPR11_VAR SampleFormatEnum sampleFormat = SampleFormatFloat32; };
+template<> struct SampleFormatTraits<uint8> { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } };
+template<> struct SampleFormatTraits<int16> { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } };
+template<> struct SampleFormatTraits<int24> { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } };
+template<> struct SampleFormatTraits<int32> { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } };
+template<> struct SampleFormatTraits<float> { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } };
template<SampleFormatEnum sampleFormat> struct SampleFormatToType;
template<> struct SampleFormatToType<SampleFormatUnsigned8> { typedef uint8 type; };
diff --git a/soundlib/AudioReadTarget.h b/soundlib/AudioReadTarget.h
index e5da84a..3bb6f5d 100644
--- a/soundlib/AudioReadTarget.h
+++ b/soundlib/AudioReadTarget.h
@@ -40,7 +40,7 @@ public:
, outputBuffer(buffer)
, outputBuffers(buffers)
{
- MPT_ASSERT(SampleFormat(SampleFormatTraits<Tsample>::sampleFormat).IsValid());
+ MPT_ASSERT(SampleFormat(SampleFormatTraits<Tsample>::sampleFormat()).IsValid());
}
std::size_t GetRenderedCount() const { return countRendered; }
public:
@@ -48,7 +48,7 @@ public:
{
// Convert to output sample format and optionally perform dithering and clipping if needed
- const SampleFormat sampleFormat = SampleFormatTraits<Tsample>::sampleFormat;
+ const SampleFormat sampleFormat = SampleFormatTraits<Tsample>::sampleFormat();
if(sampleFormat.IsInt())
{
diff --git a/soundlib/InstrumentExtensions.cpp b/soundlib/InstrumentExtensions.cpp
index 10127bd..87bb171 100644
--- a/soundlib/InstrumentExtensions.cpp
+++ b/soundlib/InstrumentExtensions.cpp
@@ -120,9 +120,9 @@ PSB. PanEnv.nSustainStart;
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 @@ if(!writeAll)
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 @@ void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments
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 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
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 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
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<uint32>(fsize);
+ if(Resampling::IsKnownMode(tmp))
+ input->resampling = static_cast<ResamplingMode>(tmp);
+ result = true;
+ } break;
case MagicBE("PTTL"):
{
// Integer part of pitch/tempo lock
diff --git a/soundlib/Load_mod.cpp b/soundlib/Load_mod.cpp
index 47567da..073fd7b 100644
--- a/soundlib/Load_mod.cpp
+++ b/soundlib/Load_mod.cpp
@@ -345,23 +345,23 @@ MPT_BINARY_STRUCT(MODSampleHeader, 30)
// 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 @@ struct AMInstrument
sample.RelativeTone = static_cast<int8>(-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<int8>(-128 + i * 8); break; // Saw
- case 2: sample.sample8()[i] = i < 16 ? -128 : 127; break; // Square
- case 3: sample.sample8()[i] = mpt::random<int8>(rng); break; // Noise
+ case 0: p[i] = ModSinusTable[i * 2]; break; // Sine
+ case 1: p[i] = static_cast<int8>(-128 + i * 8); break; // Saw
+ case 2: p[i] = i < 16 ? -128 : 127; break; // Square
+ case 3: p[i] = mpt::random<int8>(rng); break; // Noise
}
}
}
@@ -492,7 +493,7 @@ MPT_BINARY_STRUCT(PT36InfoChunk, 64)
// 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 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
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 @@ static uint32 CountInvalidChars(const char (&name)[N])
// 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 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
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 --git a/soundlib/Load_mt2.cpp b/soundlib/Load_mt2.cpp
index 2a98b24..e256cb1 100644
--- a/soundlib/Load_mt2.cpp
+++ b/soundlib/Load_mt2.cpp
@@ -1060,7 +1060,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
{
if(drumSample[drum] == i && Instruments[drumMap[drum]] != nullptr)
{
- Instruments[drumMap[drum]]->nResampling = SRCMODE_NEAREST;
+ Instruments[drumMap[drum]]->resampling = SRCMODE_NEAREST;
}
}
}
@@ -1105,7 +1105,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
mptSmp.nFineTune = group.pitch;
if(sampleNoInterpolation[sample - 1])
{
- mptIns->nResampling = SRCMODE_NEAREST;
+ mptIns->resampling = SRCMODE_NEAREST;
}
if(sampleSynchronized[sample - 1])
{
diff --git a/soundlib/Load_mtm.cpp b/soundlib/Load_mtm.cpp
index 10140ba..3819602 100644
--- a/soundlib/Load_mtm.cpp
+++ b/soundlib/Load_mtm.cpp
@@ -58,8 +58,8 @@ struct MTMSampleHeader
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 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags)
{
// 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 --git a/soundlib/ModChannel.h b/soundlib/ModChannel.h
index 88c6df5..4cc1f7f 100644
--- a/soundlib/ModChannel.h
+++ b/soundlib/ModChannel.h
@@ -28,8 +28,8 @@ struct ModChannel
struct EnvInfo
{
FlagSet<EnvelopeFlags> flags;
- uint32 nEnvPosition;
- int32 nEnvValueAtReleaseJump;
+ uint32 nEnvPosition = 0;
+ int32 nEnvValueAtReleaseJump = NOT_YET_RELEASED;
void Reset()
{
@@ -87,7 +87,7 @@ struct ModChannel
CHANNELINDEX nMasterChn;
ModCommand rowCommand;
// 8-bit members
- uint8 resamplingMode;
+ ResamplingMode resamplingMode;
uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote
uint8 nRestoreCutoffOnNewNote; // ditto
uint8 nNote;
diff --git a/soundlib/ModInstrument.cpp b/soundlib/ModInstrument.cpp
index d5eb6e6..604e4ea 100644
--- a/soundlib/ModInstrument.cpp
+++ b/soundlib/ModInstrument.cpp
@@ -163,13 +163,13 @@ ModInstrument::ModInstrument(SAMPLEINDEX sample)
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 --git a/soundlib/ModInstrument.h b/soundlib/ModInstrument.h
index 8fc1bae..41c4b51 100644
--- a/soundlib/ModInstrument.h
+++ b/soundlib/ModInstrument.h
@@ -64,46 +64,46 @@ struct InstrumentEnvelope : public std::vector<EnvelopeNode>
// 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<InstrumentFlags> 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
-
- uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5.
- SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1
+ 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<InstrumentFlags> 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
char name[MAX_INSTRUMENTNAME];
char filename[MAX_INSTRUMENTFILENAME];
diff --git a/soundlib/ModSample.cpp b/soundlib/ModSample.cpp
index 73a54ae..05a0117 100644
--- a/soundlib/ModSample.cpp
+++ b/soundlib/ModSample.cpp
@@ -113,6 +113,11 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
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 --git a/soundlib/ModSequence.cpp b/soundlib/ModSequence.cpp
index 5036913..15c3945 100644
--- a/soundlib/ModSequence.cpp
+++ b/soundlib/ModSequence.cpp
@@ -342,27 +342,31 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
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 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
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 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
}
}
}
+ ord--;
}
}
SetSequence(0);
diff --git a/soundlib/SampleFormatFLAC.cpp b/soundlib/SampleFormatFLAC.cpp
index 09aed0a..63de71c 100644
--- a/soundlib/SampleFormatFLAC.cpp
+++ b/soundlib/SampleFormatFLAC.cpp
@@ -196,8 +196,8 @@ struct FLACDecoder
if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6))
{
mpt::ustring sampleName;
- mpt::String::Read<mpt::String::maybeNullTerminated>(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<mpt::String::maybeNullTerminated>(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<uint32>(tag + 11);
diff --git a/soundlib/SampleIO.cpp b/soundlib/SampleIO.cpp
index bb371b2..f7cf942 100644
--- a/soundlib/SampleIO.cpp
+++ b/soundlib/SampleIO.cpp
@@ -1018,8 +1018,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength
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 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength
p++;
if(sample.uFlags[CHN_STEREO])
{
- s_new = (s_new + (static_cast<int>(*p)) + 1) / 2;
+ s_new = (s_new + mpt::rshift_signed(*p, 8) + 1) / 2;
p++;
}
if(GetEncoding() == deltaPCM)
diff --git a/soundlib/Snd_defs.h b/soundlib/Snd_defs.h
index 7522582..02f21d0 100644
--- a/soundlib/Snd_defs.h
+++ b/soundlib/Snd_defs.h
@@ -366,14 +366,14 @@ enum PluginMutePriority
};
// 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 @@ enum PlayBehaviour
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 --git a/soundlib/Snd_fx.cpp b/soundlib/Snd_fx.cpp
index a2b0b87..2da930a 100644
--- a/soundlib/Snd_fx.cpp
+++ b/soundlib/Snd_fx.cpp
@@ -1025,7 +1025,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
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 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo
if(returnAfterVolumeAdjust) return;
-
// Instrument adjust
chn.nNewIns = 0;
@@ -4487,6 +4487,15 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
{
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<ModSample *>(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 @@ void CSoundFile::KeyOff(ModChannel &chn) const
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 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI)
// 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 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
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::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
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 --git a/soundlib/Sndfile.cpp b/soundlib/Sndfile.cpp
index 2481b17..45e0ed6 100644
--- a/soundlib/Sndfile.cpp
+++ b/soundlib/Sndfile.cpp
@@ -711,7 +711,7 @@ void CSoundFile::SetDspEffects(uint32 DSPMask)
{
#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 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type)
break;
case MOD_TYPE_MOD:
- playBehaviour.set(kMODSampleSwap);
playBehaviour.set(kRowDelayWithNoteDelay);
break;
diff --git a/soundlib/Sndmix.cpp b/soundlib/Sndmix.cpp
index 566441e..7f51ccf 100644
--- a/soundlib/Sndmix.cpp
+++ b/soundlib/Sndmix.cpp
@@ -1036,7 +1036,8 @@ void CSoundFile::ProcessVolumeEnvelope(ModChannel &chn, int &vol) const
// 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 @@ bool CSoundFile::ReadNote()
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 @@ bool CSoundFile::ReadNote()
//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<uint8>(chn.pModInstrument->nResampling);
+ chn.resamplingMode = chn.pModInstrument->resampling;
} else if(Resampling::IsKnownMode(m_nResampling))
{
- chn.resamplingMode = static_cast<uint8>(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 @@ bool CSoundFile::ReadNote()
} else
{
// Default to global mixer settings
- chn.resamplingMode = static_cast<uint8>(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 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
if(note != NOTE_NONE)
{
int32 velocity = static_cast<int32>(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 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
SendMIDINote(nChn, realNote, static_cast<uint16>(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<uint8>(127u, 2u * vol), nChn);
else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast<uint8>(std::min<uint32>(127u, 2u * defaultVolume)), nChn);
break;
-
+ default:
+ break;
}
}
}
diff --git a/soundlib/UpgradeModule.cpp b/soundlib/UpgradeModule.cpp
index 33e70af..c614d58 100644
--- a/soundlib/UpgradeModule.cpp
+++ b/soundlib/UpgradeModule.cpp
@@ -413,6 +413,18 @@ void CSoundFile::UpgradeModule()
}
}
+ 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 --git a/test/test.cpp b/test/test.cpp
index 2863c5b..540a200 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -2598,6 +2598,41 @@ static MPT_NOINLINE void TestCharsets()
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 @@ static void TestLoadXMFile(const CSoundFile &sndFile)
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 @@ static void TestLoadXMFile(const CSoundFile &sndFile)
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 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
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 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
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++)
{