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