Updated libopenmpt to version 0.4.5

CQTexperiment
Christopher Snowhill 2019-06-20 20:17:10 -07:00
parent c5001ddf96
commit a6b0e0e8ae
38 changed files with 592 additions and 165 deletions

View File

@ -1204,6 +1204,7 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin
rm -rf bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)
mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)
mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build
mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc
mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include
svn export ./LICENSE bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE
svn export ./README.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md
@ -1213,6 +1214,9 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin
svn export ./build/make bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/make
svn export ./build/svn_version bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version
svn export ./common bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common
svn export ./doc/contributing.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md
svn export ./doc/libopenmpt_styleguide.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/libopenmpt_styleguide.md
svn export ./doc/module_formats.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/module_formats.md
svn export ./soundbase bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase
svn export ./soundlib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib
svn export ./sounddsp bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp
@ -1239,6 +1243,7 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn
mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build
mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie
mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake
mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc
mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include
svn export ./LICENSE bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE --native-eol CRLF
svn export ./README.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md --native-eol CRLF
@ -1260,6 +1265,9 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn
svn export ./build/winstore82 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/winstore82 --native-eol CRLF
svn export ./build/download_externals.cmd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/download_externals.cmd --native-eol CRLF
svn export ./common bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common --native-eol CRLF
svn export ./doc/contributing.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md --native-eol CRLF
svn export ./doc/libopenmpt_styleguide.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/libopenmpt_styleguide.md --native-eol CRLF
svn export ./doc/module_formats.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/module_formats.md --native-eol CRLF
svn export ./soundbase bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase --native-eol CRLF
svn export ./soundlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib --native-eol CRLF
svn export ./sounddsp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp --native-eol CRLF

View File

@ -1,3 +1,8 @@
README
======
OpenMPT and libopenmpt
======================

View File

@ -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)

View File

@ -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);

View File

@ -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));
}

View File

@ -221,6 +221,14 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname
}
mpt::RawPathString::size_type last_dot = p.find_last_of(PL_("."));
if(last_dot == mpt::RawPathString::npos)
{
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();

View File

@ -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>

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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
~~~~

View File

@ -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`.

View File

@ -1,6 +1,6 @@
This folder contains the stb_vorbis library from
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.15
commit 59e9702be5bfc0061e756c7beab7dcd3d363400d (2019-02-07)
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.16
commit 2c2908f50515dcd939f24be261c3ccbcd277bb49 (2019-03-05)
Modifications:
* Use of alloca has been replaced with malloc, as alloca is not in C99 and

View File

@ -1,4 +1,4 @@
// Ogg Vorbis audio decoder - v1.15 - public domain
// Ogg Vorbis audio decoder - v1.16 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
@ -33,6 +33,7 @@
// Timur Gagiev
//
// Partial history:
// 1.16 - 2019-03-04 - fix warnings
// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
// 1.14 - 2018-02-11 - delete bogus dealloca usage
// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
@ -5004,7 +5005,13 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, con
stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
{
FILE *f = fopen(filename, "rb");
FILE *f;
#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
if (0 != fopen_s(&f, filename, "rb"))
f = NULL;
#else
f = fopen(filename, "rb");
#endif
if (f)
return stb_vorbis_open_file(f, TRUE, error, alloc);
if (error) *error = VORBIS_file_open_failure;

View File

@ -785,9 +785,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 \

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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; };

View File

@ -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())
{

View File

@ -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

View File

@ -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.

View File

@ -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])
{

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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.
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 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
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
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)
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
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
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];

View File

@ -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;
}
}

View File

@ -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;
if(ord >= length)
break;
const SEQUENCEINDEX newSeq = AddSequence(false);
if(newSeq == SEQUENCEINDEX_INVALID)
break;
const ORDERINDEX startOrd = ord;
m_Sequences[newSeq].reserve(length - startOrd);
modified = true;
SEQUENCEINDEX newSeq = AddSequence(false);
m_Sequences[newSeq].reserve(length - startOrd);
// 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);

View File

@ -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);

View File

@ -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)

View File

@ -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.

View File

@ -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])
{

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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++)
{