Updated libOpenMPT to version 0.7.11

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2024-10-30 23:57:33 -07:00
parent 820a1dedfb
commit db5b4e68c2
61 changed files with 684 additions and 336 deletions

View file

@ -66,6 +66,7 @@
# ONLY_TEST=0 Only build the test suite. # ONLY_TEST=0 Only build the test suite.
# STRICT=0 Treat warnings as errors. # STRICT=0 Treat warnings as errors.
# MODERN=0 Pass more modern compiler options. # MODERN=0 Pass more modern compiler options.
# ANCIENT=0 Pass compiler options compatible with older versions.
# NATIVE=0 Optimize for system CPU. # NATIVE=0 Optimize for system CPU.
# STDCXX=c++17 C++ standard version (default depends on compiler) # STDCXX=c++17 C++ standard version (default depends on compiler)
# STDC=c17 C standard version (default depends on compiler) # STDC=c17 C standard version (default depends on compiler)
@ -975,6 +976,7 @@ endif
CPPCHECK_FLAGS += -j $(NUMTHREADS) CPPCHECK_FLAGS += -j $(NUMTHREADS)
CPPCHECK_FLAGS += --std=c11 --std=c++17 CPPCHECK_FLAGS += --std=c11 --std=c++17
CPPCHECK_FLAGS += --library=build/cppcheck/glibc-workarounds.cfg
CPPCHECK_FLAGS += --quiet CPPCHECK_FLAGS += --quiet
CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]'
CPPCHECK_FLAGS += --check-level=exhaustive CPPCHECK_FLAGS += --check-level=exhaustive
@ -1083,11 +1085,15 @@ CPPFLAGS += -DLIBOPENMPT_BUILD
COMMON_CXX_SOURCES += \ COMMON_CXX_SOURCES += \
$(sort $(wildcard src/openmpt/all/*.cpp)) \
$(sort $(wildcard src/openmpt/base/*.cpp)) \
$(sort $(wildcard src/openmpt/logging/*.cpp)) \
$(sort $(wildcard src/openmpt/random/*.cpp)) \
$(sort $(wildcard common/*.cpp)) \ $(sort $(wildcard common/*.cpp)) \
$(sort $(wildcard src/mpt/src/*.cpp)) \
SOUNDLIB_CXX_SOURCES += \ SOUNDLIB_CXX_SOURCES += \
$(COMMON_CXX_SOURCES) \ $(COMMON_CXX_SOURCES) \
$(sort $(wildcard src/openmpt/soundbase/*.cpp)) \
$(sort $(wildcard soundlib/*.cpp)) \ $(sort $(wildcard soundlib/*.cpp)) \
$(sort $(wildcard soundlib/plugins/*.cpp)) \ $(sort $(wildcard soundlib/plugins/*.cpp)) \
$(sort $(wildcard soundlib/plugins/dmo/*.cpp)) \ $(sort $(wildcard soundlib/plugins/dmo/*.cpp)) \
@ -1731,6 +1737,7 @@ bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/$
svn export ./build/scriptlib bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/scriptlib --native-eol CRLF svn export ./build/scriptlib bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/scriptlib --native-eol CRLF
svn export ./build/svn_version bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version --native-eol CRLF svn export ./build/svn_version bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version --native-eol CRLF
svn export ./build/vs bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs --native-eol CRLF svn export ./build/vs bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs --native-eol CRLF
svn export ./build/vs2017winxpansi bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017winxpansi --native-eol CRLF
svn export ./build/vs2017winxp bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017winxp --native-eol CRLF svn export ./build/vs2017winxp bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017winxp --native-eol CRLF
svn export ./build/vs2019win7 bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win7 --native-eol CRLF svn export ./build/vs2019win7 bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win7 --native-eol CRLF
svn export ./build/vs2019win81 bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win81 --native-eol CRLF svn export ./build/vs2019win81 bin/$(FLAVOUR_DIR)dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win81 --native-eol CRLF
@ -1866,6 +1873,11 @@ else
svn export ./include/miniz/miniz.c bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIZ.TXT --native-eol CRLF svn export ./include/miniz/miniz.c bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIZ.TXT --native-eol CRLF
svn export ./include/stb_vorbis/stb_vorbis.c bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/STBVORB.TXT --native-eol CRLF svn export ./include/stb_vorbis/stb_vorbis.c bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/STBVORB.TXT --native-eol CRLF
endif endif
mkdir -p bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/DJGPP
cp $(shell dirname $(shell which i386-pc-msdosdjgpp-gcc))/../license/copying bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/DJGPP/COPYING
cp $(shell dirname $(shell which i386-pc-msdosdjgpp-gcc))/../license/copying.dj bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/DJGPP/COPYING.DJ
cp $(shell dirname $(shell which i386-pc-msdosdjgpp-gcc))/../license/copying.lib bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/DJGPP/COPYING.LIB
cp $(shell dirname $(shell which i386-pc-msdosdjgpp-gcc))/../license/source.txt bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/DJGPP/SOURCE.TXT
mkdir -p bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC mkdir -p bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC
cp build/externals/csdpmi7s.zip bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC/CSDPMI7S.ZIP cp build/externals/csdpmi7s.zip bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC/CSDPMI7S.ZIP
mkdir -p bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN mkdir -p bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN

View file

@ -6,8 +6,45 @@ include $(CLEAR_VARS)
LOCAL_MODULE := openmpt LOCAL_MODULE := openmpt
ifeq ($(NDK_MAJOR),)
LOCAL_CFLAGS += -std=c17 LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++17 -fexceptions -frtti LOCAL_CPPFLAGS += -std=c++17
else
ifeq ($(NDK_MAJOR),21)
# clang 9
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++17
else ifeq ($(NDK_MAJOR),22)
# clang 11
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else ifeq ($(NDK_MAJOR),23)
# clang 12
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else ifeq ($(NDK_MAJOR),24)
# clang 14
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else ifeq ($(NDK_MAJOR),25)
# clang 14
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else ifeq ($(NDK_MAJOR),26)
# clang 17
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else ifeq ($(NDK_MAJOR),27)
# clang 18
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
else
LOCAL_CFLAGS += -std=c17
LOCAL_CPPFLAGS += -std=c++20
endif
endif
LOCAL_CPPFLAGS += -fexceptions -frtti
LOCAL_CPP_FEATURES += exceptions rtti LOCAL_CPP_FEATURES += exceptions rtti

View file

@ -1,6 +1,42 @@
ifeq ($(NDK_MAJOR),)
APP_CFLAGS := -std=c17 APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++17 -fexceptions -frtti APP_CPPFLAGS := -std=c++17 -fexceptions -frtti
else
ifeq ($(NDK_MAJOR),21)
# clang 9
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++17 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),22)
# clang 11
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),23)
# clang 12
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),24)
# clang 14
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),25)
# clang 14
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),26)
# clang 17
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else ifeq ($(NDK_MAJOR),27)
# clang 18
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
else
APP_CFLAGS := -std=c17
APP_CPPFLAGS := -std=c++20 -fexceptions -frtti
endif
endif
APP_LDFLAGS := APP_LDFLAGS :=
APP_STL := c++_shared APP_STL := c++_shared

View file

@ -1,4 +1,4 @@
MPT_SVNVERSION=21223 MPT_SVNVERSION=21953
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9 MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.11
MPT_SVNDATE=2024-07-21T12:01:13.335584Z MPT_SVNDATE=2024-10-26T13:09:27.804941Z

View file

@ -1,9 +1,9 @@
ifeq ($(origin CC),default) ifeq ($(origin CC),default)
CC = emcc -c CC = emcc
endif endif
ifeq ($(origin CXX),default) ifeq ($(origin CXX),default)
CXX = em++ -c CXX = em++
endif endif
ifeq ($(origin LD),default) ifeq ($(origin LD),default)
LD = em++ LD = em++

View file

@ -2,8 +2,13 @@
CXXFLAGS_WARNINGS += -Wcast-align -Wcast-qual -Wdouble-promotion -Wfloat-conversion -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-op-parentheses -Wshift-overflow -Wshift-sign-overflow -Wundef CXXFLAGS_WARNINGS += -Wcast-align -Wcast-qual -Wdouble-promotion -Wfloat-conversion -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-op-parentheses -Wshift-overflow -Wshift-sign-overflow -Wundef
CFLAGS_WARNINGS += -Wcast-align -Wcast-qual -Wdouble-promotion -Wfloat-conversion -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-op-parentheses -Wshift-overflow -Wshift-sign-overflow -Wundef CFLAGS_WARNINGS += -Wcast-align -Wcast-qual -Wdouble-promotion -Wfloat-conversion -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-op-parentheses -Wshift-overflow -Wshift-sign-overflow -Wundef
CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wframe-larger-than=16000 -Wglobal-constructors -Wimplicit-fallthrough -Wmissing-declarations -Wnon-virtual-dtor -Wreserved-id-macro CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wglobal-constructors -Wimplicit-fallthrough -Wmissing-declarations -Wnon-virtual-dtor -Wreserved-id-macro
CFLAGS_WARNINGS +=
ifneq ($(ANCIENT),1)
CXXFLAGS_WARNINGS += -Wframe-larger-than=16000
CFLAGS_WARNINGS += -Wframe-larger-than=4000 CFLAGS_WARNINGS += -Wframe-larger-than=4000
endif
#CXXFLAGS_WARNINGS += -Wfloat-equal #CXXFLAGS_WARNINGS += -Wfloat-equal
#CXXFLAGS_WARNINGS += -Wdocumentation #CXXFLAGS_WARNINGS += -Wdocumentation
@ -21,10 +26,14 @@ CFLAGS_SILENT += -Wno-cast-align
CFLAGS_SILENT += -Wno-cast-qual CFLAGS_SILENT += -Wno-cast-qual
CFLAGS_SILENT += -Wno-double-promotion CFLAGS_SILENT += -Wno-double-promotion
CFLAGS_SILENT += -Wno-float-conversion CFLAGS_SILENT += -Wno-float-conversion
ifneq ($(ANCIENT),1)
CFLAGS_SILENT += -Wno-frame-larger-than CFLAGS_SILENT += -Wno-frame-larger-than
endif
CFLAGS_SILENT += -Wno-missing-prototypes CFLAGS_SILENT += -Wno-missing-prototypes
CFLAGS_SILENT += -Wno-sign-compare CFLAGS_SILENT += -Wno-sign-compare
ifneq ($(ANCIENT),1)
CFLAGS_SILENT += -Wno-unused-but-set-variable CFLAGS_SILENT += -Wno-unused-but-set-variable
endif
CFLAGS_SILENT += -Wno-unused-function CFLAGS_SILENT += -Wno-unused-function
CFLAGS_SILENT += -Wno-unused-parameter CFLAGS_SILENT += -Wno-unused-parameter
CFLAGS_SILENT += -Wno-unused-variable CFLAGS_SILENT += -Wno-unused-variable

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#define OPENMPT_VERSION_SVNVERSION "21223" #define OPENMPT_VERSION_SVNVERSION "21953"
#define OPENMPT_VERSION_REVISION 21223 #define OPENMPT_VERSION_REVISION 21953
#define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9" #define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.11"
#define OPENMPT_VERSION_DATE "2024-07-21T12:01:13.335584Z" #define OPENMPT_VERSION_DATE "2024-10-26T13:09:27.804941Z"
#define OPENMPT_VERSION_IS_PACKAGE 1 #define OPENMPT_VERSION_IS_PACKAGE 1

View file

@ -20,6 +20,7 @@
#include "mpt/base/detect_arch.hpp"
#include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_os.hpp" #include "mpt/base/detect_os.hpp"
#include "mpt/base/detect_quirks.hpp" #include "mpt/base/detect_quirks.hpp"
@ -229,14 +230,14 @@
#endif #endif
#if defined(MPT_ENABLE_ARCH_INTRINSICS) #if defined(MPT_ENABLE_ARCH_INTRINSICS)
#if MPT_COMPILER_MSVC && defined(_M_IX86) #if MPT_COMPILER_MSVC && MPT_ARCH_X86
#define MPT_ENABLE_ARCH_X86 #define MPT_ENABLE_ARCH_X86
#define MPT_ENABLE_ARCH_INTRINSICS_SSE #define MPT_ENABLE_ARCH_INTRINSICS_SSE
#define MPT_ENABLE_ARCH_INTRINSICS_SSE2 #define MPT_ENABLE_ARCH_INTRINSICS_SSE2
#elif MPT_COMPILER_MSVC && defined(_M_X64) #elif MPT_COMPILER_MSVC && MPT_ARCH_AMD64
#define MPT_ENABLE_ARCH_AMD64 #define MPT_ENABLE_ARCH_AMD64

View file

@ -18,6 +18,8 @@
#include "mpt/base/array.hpp" #include "mpt/base/array.hpp"
#include "mpt/base/bit.hpp" #include "mpt/base/bit.hpp"
#include "mpt/base/constexpr_throw.hpp" #include "mpt/base/constexpr_throw.hpp"
#include "mpt/base/detect_arch.hpp"
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/math.hpp" #include "mpt/base/math.hpp"
#include "mpt/base/memory.hpp" #include "mpt/base/memory.hpp"
#include "mpt/base/numeric.hpp" #include "mpt/base/numeric.hpp"
@ -115,7 +117,7 @@ namespace Util {
// MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul.
MPT_CONSTEXPR20_FUN int64 mul32to64(int32 a, int32 b) MPT_CONSTEXPR20_FUN int64 mul32to64(int32 a, int32 b)
{ {
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) #if MPT_COMPILER_MSVC && (MPT_ARCH_X86 || MPT_ARCH_AMD64)
MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
{ {
return static_cast<int64>(a) * b; return static_cast<int64>(a) * b;
@ -130,7 +132,7 @@ namespace Util {
MPT_CONSTEXPR20_FUN uint64 mul32to64_unsigned(uint32 a, uint32 b) MPT_CONSTEXPR20_FUN uint64 mul32to64_unsigned(uint32 a, uint32 b)
{ {
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) #if MPT_COMPILER_MSVC && (MPT_ARCH_X86 || MPT_ARCH_AMD64)
MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
{ {
return static_cast<uint64>(a) * b; return static_cast<uint64>(a) * b;

View file

@ -615,9 +615,11 @@ mpt::ustring GetFullCreditsString()
"\n" "\n"
"Additional contributors:\n" "Additional contributors:\n"
"coda (https://coda.s3m.us/)\n" "coda (https://coda.s3m.us/)\n"
"cs127 (https://cs127.github.io/)\n"
"Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n" "Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n"
"kode54 (https://kode54.net/)\n" "kode54 (https://kode54.net/)\n"
"Revenant (https://revenant1.net/)\n" "Revenant (https://revenant1.net/)\n"
"SYRiNX\n"
"xaimus (http://xaimus.com/)\n" "xaimus (http://xaimus.com/)\n"
"\n" "\n"
"Thanks to:\n" "Thanks to:\n"

View file

@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN
// Version definitions. The only thing that needs to be changed when changing version number. // Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1 #define VER_MAJORMAJOR 1
#define VER_MAJOR 31 #define VER_MAJOR 31
#define VER_MINOR 09 #define VER_MINOR 11
#define VER_MINORMINOR 00 #define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END OPENMPT_NAMESPACE_END

View file

@ -1,6 +1,12 @@
minimp3 library from https://github.com/lieff/minimp3 minimp3 library from https://github.com/lieff/minimp3
commit 50d2aaf360a53653b718fead8e258d654c3a7e41 (2021-11-27) Fork https://github.com/manxorist/minimp3/releases/tag/openmpt-2024-08-15-v4
commit 2116754771b79347ad2f39127abace2a093c383e (2024-08-15)
The following changes have been made: The following changes have been made:
* minimp3.c has been added * minimp3.c has been added
* some warnings have been fixed * The following pull rquests have been merged:
* https://github.com/lieff/minimp3/pull/126
* https://github.com/lieff/minimp3/pull/96
* https://github.com/lieff/minimp3/pull/97
* https://github.com/lieff/minimp3/pull/125
* https://github.com/lieff/minimp3/pull/127
* all modifications are marked by /* OpenMPT */ * all modifications are marked by /* OpenMPT */

View file

@ -8,6 +8,10 @@
*/ */
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define MINIMP3_MAX_SAMPLES_PER_FRAME (1152*2) #define MINIMP3_MAX_SAMPLES_PER_FRAME (1152*2)
typedef struct typedef struct
@ -22,10 +26,6 @@ typedef struct
unsigned char header[4], reserv_buf[511]; unsigned char header[4], reserv_buf[511];
} mp3dec_t; } mp3dec_t;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void mp3dec_init(mp3dec_t *dec); void mp3dec_init(mp3dec_t *dec);
#ifndef MINIMP3_FLOAT_OUTPUT #ifndef MINIMP3_FLOAT_OUTPUT
typedef int16_t mp3d_sample_t; typedef int16_t mp3d_sample_t;
@ -176,11 +176,7 @@ end:
#define VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) #define VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
#define VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) #define VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
typedef float32x4_t f4; typedef float32x4_t f4;
#if 1 /* OpenMPT */
static int have_simd(void) static int have_simd(void)
#else /* OpenMPT */
static int have_simd()
#endif /* OpenMPT */
{ /* TODO: detect neon for !MINIMP3_ONLY_SIMD */ { /* TODO: detect neon for !MINIMP3_ONLY_SIMD */
return 1; return 1;
} }
@ -195,7 +191,7 @@ static int have_simd()
#define HAVE_SIMD 0 #define HAVE_SIMD 0
#endif /* !defined(MINIMP3_NO_SIMD) */ #endif /* !defined(MINIMP3_NO_SIMD) */
#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__)
#define HAVE_ARMV6 1 #define HAVE_ARMV6 1
static __inline__ __attribute__((always_inline)) int32_t minimp3_clip_int16_arm(int32_t a) static __inline__ __attribute__((always_inline)) int32_t minimp3_clip_int16_arm(int32_t a)
{ {
@ -945,7 +941,8 @@ static void L3_stereo_top_band(const float *right, const uint8_t *sfb, int nband
static void L3_stereo_process(float *left, const uint8_t *ist_pos, const uint8_t *sfb, const uint8_t *hdr, int max_band[3], int mpeg2_sh) static void L3_stereo_process(float *left, const uint8_t *ist_pos, const uint8_t *sfb, const uint8_t *hdr, int max_band[3], int mpeg2_sh)
{ {
static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 };
unsigned i, max_pos = HDR_TEST_MPEG1(hdr) ? 7 : 64; const uint8_t mpeg1 = HDR_TEST_MPEG1(hdr);
unsigned i, max_pos = mpeg1 ? 7 : 64;
for (i = 0; sfb[i]; i++) for (i = 0; sfb[i]; i++)
{ {
@ -953,7 +950,7 @@ static void L3_stereo_process(float *left, const uint8_t *ist_pos, const uint8_t
if ((int)i > max_band[i % 3] && ipos < max_pos) if ((int)i > max_band[i % 3] && ipos < max_pos)
{ {
float kl, kr, s = HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; float kl, kr, s = HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
if (HDR_TEST_MPEG1(hdr)) if (mpeg1)
{ {
kl = g_pan[2*ipos]; kl = g_pan[2*ipos];
kr = g_pan[2*ipos + 1]; kr = g_pan[2*ipos + 1];
@ -1658,6 +1655,21 @@ static void mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int n
} }
} }
static int hdr_is_tag(const uint8_t* hdr)
{
return hdr[0] == 'T' && hdr[1] == 'A' && hdr[2] == 'G' && hdr[3] == '\0';
}
static int hdr_is_null(const uint8_t* hdr)
{
return hdr[0] == '\0' && hdr[1] == '\0' && hdr[2] == '\0' && hdr[3] == '\0';
}
static int hdr_is_null_or_tag(const uint8_t* hdr)
{
return hdr_is_tag(hdr) > 0 || hdr_is_null(hdr) > 0;
}
static int mp3d_match_frame(const uint8_t *hdr, int mp3_bytes, int frame_bytes) static int mp3d_match_frame(const uint8_t *hdr, int mp3_bytes, int frame_bytes)
{ {
int i, nmatch; int i, nmatch;
@ -1666,6 +1678,8 @@ static int mp3d_match_frame(const uint8_t *hdr, int mp3_bytes, int frame_bytes)
i += hdr_frame_bytes(hdr + i, frame_bytes) + hdr_padding(hdr + i); i += hdr_frame_bytes(hdr + i, frame_bytes) + hdr_padding(hdr + i);
if (i + HDR_SIZE > mp3_bytes) if (i + HDR_SIZE > mp3_bytes)
return nmatch > 0; return nmatch > 0;
if (hdr_is_null_or_tag(hdr + i))
return nmatch > 0;
if (!hdr_compare(hdr, hdr + i)) if (!hdr_compare(hdr, hdr + i))
return 0; return 0;
} }
@ -1773,7 +1787,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
{ {
for (igr = 0; igr < (HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) for (igr = 0; igr < (HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels)
{ {
memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); memset(scratch.grbuf, 0, sizeof(scratch.grbuf));
L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]);
} }
@ -1787,7 +1801,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
L12_scale_info sci[1]; L12_scale_info sci[1];
L12_read_scale_info(hdr, bs_frame, sci); L12_read_scale_info(hdr, bs_frame, sci);
memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); memset(scratch.grbuf, 0, sizeof(scratch.grbuf));
for (i = 0, igr = 0; igr < 3; igr++) for (i = 0, igr = 0; igr < 3; igr++)
{ {
if (12 == (i += L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) if (12 == (i += L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
@ -1795,7 +1809,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
i = 0; i = 0;
L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]);
memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); memset(scratch.grbuf, 0, sizeof(scratch.grbuf));
pcm += 384*info->channels; pcm += 384*info->channels;
} }
if (bs_frame->pos > bs_frame->limit) if (bs_frame->pos > bs_frame->limit)

View file

@ -21,7 +21,7 @@
/*! \brief libopenmpt minor version number */ /*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 7 #define OPENMPT_API_VERSION_MINOR 7
/*! \brief libopenmpt patch version number */ /*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 9 #define OPENMPT_API_VERSION_PATCH 11
/*! \brief libopenmpt pre-release tag */ /*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL "" #define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */ /*! \brief libopenmpt pre-release flag */

View file

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=7 LIBOPENMPT_VERSION_MINOR=7
LIBOPENMPT_VERSION_PATCH=9 LIBOPENMPT_VERSION_PATCH=11
LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=4 LIBOPENMPT_LTVER_CURRENT=4
LIBOPENMPT_LTVER_REVISION=9 LIBOPENMPT_LTVER_REVISION=11
LIBOPENMPT_LTVER_AGE=4 LIBOPENMPT_LTVER_AGE=4

View file

@ -2203,8 +2203,8 @@ static int wmain( int wargc, wchar_t * wargv [] ) {
static int main( int argc, char * argv [] ) { static int main( int argc, char * argv [] ) {
#endif #endif
#if MPT_OS_DJGPP #if MPT_OS_DJGPP
_crt0_startup_flags &= ~_CRT0_FLAG_LOCK_MEMORY; /* disable automatic locking for all further memory allocations */
assert(mpt::platform::libc().is_ok()); assert(mpt::platform::libc().is_ok());
_crt0_startup_flags &= ~_CRT0_FLAG_LOCK_MEMORY; /* disable automatic locking for all further memory allocations */
#endif /* MPT_OS_DJGPP */ #endif /* MPT_OS_DJGPP */
std::vector<mpt::ustring> args; std::vector<mpt::ustring> args;
#if MPT_OS_WINDOWS && defined(UNICODE) #if MPT_OS_WINDOWS && defined(UNICODE)
@ -2301,6 +2301,10 @@ static int main( int argc, char * argv [] ) {
return 0; return 0;
} catch ( silent_exit_exception & ) { } catch ( silent_exit_exception & ) {
return 0; return 0;
} catch ( exception & e ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout();
return 1;
} catch ( std::exception & e ) { } catch ( std::exception & e ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf; std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout(); std_err.writeout();
@ -2468,6 +2472,10 @@ static int main( int argc, char * argv [] ) {
#endif #endif
} catch ( silent_exit_exception & ) { } catch ( silent_exit_exception & ) {
return 0; return 0;
} catch ( exception & e ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout();
return 1;
} catch ( std::exception & e ) { } catch ( std::exception & e ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf; std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout(); std_err.writeout();

View file

@ -33,6 +33,8 @@
#include <string> #include <string>
#include <libopenmpt/libopenmpt.hpp>
namespace mpt { namespace mpt {
inline namespace MPT_INLINE_NS { inline namespace MPT_INLINE_NS {

View file

@ -117,7 +117,7 @@ private:
case match_recurse: case match_recurse:
break; break;
case match_exact: case match_exact:
if ( mpt::transcode<std::string>( mpt::common_encoding::utf8, extension ) == format_info.extension ) { if ( mpt::transcode<std::string>( sndfile_encoding, extension ) == format_info.extension ) {
if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT ) ) { if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT ) ) {
return matched_result( format, format_info, subformat_info, match_mode ); return matched_result( format, format_info, subformat_info, match_mode );
} else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) { } else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) {
@ -126,7 +126,7 @@ private:
} }
break; break;
case match_better: case match_better:
if ( mpt::transcode<std::string>( mpt::common_encoding::utf8, extension ) == format_info.extension ) { if ( mpt::transcode<std::string>( sndfile_encoding, extension ) == format_info.extension ) {
if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT || subformat_info.format == SF_FORMAT_DOUBLE ) ) { if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT || subformat_info.format == SF_FORMAT_DOUBLE ) ) {
return matched_result( format, format_info, subformat_info, match_mode ); return matched_result( format, format_info, subformat_info, match_mode );
} else if ( !flags.use_float && ( subformat_info.format & ( subformat_info.format == SF_FORMAT_PCM_16 || subformat_info.format == SF_FORMAT_PCM_24 || subformat_info.format == SF_FORMAT_PCM_32 ) ) ) { } else if ( !flags.use_float && ( subformat_info.format & ( subformat_info.format == SF_FORMAT_PCM_16 || subformat_info.format == SF_FORMAT_PCM_24 || subformat_info.format == SF_FORMAT_PCM_32 ) ) ) {
@ -135,7 +135,7 @@ private:
} }
break; break;
case match_any: case match_any:
if ( mpt::transcode<std::string>( mpt::common_encoding::utf8, extension ) == format_info.extension ) { if ( mpt::transcode<std::string>( sndfile_encoding, extension ) == format_info.extension ) {
return matched_result( format, format_info, subformat_info, match_mode ); return matched_result( format, format_info, subformat_info, match_mode );
} }
break; break;

View file

@ -31,6 +31,9 @@
#include <cstddef> #include <cstddef>
#if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE)
#if MPT_COMPILER_MSVC
#include <intrin.h>
#endif
#include <xmmintrin.h> #include <xmmintrin.h>
#endif #endif

View file

@ -20,6 +20,9 @@
#include "mpt/base/numbers.hpp" #include "mpt/base/numbers.hpp"
#if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2) #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
#if MPT_COMPILER_MSVC
#include <intrin.h>
#endif
#include <emmintrin.h> #include <emmintrin.h>
#endif #endif

View file

@ -616,15 +616,22 @@ CDLSBank::CDLSBank()
bool CDLSBank::IsDLSBank(const mpt::PathString &filename) bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
{ {
RIFFChunkID riff; if(filename.empty())
if(filename.empty()) return false;
mpt::ifstream f(filename, std::ios::binary);
if(!f)
{
return false; return false;
mpt::IO::InputFile f(filename, false);
if(!f.IsValid())
return false;
return IsDLSBank(GetFileReader(f));
} }
MemsetZero(riff);
mpt::IO::Read(f, riff);
bool CDLSBank::IsDLSBank(FileReader file)
{
file.Rewind();
RIFFChunkID riff;
if(!file.ReadStruct(riff))
return false;
// Check for embedded DLS sections // Check for embedded DLS sections
if(riff.id_RIFF == IFFID_FORM) if(riff.id_RIFF == IFFID_FORM)
{ {
@ -635,25 +642,28 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
if(len <= 4) break; if(len <= 4) break;
if(riff.id_DLS == IFFID_XDLS) if(riff.id_DLS == IFFID_XDLS)
{ {
mpt::IO::Read(f, riff); if(!file.ReadStruct(riff))
break; return false;
break; // found it
} }
if((len % 2u) != 0) if((len % 2u) != 0)
len++; len++;
if (!mpt::IO::SeekRelative(f, len-4)) break; if(!file.Skip(len - 4))
} while (mpt::IO::Read(f, riff)); return false;
} while(file.ReadStruct(riff));
} else if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID) } else if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID)
{ {
for (;;) for (;;)
{ {
if(!mpt::IO::Read(f, riff)) if(!file.ReadStruct(riff))
break; return false;
if (riff.id_DLS == IFFID_DLS) if(riff.id_DLS == IFFID_DLS || riff.id_DLS == IFFID_sfbk)
break; // found it break; // found it
int len = riff.riff_len; int len = riff.riff_len;
if((len % 2u) != 0) if((len % 2u) != 0)
len++; len++;
if ((len <= 4) || !mpt::IO::SeekRelative(f, len-4)) break; if((len <= 4) || !file.Skip(len - 4))
return false;
} }
} }
return ((riff.id_RIFF == IFFID_RIFF) return ((riff.id_RIFF == IFFID_RIFF)
@ -733,7 +743,7 @@ const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 p
} }
bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum, FileReader *file) const
{ {
ModInstrument *pIns = sndFile.Instruments[ins]; ModInstrument *pIns = sndFile.Instruments[ins];
if(pIns == nullptr) if(pIns == nullptr)
@ -747,7 +757,7 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co
|| FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns)) || FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns))
{ {
if(key < 0x80) drumRgn = GetRegionFromKey(dlsIns, key); if(key < 0x80) drumRgn = GetRegionFromKey(dlsIns, key);
if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn)) if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn, file))
{ {
pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value. pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value.
if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames)))
@ -1394,7 +1404,7 @@ bool CDLSBank::Open(const mpt::PathString &filename)
{ {
if(filename.empty()) return false; if(filename.empty()) return false;
m_szFileName = filename; m_szFileName = filename;
mpt::IO::InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); mpt::IO::InputFile f(filename, false);
if(!f.IsValid()) return false; if(!f.IsValid()) return false;
return Open(GetFileReader(f)); return Open(GetFileReader(f));
} }
@ -1408,12 +1418,8 @@ bool CDLSBank::Open(FileReader file)
m_szFileName = file.GetOptionalFileName().value(); m_szFileName = file.GetOptionalFileName().value();
file.Rewind(); file.Rewind();
size_t dwMemLength = file.GetLength();
size_t dwMemPos = 0;
if(!file.CanRead(256)) if(!file.CanRead(256))
{
return false; return false;
}
RIFFChunkID riff; RIFFChunkID riff;
file.ReadStruct(riff); file.ReadStruct(riff);
@ -1422,11 +1428,9 @@ bool CDLSBank::Open(FileReader file)
{ {
while(file.ReadStruct(riff)) while(file.ReadStruct(riff))
{ {
if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_DLS) if(riff.id_RIFF == IFFID_RIFF && (riff.id_DLS == IFFID_DLS || riff.id_DLS == IFFID_sfbk))
{
file.SkipBack(sizeof(riff));
break; break;
}
uint32 len = riff.riff_len; uint32 len = riff.riff_len;
if((len % 2u) != 0) if((len % 2u) != 0)
len++; len++;
@ -1469,13 +1473,12 @@ bool CDLSBank::Open(FileReader file)
m_WaveForms.clear(); m_WaveForms.clear();
m_Envelopes.clear(); m_Envelopes.clear();
nInsDef = 0; nInsDef = 0;
if (dwMemLength > 8 + riff.riff_len + dwMemPos) dwMemLength = 8 + riff.riff_len + dwMemPos;
bool applyPaddingToSampleChunk = true; bool applyPaddingToSampleChunk = true;
while(file.CanRead(sizeof(IFFCHUNK))) while(file.CanRead(sizeof(IFFCHUNK)))
{ {
IFFCHUNK chunkHeader; IFFCHUNK chunkHeader;
file.ReadStruct(chunkHeader); file.ReadStruct(chunkHeader);
dwMemPos = file.GetPosition(); const auto chunkStartPos = file.GetPosition();
FileReader chunk = file.ReadChunk(chunkHeader.len); FileReader chunk = file.ReadChunk(chunkHeader.len);
bool applyPadding = (chunkHeader.len % 2u) != 0; bool applyPadding = (chunkHeader.len % 2u) != 0;
@ -1531,7 +1534,7 @@ bool CDLSBank::Open(FileReader file)
if (((listid == IFFID_wvpl) && (m_nType & SOUNDBANK_TYPE_DLS)) if (((listid == IFFID_wvpl) && (m_nType & SOUNDBANK_TYPE_DLS))
|| ((listid == IFFID_sdta) && (m_nType & SOUNDBANK_TYPE_SF2))) || ((listid == IFFID_sdta) && (m_nType & SOUNDBANK_TYPE_SF2)))
{ {
m_dwWavePoolOffset = dwMemPos + 4; m_dwWavePoolOffset = chunkStartPos + 4;
#ifdef DLSBANK_LOG #ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Wave Pool offset: {}")(m_dwWavePoolOffset)); MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Wave Pool offset: {}")(m_dwWavePoolOffset));
#endif #endif
@ -1686,17 +1689,16 @@ uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const
} }
bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &waveData, uint32 &length) const std::vector<uint8> CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, FileReader *file) const
{ {
waveData.clear(); std::vector<uint8> waveData;
length = 0;
if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset) if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset)
{ {
#ifdef DLSBANK_LOG #ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ExtractWaveForm({}) failed: m_Instruments.size()={} m_dwWavePoolOffset={} m_WaveForms.size()={}")(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size())); MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ExtractWaveForm({}) failed: m_Instruments.size()={} m_dwWavePoolOffset={} m_WaveForms.size()={}")(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size()));
#endif #endif
return false; return waveData;
} }
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if(nRgn >= dlsIns.Regions.size()) if(nRgn >= dlsIns.Regions.size())
@ -1704,7 +1706,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
#ifdef DLSBANK_LOG #ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size())); MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size()));
#endif #endif
return false; return waveData;
} }
uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink; uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink;
if(nWaveLink >= m_WaveForms.size()) if(nWaveLink >= m_WaveForms.size())
@ -1712,29 +1714,34 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
#ifdef DLSBANK_LOG #ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Invalid wavelink id: nWaveLink={} nWaveForms={}")(nWaveLink, m_WaveForms.size())); MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Invalid wavelink id: nWaveLink={} nWaveForms={}")(nWaveLink, m_WaveForms.size()));
#endif #endif
return false; return waveData;
} }
mpt::ifstream f(m_szFileName, std::ios::binary); std::unique_ptr<mpt::IO::InputFile> inputFile;
if(!f) FileReader f;
if(file)
{ {
return false; f = *file;
} else
{
inputFile = std::make_unique<mpt::IO::InputFile>(m_szFileName, false);
if(!inputFile->IsValid())
return waveData;
f = GetFileReader(*inputFile);
} }
mpt::IO::Offset sampleOffset = mpt::saturate_cast<mpt::IO::Offset>(m_WaveForms[nWaveLink] + m_dwWavePoolOffset); auto sampleOffset = mpt::saturate_cast<FileReader::pos_type>(m_WaveForms[nWaveLink] + m_dwWavePoolOffset);
if(mpt::IO::SeekAbsolute(f, sampleOffset)) if(f.Seek(sampleOffset))
{ {
if (m_nType & SOUNDBANK_TYPE_SF2) if (m_nType & SOUNDBANK_TYPE_SF2)
{ {
if (m_SamplesEx[nWaveLink].dwLen) if (m_SamplesEx[nWaveLink].dwLen)
{ {
if (mpt::IO::SeekRelative(f, 8)) if (f.Skip(8))
{ {
length = m_SamplesEx[nWaveLink].dwLen;
try try
{ {
waveData.assign(length + 8, 0); f.ReadVector(waveData, m_SamplesEx[nWaveLink].dwLen);
mpt::IO::ReadRaw(f, waveData.data(), length);
} catch(mpt::out_of_memory e) } catch(mpt::out_of_memory e)
{ {
mpt::delete_out_of_memory(e); mpt::delete_out_of_memory(e);
@ -1744,16 +1751,14 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
} else } else
{ {
LISTChunk chunk; LISTChunk chunk;
if(mpt::IO::Read(f, chunk)) if(f.ReadStruct(chunk))
{ {
if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4))
{ {
length = chunk.len + 8;
try try
{ {
waveData.assign(chunk.len + sizeof(IFFCHUNK), 0); f.SkipBack(sizeof(chunk));
memcpy(waveData.data(), &chunk, sizeof(chunk)); f.ReadVector(waveData, chunk.len + sizeof(IFFCHUNK));
mpt::IO::ReadRaw(f, waveData.data() + sizeof(chunk), length - sizeof(chunk));
} catch(mpt::out_of_memory e) } catch(mpt::out_of_memory e)
{ {
mpt::delete_out_of_memory(e); mpt::delete_out_of_memory(e);
@ -1762,14 +1767,12 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
} }
} }
} }
return !waveData.empty(); return waveData;
} }
bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose) const bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose, FileReader *file) const
{ {
std::vector<uint8> pWaveForm;
uint32 dwLen = 0;
bool ok, hasWaveform; bool ok, hasWaveform;
if(nIns >= m_Instruments.size()) if(nIns >= m_Instruments.size())
@ -1777,9 +1780,8 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if(nRgn >= dlsIns.Regions.size()) if(nRgn >= dlsIns.Regions.size())
return false; return false;
if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) const std::vector<uint8> waveData = ExtractWaveForm(nIns, nRgn, file);
return false; if(waveData.size() < 16)
if(dwLen < 16)
return false; return false;
ok = false; ok = false;
@ -1798,10 +1800,10 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
#endif #endif
sample.Initialize(); sample.Initialize();
FileReader chunk{mpt::as_span(pWaveForm.data(), dwLen)}; FileReader chunk{mpt::as_span(waveData)};
if(!p.compressed || !sndFile.ReadSampleFromFile(nSample, chunk, false, false)) if(!p.compressed || !sndFile.ReadSampleFromFile(nSample, chunk, false, false))
{ {
sample.nLength = dwLen / 2; sample.nLength = mpt::saturate_cast<SmpLength>(waveData.size() / 2);
SampleIO( SampleIO(
SampleIO::_16bit, SampleIO::_16bit,
SampleIO::mono, SampleIO::mono,
@ -1822,8 +1824,8 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
hasWaveform = sample.HasSampleData(); hasWaveform = sample.HasSampleData();
} else } else
{ {
FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); FileReader wavChunk(mpt::as_span(waveData));
hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); hasWaveform = sndFile.ReadWAVSample(nSample, wavChunk, false, &wsmpChunk);
if(dlsIns.szName[0]) if(dlsIns.szName[0])
sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName); sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName);
} }
@ -2033,7 +2035,7 @@ uint32 DLSENVELOPE::Envelope::ConvertToMPT(InstrumentEnvelope &mptEnv, const Env
} }
bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn, FileReader *file) const
{ {
uint32 minRegion, maxRegion, nEnv; uint32 minRegion, maxRegion, nEnv;
@ -2194,7 +2196,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
// Load the sample // Load the sample
if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData()) if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData())
{ {
ExtractSample(sndFile, nSmp, nIns, nRgn, transpose); ExtractSample(sndFile, nSmp, nIns, nRgn, transpose, file);
extractedSamples.insert(rgn.nWaveLink); extractedSamples.insert(rgn.nWaveLink);
} }
} else if(duplicateRegion && sndFile.GetSample(RgnToSmp[dupRegion]).GetNumChannels() == 1) } else if(duplicateRegion && sndFile.GetSample(RgnToSmp[dupRegion]).GetNumChannels() == 1)
@ -2213,9 +2215,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
const uint8 offsetOrig = (pan1 < pan2) ? 1 : 0; const uint8 offsetOrig = (pan1 < pan2) ? 1 : 0;
const uint8 offsetNew = (pan1 < pan2) ? 0 : 1; const uint8 offsetNew = (pan1 < pan2) ? 0 : 1;
std::vector<uint8> pWaveForm; const std::vector<uint8> waveData = ExtractWaveForm(nIns, nRgn, file);
uint32 dwLen = 0; if(waveData.empty())
if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
continue; continue;
extractedSamples.insert(rgn.nWaveLink); extractedSamples.insert(rgn.nWaveLink);
@ -2230,8 +2231,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
// Now read the other channel // Now read the other channel
if(m_SamplesEx[m_Instruments[nIns].Regions[nRgn].nWaveLink].compressed) if(m_SamplesEx[m_Instruments[nIns].Regions[nRgn].nWaveLink].compressed)
{ {
FileReader file{mpt::as_span(pWaveForm)}; FileReader smpChunk{mpt::as_span(waveData)};
if(sndFile.ReadSampleFromFile(nSmp, file, false, false)) if(sndFile.ReadSampleFromFile(nSmp, smpChunk, false, false))
{ {
pDest = sampleCopy.sample16() + offsetNew; pDest = sampleCopy.sample16() + offsetNew;
const SmpLength copyLength = std::min(sample.nLength, sampleCopy.nLength); const SmpLength copyLength = std::min(sample.nLength, sampleCopy.nLength);
@ -2242,10 +2243,10 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
} }
} else } else
{ {
SmpLength len = std::min(dwLen / 2u, sampleCopy.nLength); SmpLength len = std::min(mpt::saturate_cast<SmpLength>(waveData.size() / 2u), sampleCopy.nLength);
const std::byte *src = mpt::byte_cast<const std::byte *>(pWaveForm.data()); const std::byte *src = mpt::byte_cast<const std::byte *>(waveData.data());
int16 *dst = sampleCopy.sample16() + offsetNew; int16 *dst = sampleCopy.sample16() + offsetNew;
CopySample<SC::ConversionChain<SC::Convert<int16, int16>, SC::DecodeInt16<0, littleEndian16>>>(dst, len, 2, src, pWaveForm.size(), 1); CopySample<SC::ConversionChain<SC::Convert<int16, int16>, SC::DecodeInt16<0, littleEndian16>>>(dst, len, 2, src, waveData.size(), 1);
} }
sample.FreeSample(); sample.FreeSample();
sample = sampleCopy; sample = sampleCopy;

View file

@ -129,6 +129,7 @@ public:
bool operator==(const CDLSBank &other) const noexcept { return !mpt::PathCompareNoCase(m_szFileName, other.m_szFileName); } bool operator==(const CDLSBank &other) const noexcept { return !mpt::PathCompareNoCase(m_szFileName, other.m_szFileName); }
static bool IsDLSBank(const mpt::PathString &filename); static bool IsDLSBank(const mpt::PathString &filename);
static bool IsDLSBank(FileReader file);
static uint32 MakeMelodicCode(uint32 bank, uint32 instr) { return ((bank << 16) | (instr));} static uint32 MakeMelodicCode(uint32 bank, uint32 instr) { return ((bank << 16) | (instr));}
static uint32 MakeDrumCode(uint32 rgn, uint32 instr) { return (0x80000000 | (rgn << 16) | (instr));} static uint32 MakeDrumCode(uint32 rgn, uint32 instr) { return (0x80000000 | (rgn << 16) | (instr));}
@ -144,16 +145,16 @@ public:
uint32 GetNumSamples() const { return static_cast<uint32>(m_WaveForms.size()); } uint32 GetNumSamples() const { return static_cast<uint32>(m_WaveForms.size()); }
const DLSINSTRUMENT *GetInstrument(uint32 iIns) const { return iIns < m_Instruments.size() ? &m_Instruments[iIns] : nullptr; } const DLSINSTRUMENT *GetInstrument(uint32 iIns) const { return iIns < m_Instruments.size() ? &m_Instruments[iIns] : nullptr; }
[[nodiscard]] const DLSINSTRUMENT *FindInstrument(bool isDrum, uint32 bank = 0xFF, uint32 program = 0xFF, uint32 key = 0xFF, uint32 *pInsNo = nullptr) const; [[nodiscard]] const DLSINSTRUMENT *FindInstrument(bool isDrum, uint32 bank = 0xFF, uint32 program = 0xFF, uint32 key = 0xFF, uint32 *pInsNo = nullptr) const;
bool FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const; bool FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum, FileReader *file = nullptr) const;
uint32 GetRegionFromKey(uint32 nIns, uint32 nKey) const; uint32 GetRegionFromKey(uint32 nIns, uint32 nKey) const;
bool ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &waveData, uint32 &length) const; bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0, FileReader *file = nullptr) const;
bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0) const; bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn, FileReader *file = nullptr) const;
bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const;
const char *GetRegionName(uint32 nIns, uint32 nRgn) const; const char *GetRegionName(uint32 nIns, uint32 nRgn) const;
uint16 GetPanning(uint32 ins, uint32 region) const; uint16 GetPanning(uint32 ins, uint32 region) const;
// Internal Loader Functions // Internal Loader Functions
protected: protected:
std::vector<uint8> ExtractWaveForm(uint32 nIns, uint32 nRgn, FileReader *file) const;
bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk); bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk);
bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk); bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk);
bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info); bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info);

View file

@ -248,7 +248,15 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
uint8 command = effect[chn] >> 4; uint8 command = effect[chn] >> 4;
if(command < static_cast<uint8>(std::size(effTrans))) if(command < static_cast<uint8>(std::size(effTrans)))
{ {
#if MPT_COMPILER_MSVC
#pragma warning(push)
// false-positive
#pragma warning(disable:6385) // Reading invalid data from 'effTrans'.
#endif
m->command = effTrans[command]; m->command = effTrans[command];
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif
} else } else
{ {
m->command = CMD_NONE; m->command = CMD_NONE;

View file

@ -24,7 +24,7 @@ struct DMFFileHeader
{ {
char signature[4]; // "DDMF" char signature[4]; // "DDMF"
uint8 version; // 1 - 7 are beta versions, 8 is the official thing, 10 is xtracker32 uint8 version; // 1 - 7 are beta versions, 8 is the official thing, 10 is xtracker32
char tracker[8]; // "XTRACKER" char tracker[8]; // "XTRACKER", or "SCREAM 3" when converting from S3M, etc.
char songname[30]; char songname[30];
char composer[20]; char composer[20];
uint8 creationDay; uint8 creationDay;

View file

@ -126,6 +126,8 @@ static std::vector<std::byte> DecompressDSymLZW(FileReader &file, uint32 size)
// Align length to 4 bytes // Align length to 4 bytes
file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3))); file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3)));
// cppcheck false-positive
// cppcheck-suppress returnDanglingLifetime
return output; return output;
} }

View file

@ -341,6 +341,26 @@ static void CopyPatternName(CPattern &pattern, FileReader &file)
} }
// Get version of Impulse Tracker that was used to create an IT/S3M file.
mpt::ustring CSoundFile::GetImpulseTrackerVersion(uint16 cwtv, uint16 cmwt)
{
mpt::ustring version;
cwtv &= 0xFFF;
if(cmwt > 0x0214)
{
version = UL_("Impulse Tracker 2.15");
} else if(cwtv >= 0x0215 && cwtv <= 0x0217)
{
const mpt::uchar *versions[] = {UL_("1-2"), UL_("3"), UL_("4-5")};
version = MPT_UFORMAT("Impulse Tracker 2.14p{}")(mpt::ustring_view(versions[cwtv - 0x0215]));
} else
{
version = MPT_UFORMAT("Impulse Tracker {}.{}")((cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((cwtv & 0xFF)));
}
return version;
}
// Get version of Schism Tracker that was used to create an IT/S3M file. // Get version of Schism Tracker that was used to create an IT/S3M file.
mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv, uint32 reserved) mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv, uint32 reserved)
{ {
@ -1027,6 +1047,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
auto patData = Patterns[pat].begin(); auto patData = Patterns[pat].begin();
ROWINDEX row = 0; ROWINDEX row = 0;
ModCommand dummy{};
while(row < numRows && patternData.CanRead(1)) while(row < numRows && patternData.CanRead(1))
{ {
uint8 b = patternData.ReadUint8(); uint8 b = patternData.ReadUint8();
@ -1057,7 +1078,6 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
} }
// Now we grab the data for this particular row/channel. // Now we grab the data for this particular row/channel.
ModCommand dummy{};
ModCommand &m = ch < m_nChannels ? patData[ch] : dummy; ModCommand &m = ch < m_nChannels ? patData[ch] : dummy;
if(chnMask[ch] & 0x10) if(chnMask[ch] & 0x10)
@ -1138,8 +1158,18 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
{ {
const auto [command, param] = patternData.ReadArray<uint8, 2>(); const auto [command, param] = patternData.ReadArray<uint8, 2>();
S3MConvert(m, command, param, true); S3MConvert(m, command, param, true);
// IT 1.xx does not support high offset command
if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xA0 && fileHeader.cwtv < 0x0200)
m.command = CMD_DUMMY;
// Fix handling of commands V81-VFF in ITs made with old Schism Tracker versions
// (fixed in https://github.com/schismtracker/schismtracker/commit/ab5517d4730d4c717f7ebffb401445679bd30888 - one of the last versions to identify as v0.50)
else if(m.command == CMD_GLOBALVOLUME && m.param > 0x80 && fileHeader.cwtv >= 0x1000 && fileHeader.cwtv <= 0x1050)
m.param = 0x80;
// In some IT-compatible trackers, it is possible to input a parameter without a command. // In some IT-compatible trackers, it is possible to input a parameter without a command.
// In this case, we still need to update the last value memory. OpenMPT didn't do this until v1.25.01.07. // In this case, we still need to update the last value memory (so that we don't reuse a previous non-empty effect).
// OpenMPT didn't do this until v1.25.01.07.
// Example: ckbounce.it // Example: ckbounce.it
lastValue[ch].command = m.command; lastValue[ch].command = m.command;
lastValue[ch].param = m.param; lastValue[ch].param = m.param;
@ -1229,19 +1259,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
madeWithTracker = U_("Unknown"); madeWithTracker = U_("Unknown");
} else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty()) } else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty())
{ {
if(fileHeader.cmwt > 0x0214) madeWithTracker = GetImpulseTrackerVersion(fileHeader.cwtv, fileHeader.cmwt);
{
madeWithTracker = U_("Impulse Tracker 2.15");
} else if(fileHeader.cwtv > 0x0214)
{
// Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3)
// p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change
// anything as far as file saving is concerned.
madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - 0x0214);
} else
{
madeWithTracker = MPT_UFORMAT("Impulse Tracker {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF)));
}
if(m_FileHistory.empty() && fileHeader.reserved != 0) if(m_FileHistory.empty() && fileHeader.reserved != 0)
{ {
// Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field // Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field
@ -1739,6 +1757,10 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
// Write pattern header // Write pattern header
ROWINDEX writeRows = mpt::saturate_cast<uint16>(Patterns[pat].GetNumRows()); ROWINDEX writeRows = mpt::saturate_cast<uint16>(Patterns[pat].GetNumRows());
if(compatibilityExport)
writeRows = std::clamp(writeRows, ROWINDEX(32), ROWINDEX(200));
if(writeRows != Patterns[pat].GetNumRows())
AddToLog(LogWarning, MPT_UFORMAT("Warning: Pattern {} was resized from {} to {} rows.")(pat, Patterns[pat].GetNumRows(), writeRows));
uint16 writeSize = 0; uint16 writeSize = 0;
uint16le patinfo[4]; uint16le patinfo[4];
patinfo[0] = 0; patinfo[0] = 0;
@ -1755,11 +1777,12 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
// Maximum 7 bytes per cell, plus end of row marker, so this buffer is always large enough to cover one row. // Maximum 7 bytes per cell, plus end of row marker, so this buffer is always large enough to cover one row.
std::vector<uint8> buf(7 * maxChannels + 1); std::vector<uint8> buf(7 * maxChannels + 1);
for(ROWINDEX row = 0; row < writeRows; row++) const ROWINDEX readRows = std::min(writeRows, Patterns[pat].GetNumRows());
for(ROWINDEX row = 0; row < readRows; row++)
{ {
uint32 len = 0; uint32 len = 0;
const ModCommand *m = Patterns[pat].GetpModCommand(row, 0); const ModCommand *m = Patterns[pat].GetpModCommand(row, 0);
bool writePatternBreak = (readRows < writeRows && row + 1 == readRows && !Patterns[pat].RowHasJump(row));
for(CHANNELINDEX ch = 0; ch < maxChannels; ch++, m++) for(CHANNELINDEX ch = 0; ch < maxChannels; ch++, m++)
{ {
// Skip mptm-specific notes. // Skip mptm-specific notes.
@ -1813,6 +1836,12 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
S3MSaveConvert(*m, command, param, true, compatibilityExport); S3MSaveConvert(*m, command, param, true, compatibilityExport);
if (command) b |= 8; if (command) b |= 8;
} }
if(writePatternBreak && !(b & 8))
{
b |= 8;
command = 'C' ^ 0x40;
writePatternBreak = false;
}
// Packing information // Packing information
if (b) if (b)
{ {
@ -1895,11 +1924,25 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
break; break;
} else } else
{ {
dwPos += len; writeSize += static_cast<uint16>(len);
writeSize += (uint16)len;
mpt::IO::WriteRaw(f, buf.data(), len); mpt::IO::WriteRaw(f, buf.data(), len);
} }
if(writePatternBreak)
{
// Didn't manage to put a pattern break, so put it on the next row instead.
const uint8 patternBreak[] = {1 | IT_bitmask_patternChanEnabled_c, 8, 'C' ^ 0x40, 0};
mpt::IO::Write(f, patternBreak);
writeSize += sizeof(patternBreak);
} }
}
if(readRows < writeRows)
{
// Invent empty rows at end (if we end up here, the pattern is very short and we don't have to care about writeSize overflowing the 16-bit limit)
writeSize += static_cast<uint16>(writeRows - readRows);
buf.assign(writeRows - readRows, 0);
mpt::IO::Write(f, buf);
}
dwPos += writeSize;
mpt::IO::SeekAbsolute(f, dwPatPos); mpt::IO::SeekAbsolute(f, dwPatPos);
patinfo[0] = writeSize; patinfo[0] = writeSize;

View file

@ -411,12 +411,12 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
switch(command) switch(command)
{ {
case 0x04: // Vibrato (twice as deep as in ProTracker) case 0x04: // Vibrato (twice as deep as in ProTracker)
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>(param >> 3, 0x0F) << 4) | std::min<uint8>((param & 0x0F) * 2, 0x0F)); m.SetEffectCommand(CMD_VIBRATO, (param & 0xF0) | std::min<uint8>((param & 0x0F) * 2, 0x0F));
break; break;
case 0x08: // Hold and decay case 0x08: // Hold and decay
break; break;
case 0x09: // Set secondary speed case 0x09: // Set secondary speed
if(param > 0 && param <= 20) if(param > 0 && param <= 0x20)
m.SetEffectCommand(CMD_SPEED, param); m.SetEffectCommand(CMD_SPEED, param);
break; break;
case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support) case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support)
@ -456,10 +456,6 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
return {CMD_XPARAM, static_cast<ModCommand::PARAM>(tempo & 0xFF)}; return {CMD_XPARAM, static_cast<ModCommand::PARAM>(tempo & 0xFF)};
} }
} }
#ifdef MODPLUG_TRACKER
if(m.param < 0x20)
m.param = 0x20;
#endif // MODPLUG_TRACKER
} else switch(param) } else switch(param)
{ {
case 0xF1: // Play note twice case 0xF1: // Play note twice
@ -495,7 +491,7 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo); m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo);
break; break;
case 0x14: // Vibrato (ProTracker compatible depth, but faster) case 0x14: // Vibrato (ProTracker compatible depth, but faster)
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>((param >> 4) + 1, 0x0F) << 4) | (param & 0x0F)); m.SetEffectCommand(CMD_VIBRATO, param);
break; break;
case 0x15: // Set finetune case 0x15: // Set finetune
m.SetEffectCommand(CMD_MODCMDEX, 0x50 | (param & 0x0F)); m.SetEffectCommand(CMD_MODCMDEX, 0x50 | (param & 0x0F));
@ -1143,7 +1139,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64) if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64)
return false; return false;
const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN); const bool freePan = !hardwareMixSamples && (header.flags3 & MMD2Song::FLAG3_FREEPAN);
if(header.volAdjust) if(header.volAdjust)
preamp = Util::muldivr_unsigned(preamp, std::min<uint16>(header.volAdjust, 800), 100); preamp = Util::muldivr_unsigned(preamp, std::min<uint16>(header.volAdjust, 800), 100);
if (freePan) if (freePan)
@ -1470,8 +1466,8 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
order[from] = pat; order[from] = pat;
} }
Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast<ModCommand::PARAM>(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast<ModCommand::PARAM>(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow());
if(pat >= numPatterns) if(pat >= basePattern && (pat - basePattern) >= numPatterns)
numPatterns = pat + 1; numPatterns = static_cast<PATTERNINDEX>(pat - basePattern + 1);
} }
if(numSongs > 1) if(numSongs > 1)
@ -1484,8 +1480,8 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
Patterns[firstPat].WriteEffect(EffectWriter(CMD_CHANNELVOLUME, static_cast<ModCommand::PARAM>(ChnSettings[chn].nVolume)).Channel(chn).RetryNextRow()); Patterns[firstPat].WriteEffect(EffectWriter(CMD_CHANNELVOLUME, static_cast<ModCommand::PARAM>(ChnSettings[chn].nVolume)).Channel(chn).RetryNextRow());
Patterns[firstPat].WriteEffect(EffectWriter(CMD_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(ChnSettings[chn].nPan)).Channel(chn).RetryNextRow()); Patterns[firstPat].WriteEffect(EffectWriter(CMD_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(ChnSettings[chn].nPan)).Channel(chn).RetryNextRow());
} }
if(firstPat >= numPatterns) if(firstPat >= basePattern && (firstPat - basePattern) >= numPatterns)
numPatterns = firstPat + 1; numPatterns = static_cast<PATTERNINDEX>(firstPat - basePattern + 1);
} }
} }

View file

@ -1320,12 +1320,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
} }
std::unique_ptr<CDLSBank> cachedBank, embeddedBank; std::unique_ptr<CDLSBank> cachedBank, embeddedBank;
FileReader *bankFile = nullptr;
if(CDLSBank::IsDLSBank(file.GetOptionalFileName().value_or(P_("")))) if(CDLSBank::IsDLSBank(file))
{ {
// Soundfont embedded in MIDI file // Soundfont embedded in MIDI file
embeddedBank = std::make_unique<CDLSBank>(); embeddedBank = std::make_unique<CDLSBank>();
embeddedBank->Open(file.GetOptionalFileName().value_or(P_(""))); embeddedBank->Open(file);
bankFile = &file;
} else } else
{ {
// Soundfont with same name as MIDI file // Soundfont with same name as MIDI file
@ -1353,7 +1355,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
else if(pIns->nMidiProgram) else if(pIns->nMidiProgram)
midiCode = (pIns->nMidiProgram - 1) & 0x7F; midiCode = (pIns->nMidiProgram - 1) & 0x7F;
if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80)) if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80, bankFile))
{ {
continue; continue;
} }

View file

@ -2451,6 +2451,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const
continue; continue;
} }
const auto rowBase = Patterns[pat].GetRow(row); const auto rowBase = Patterns[pat].GetRow(row);
bool writePatternBreak = (Patterns[pat].GetNumRows() < 64 && row + 1 == Patterns[pat].GetNumRows() && !Patterns[pat].RowHasJump(row));
events.resize(writeChannels * 4); events.resize(writeChannels * 4);
size_t eventByte = 0; size_t eventByte = 0;
@ -2466,6 +2467,11 @@ bool CSoundFile::SaveMod(std::ostream &f) const
command = 0x0C; command = 0x0C;
param = std::min(m.vol, uint8(64)); param = std::min(m.vol, uint8(64));
} }
if(writePatternBreak && !command && !param)
{
command = 0x0D;
writePatternBreak = false;
}
uint16 period = 0; uint16 period = 0;
// Convert note to period // Convert note to period

View file

@ -30,7 +30,7 @@ struct OktIffChunk
}; };
uint32be signature; // IFF chunk name uint32be signature; // IFF chunk name
uint32be chunksize; // chunk size without header uint32be chunkSize; // Chunk size without header
}; };
MPT_BINARY_STRUCT(OktIffChunk, 8) MPT_BINARY_STRUCT(OktIffChunk, 8)
@ -38,11 +38,11 @@ MPT_BINARY_STRUCT(OktIffChunk, 8)
struct OktSample struct OktSample
{ {
char name[20]; char name[20];
uint32be length; // length in bytes uint32be length; // Length in bytes
uint16be loopStart; // *2 for real value uint16be loopStart; // *2 for real value
uint16be loopLength; // ditto uint16be loopLength; // ditto
uint16be volume; // default volume uint16be volume; // Default volume
uint16be type; // 7-/8-bit sample uint16be type; // 7-/8-bit sample (0: 7-bit, only usable on paired channels ["8" in GUI], 1: 8-bit, only usable on unpaired channels ["4" in GUI], 2: 7-bit, usable on all channels ["B" in GUI])
}; };
MPT_BINARY_STRUCT(OktSample, 32) MPT_BINARY_STRUCT(OktSample, 32)
@ -51,7 +51,8 @@ MPT_BINARY_STRUCT(OktSample, 32)
// Parse the sample header block // Parse the sample header block
static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile) static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile)
{ {
sndFile.m_nSamples = std::min(static_cast<SAMPLEINDEX>(chunk.BytesLeft() / sizeof(OktSample)), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1)); static_assert(MAX_SAMPLES >= 72); // For copies of type "B" samples
sndFile.m_nSamples = std::min(static_cast<SAMPLEINDEX>(chunk.BytesLeft() / sizeof(OktSample)), SAMPLEINDEX(36));
for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++)
{ {
@ -66,6 +67,7 @@ static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile)
mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u; mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u;
mptSmp.nLength = oktSmp.length & ~1; mptSmp.nLength = oktSmp.length & ~1;
mptSmp.cues[0] = oktSmp.type; // Temporary storage for pattern reader, will be reset later mptSmp.cues[0] = oktSmp.type; // Temporary storage for pattern reader, will be reset later
mptSmp.cues[1] = 0;
// Parse loops // Parse loops
const SmpLength loopStart = oktSmp.loopStart * 2; const SmpLength loopStart = oktSmp.loopStart * 2;
const SmpLength loopLength = oktSmp.loopLength * 2; const SmpLength loopLength = oktSmp.loopLength * 2;
@ -124,20 +126,26 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
if(note > 0 && note <= 36) if(note > 0 && note <= 36)
{ {
m.note = note + (NOTE_MIDDLEC - 13); m.note = note + (NOTE_MIDDLEC - 13);
if(pairedChn[chn] && m.note >= NOTE_MIDDLEC + 22)
m.note = NOTE_MIDDLEC + 21;
m.instr = instr + 1; m.instr = instr + 1;
if(m.instr > 0 && m.instr <= sndFile.GetNumSamples()) if(m.instr > 0 && m.instr <= sndFile.GetNumSamples())
{ {
const auto &sample = sndFile.GetSample(m.instr); auto &sample = sndFile.GetSample(m.instr);
// Default volume only works on raw Paula channels // Default volume only works on raw Paula channels
if(pairedChn[chn] && sample.nVolume < 256) if(pairedChn[chn] && sample.nVolume < 256)
{ m.SetVolumeCommand(VOLCMD_VOLUME, 64);
m.volcmd = VOLCMD_VOLUME;
m.vol = 64;
}
// If channel and sample type don't match, stop this channel (add 100 to the instrument number to make it understandable what happened during import) // If channel and sample type don't match, stop this channel (add 100 to the instrument number to make it understandable what happened during import)
if((sample.cues[0] == 1 && pairedChn[chn] != 0) || (sample.cues[0] == 0 && pairedChn[chn] == 0)) if((sample.cues[0] == 1 && pairedChn[chn] != 0) || (sample.cues[0] == 0 && pairedChn[chn] == 0))
{ {
m.instr += 100; m.instr += 100;
} else if(sample.cues[0] == 2 && pairedChn[chn] && sample.uFlags[CHN_SUSTAINLOOP])
{
// Type "B" sample: Loops only work on raw Paula channels
sample.cues[1] = 1;
m.instr += 36;
} }
} }
} }
@ -150,105 +158,72 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
case 1: // 1 Portamento Down (Period) case 1: // 1 Portamento Down (Period)
if(param) if(param)
{ {
m.command = CMD_PORTAMENTOUP; m.SetEffectCommand(CMD_PORTAMENTOUP, param);
m.param = param;
} }
break; break;
case 2: // 2 Portamento Up (Period) case 2: // 2 Portamento Up (Period)
if(param) if(param)
{ m.SetEffectCommand(CMD_PORTAMENTODOWN, param);
m.command = CMD_PORTAMENTODOWN;
m.param = param;
}
break; break;
case 10: // A Arpeggio 1 (down, orig, up) case 10: // A Arpeggio 1 (down, orig, up)
if(param) if(param)
{ m.SetEffectCommand(CMD_ARPEGGIO, (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4));
m.command = CMD_ARPEGGIO;
m.param = (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4);
}
break; break;
case 11: // B Arpeggio 2 (orig, up, orig, down) case 11: // B Arpeggio 2 (orig, up, orig, down)
if(param) if(param)
{ m.SetEffectCommand(CMD_ARPEGGIO, (param & 0xF0) | InvertArpeggioParam(param & 0x0F));
m.command = CMD_ARPEGGIO;
m.param = (param & 0xF0) | InvertArpeggioParam(param & 0x0F);
}
break; break;
// This one is close enough to "standard" arpeggio -- I think! // This one is close enough to "standard" arpeggio -- I think!
case 12: // C Arpeggio 3 (up, up, orig) case 12: // C Arpeggio 3 (up, up, orig)
if(param) if(param)
{ m.SetEffectCommand(CMD_ARPEGGIO, param);
m.command = CMD_ARPEGGIO;
m.param = param;
}
break; break;
case 13: // D Slide Down (Notes) case 13: // D Slide Down (Notes)
if(param) if(param)
{ m.SetEffectCommand(CMD_NOTESLIDEDOWN, 0x10 | std::min(uint8(0x0F), param));
m.command = CMD_NOTESLIDEDOWN;
m.param = 0x10 | std::min(uint8(0x0F), param);
}
break; break;
case 30: // U Slide Up (Notes) case 30: // U Slide Up (Notes)
if(param) if(param)
{ m.SetEffectCommand(CMD_NOTESLIDEUP, 0x10 | std::min(uint8(0x0F), param));
m.command = CMD_NOTESLIDEUP;
m.param = 0x10 | std::min(uint8(0x0F), param);
}
break; break;
// Fine Slides are only implemented for libopenmpt. For OpenMPT, // Fine Slides are only implemented for libopenmpt. For OpenMPT,
// sliding every 5 (non-note) ticks kind of works (at least at // sliding every 5 (non-note) ticks kind of works (at least at
// speed 6), but implementing separate (format-agnostic) fine slide commands would of course be better. // speed 6), but implementing separate (format-agnostic) fine slide commands would of course be better.
case 21: // L Slide Down Once (Notes) case 21: // L Slide Down Once (Notes)
if(param) if(param)
{ m.SetEffectCommand(CMD_NOTESLIDEDOWN, 0x50 | std::min(uint8(0x0F), param));
m.command = CMD_NOTESLIDEDOWN;
m.param = 0x50 | std::min(uint8(0x0F), param);
}
break; break;
case 17: // H Slide Up Once (Notes) case 17: // H Slide Up Once (Notes)
if(param) if(param)
{ m.SetEffectCommand(CMD_NOTESLIDEUP, 0x50 | std::min(uint8(0x0F), param));
m.command = CMD_NOTESLIDEUP;
m.param = 0x50 | std::min(uint8(0x0F), param);
}
break; break;
case 15: // F Set Filter <>00:ON case 15: // F Set Filter <>00:ON
m.command = CMD_MODCMDEX; m.SetEffectCommand(CMD_MODCMDEX, !!param);
m.param = !!param;
break; break;
case 25: // P Pos Jump case 25: // P Pos Jump
m.command = CMD_POSITIONJUMP; m.SetEffectCommand(CMD_POSITIONJUMP, param);
m.param = param;
break; break;
case 27: // R Release sample (apparently not listed in the help!) case 27: // R Release sample (apparently not listed in the help!)
m.Clear();
m.note = NOTE_KEYOFF; m.note = NOTE_KEYOFF;
m.instr = 0;
break; break;
case 28: // S Speed case 28: // S Speed
if(param < 0x20) if(param < 0x20)
{ m.SetEffectCommand(CMD_SPEED, param);
m.command = CMD_SPEED;
m.param = param;
}
break; break;
case 31: // V Volume case 31: // V Volume
// Volume on mixed channels is permanent, on hardware channels it behaves like in regular MODs // Volume on mixed channels is permanent, on hardware channels it behaves like in regular MODs
if(param & 0x0F) if(param & 0x0F)
{ m.SetEffectCommand(pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE, param & 0x0F);
m.command = pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE;
m.param = param & 0x0F;
}
switch(param >> 4) switch(param >> 4)
{ {
@ -260,13 +235,11 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
case 0: case 1: case 2: case 3: case 0: case 1: case 2: case 3:
if(pairedChn[chn]) if(pairedChn[chn])
{ {
m.command = CMD_CHANNELVOLUME; m.SetEffectCommand(CMD_CHANNELVOLUME, param);
m.param = param;
} else } else
{ {
m.volcmd = VOLCMD_VOLUME; m.SetVolumeCommand(VOLCMD_VOLUME, param);
m.vol = param; m.SetEffectCommand(oldCmd, oldParam);
m.command = CMD_NONE;
} }
break; break;
case 5: // Normal slide up case 5: // Normal slide up
@ -280,7 +253,7 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
break; break;
default: default:
// Junk. // Junk.
m.command = CMD_NONE; m.SetEffectCommand(oldCmd, oldParam);
break; break;
} }
@ -293,7 +266,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
{ {
other.SetVolumeCommand(volCmd); other.SetVolumeCommand(volCmd);
} }
if(ModCommand::GetEffectWeight(other.command) < ModCommand::GetEffectWeight(m.command))
{
other.SetEffectCommand(m); other.SetEffectCommand(m);
} else if(row < rows - 1)
{
// Retry on next row
sndFile.Patterns[pat].GetpModCommand(row + 1, static_cast<CHANNELINDEX>(chn + pairedChn[chn]))->SetEffectCommand(m);
}
} }
break; break;
@ -359,7 +339,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
if(!file.ReadStruct(iffHead)) if(!file.ReadStruct(iffHead))
break; break;
FileReader chunk = file.ReadChunk(iffHead.chunksize); FileReader chunk = file.ReadChunk(iffHead.chunkSize);
if(!chunk.IsValid()) if(!chunk.IsValid())
continue; continue;
@ -467,12 +447,14 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
// Read samples // Read samples
size_t fileSmp = 0; size_t fileSmp = 0;
for(SAMPLEINDEX smp = 1; smp < m_nSamples; smp++) const SAMPLEINDEX origSamples = m_nSamples;
for(SAMPLEINDEX smp = 1; smp <= origSamples; smp++)
{ {
if(fileSmp >= sampleChunks.size() || !(loadFlags & loadSampleData)) if(fileSmp >= sampleChunks.size() || !(loadFlags & loadSampleData))
break; break;
ModSample &mptSample = Samples[smp]; ModSample &mptSample = Samples[smp];
const bool needCopy = mptSample.cues[1] != 0;
mptSample.SetDefaultCuePoints(); mptSample.SetDefaultCuePoints();
if(mptSample.nLength == 0) if(mptSample.nLength == 0)
continue; continue;
@ -487,6 +469,20 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
SampleIO::signedPCM) SampleIO::signedPCM)
.ReadSample(mptSample, sampleChunks[fileSmp]); .ReadSample(mptSample, sampleChunks[fileSmp]);
if(needCopy)
{
// Type "B" samples (can play on both paired and unpaired channels) can have loop information,
// which can only be used on unpaired channels. So we need a looped and unlooped copy of the sample.
m_nSamples = std::max(m_nSamples, static_cast<SAMPLEINDEX>(smp + 36));
ModSample &copySample = Samples[smp + 36];
copySample.Initialize();
copySample.nC5Speed = mptSample.nC5Speed;
copySample.nVolume = mptSample.nVolume;
copySample.nLength = mptSample.nLength;
copySample.CopyWaveform(mptSample);
m_szNames[smp + 36] = m_szNames[smp];
}
fileSmp++; fileSmp++;
} }

View file

@ -175,6 +175,7 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags)
m_modFormat.madeWithTracker = MPT_UFORMAT("PolyTracker {}.{}")(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get())); m_modFormat.madeWithTracker = MPT_UFORMAT("PolyTracker {}.{}")(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get()));
m_modFormat.charset = mpt::Charset::CP437; m_modFormat.charset = mpt::Charset::CP437;
SetMixLevels(MixLevels::CompatibleFT2);
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
m_nChannels = fileHeader.numChannels; m_nChannels = fileHeader.numChannels;
m_nSamples = std::min(static_cast<SAMPLEINDEX>(fileHeader.numSamples), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1)); m_nSamples = std::min(static_cast<SAMPLEINDEX>(fileHeader.numSamples), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
@ -267,20 +268,25 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags)
{ {
case CMD_PANNING8: case CMD_PANNING8:
// Don't be surprised about the strange formula, this is directly translated from original disassembly... // Don't be surprised about the strange formula, this is directly translated from original disassembly...
m.command = CMD_S3MCMDEX; m.SetEffectCommand(CMD_S3MCMDEX, static_cast<ModCommand::PARAM>(0x80 | ((std::max<uint8>(m.param >> 3, 1u) - 1u) & 0x0F)));
m.param = 0x80 | ((std::max<uint8>(m.param >> 3, 1u) - 1u) & 0x0F);
break; break;
case CMD_GLOBALVOLUME: case CMD_GLOBALVOLUME:
m.param = std::min(m.param, uint8(0x40)) * 2u; m.param = std::min(m.param, uint8(0x40)) * 2u;
break; break;
#ifdef MODPLUG_TRACKER
case CMD_OFFSET:
case CMD_REVERSEOFFSET:
if(m.instr && m.instr <= GetNumSamples() && Samples[m.instr].uFlags[CHN_16BIT])
m.param /= 2;
break;
#endif // MODPLUG_TRACKER
default: default:
break; break;
} }
} }
if(b & 0x80) if(b & 0x80)
{ {
m.volcmd = VOLCMD_VOLUME; m.SetVolumeCommand(VOLCMD_VOLUME, file.ReadUint8());
m.vol = file.ReadUint8();
} }
} }
} }

View file

@ -283,9 +283,16 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
// though several ST3.01/3.03 files with ultra-click values of 16 have been found as well. // though several ST3.01/3.03 files with ultra-click values of 16 have been found as well.
// However, we won't fingerprint these values here as it's unlikely that there is any other tracker out there disguising as ST3 and using a strange ultra-click value. // However, we won't fingerprint these values here as it's unlikely that there is any other tracker out there disguising as ST3 and using a strange ultra-click value.
// Also, re-saving a file with a strange ultra-click value in ST3 doesn't fix this value unless the user manually changes it, or if it's below 16. // Also, re-saving a file with a strange ultra-click value in ST3 doesn't fix this value unless the user manually changes it, or if it's below 16.
isST3 = true;
if(fileHeader.cwtv == S3MFileHeader::trkST3_20)
{
// 3.21 writes the version number as 3.20. There is no known way to differentiate between the two.
madeWithTracker = UL_("Scream Tracker 3.20 - 3.21");
} else
{
madeWithTracker = UL_("Scream Tracker"); madeWithTracker = UL_("Scream Tracker");
formatTrackerStr = true; formatTrackerStr = true;
isST3 = true; }
} }
break; break;
case S3MFileHeader::trkImagoOrpheus: case S3MFileHeader::trkImagoOrpheus:
@ -297,17 +304,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
nonCompatTracker = true; nonCompatTracker = true;
break; break;
case S3MFileHeader::trkImpulseTracker: case S3MFileHeader::trkImpulseTracker:
if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14) if(fileHeader.cwtv == S3MFileHeader::trkIT1_old)
{
madeWithTracker = UL_("Impulse Tracker");
formatTrackerStr = true;
} else if(fileHeader.cwtv == S3MFileHeader::trkIT1_old)
{
madeWithTracker = UL_("Impulse Tracker 1.03"); // Could also be 1.02, maybe? I don't have that one madeWithTracker = UL_("Impulse Tracker 1.03"); // Could also be 1.02, maybe? I don't have that one
} else else
{ madeWithTracker = GetImpulseTrackerVersion(fileHeader.cwtv, 0);
madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14);
}
if(fileHeader.cwtv >= S3MFileHeader::trkIT2_07 && fileHeader.reserved3 != 0) if(fileHeader.cwtv >= S3MFileHeader::trkIT2_07 && fileHeader.reserved3 != 0)
{ {
// Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field // Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field
@ -724,7 +725,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
else if(m.param > 0x08) else if(m.param > 0x08)
zxxCountRight++; zxxCountRight++;
} }
} else if(m.command == CMD_OFFSET && m.param == 0 && fileHeader.cwtv <= S3MFileHeader::trkST3_01) } else if(m.command == CMD_OFFSET && m.param == 0 && isST3 && fileHeader.cwtv <= S3MFileHeader::trkST3_01)
{ {
// Offset command didn't have effect memory in ST3.01; fixed in ST3.03 // Offset command didn't have effect memory in ST3.01; fixed in ST3.03
m.command = CMD_DUMMY; m.command = CMD_DUMMY;
@ -930,6 +931,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
const auto rowBase = Patterns[pat].GetRow(row); const auto rowBase = Patterns[pat].GetRow(row);
CHANNELINDEX writeChannels = std::min(CHANNELINDEX(32), GetNumChannels()); CHANNELINDEX writeChannels = std::min(CHANNELINDEX(32), GetNumChannels());
bool writePatternBreak = (Patterns[pat].GetNumRows() < 64 && row + 1 == Patterns[pat].GetNumRows() && !Patterns[pat].RowHasJump(row));
for(CHANNELINDEX chn = 0; chn < writeChannels; chn++) for(CHANNELINDEX chn = 0; chn < writeChannels; chn++)
{ {
const ModCommand &m = rowBase[chn]; const ModCommand &m = rowBase[chn];
@ -995,6 +997,12 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
} }
} }
} }
if(writePatternBreak && !(info & s3mEffectPresent))
{
info |= s3mEffectPresent;
command = 'C' ^ 0x40;
writePatternBreak = false;
}
if(info & s3mAnyPresent) if(info & s3mAnyPresent)
{ {

View file

@ -54,7 +54,7 @@ MPT_BINARY_STRUCT(SFXOrderHeader, 130)
struct SFXSampleHeader struct SFXSampleHeader
{ {
char name[22]; char name[22];
char dummy[2]; // Supposedly sample length, but almost always incorrect uint16be oneshotLength; // For unlooped samples, this is quite frequently 2 bytes shorter than the sample data length (and the last two samples would cause a click to be heard)
uint8be finetune; uint8be finetune;
uint8be volume; uint8be volume;
uint16be loopStart; uint16be loopStart;
@ -64,7 +64,7 @@ struct SFXSampleHeader
void ConvertToMPT(ModSample &mptSmp, uint32 length) const void ConvertToMPT(ModSample &mptSmp, uint32 length) const
{ {
mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.Initialize(MOD_TYPE_MOD);
mptSmp.nLength = length; mptSmp.nLength = (loopLength > 1) ? length : (oneshotLength * 2u);
mptSmp.nFineTune = MOD2XMFineTune(finetune); mptSmp.nFineTune = MOD2XMFineTune(finetune);
mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
@ -434,14 +434,18 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
// Reading samples // Reading samples
if(loadFlags & loadSampleData) if(loadFlags & loadSampleData)
{ {
for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) if(Samples[smp].nLength) for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{ {
if(!sampleLen[smp - 1])
continue;
FileReader chunk = file.ReadChunk(sampleLen[smp - 1]);
SampleIO( SampleIO(
SampleIO::_8bit, SampleIO::_8bit,
SampleIO::mono, SampleIO::mono,
SampleIO::littleEndian, SampleIO::littleEndian,
SampleIO::signedPCM) SampleIO::signedPCM)
.ReadSample(Samples[smp], file); .ReadSample(Samples[smp], chunk);
} }
} }

View file

@ -236,9 +236,24 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
m_modFormat.formatName = U_("Scream Tracker 2"); m_modFormat.formatName = U_("Scream Tracker 2");
m_modFormat.type = U_("stm"); m_modFormat.type = U_("stm");
m_modFormat.madeWithTracker = MPT_UFORMAT("Scream Tracker {}.{}")(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor));
m_modFormat.charset = mpt::Charset::CP437; m_modFormat.charset = mpt::Charset::CP437;
if(!std::memcmp(fileHeader.trackerName, "!Scream!", 8))
{
if(fileHeader.verMinor >= 21)
m_modFormat.madeWithTracker = UL_("Scream Tracker 2.2 - 2.3 or compatible");
else
m_modFormat.madeWithTracker = MPT_UFORMAT("Scream Tracker {}.{} or compatible")(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor));
}
else if(!std::memcmp(fileHeader.trackerName, "BMOD2STM", 8))
m_modFormat.madeWithTracker = UL_("BMOD2STM");
else if(!std::memcmp(fileHeader.trackerName, "WUZAMOD!", 8))
m_modFormat.madeWithTracker = UL_("Wuzamod");
else if(!std::memcmp(fileHeader.trackerName, "SWavePro", 8))
m_modFormat.madeWithTracker = UL_("SoundWave Pro");
else
m_modFormat.madeWithTracker = UL_("Unknown");
m_playBehaviour.set(kST3SampleSwap); m_playBehaviour.set(kST3SampleSwap);
m_nSamples = 31; m_nSamples = 31;

View file

@ -980,6 +980,30 @@ static bool ConvertDSP(const SymEvent event, MIDIMacroConfigData::Macro &macro,
} }
static uint8 MapToClosestMidiMacro(const SymEvent event, std::map<SymEvent, uint8> &macroMap)
{
if(event.command == SymEvent::DSPDelay)
return 0;
uint8 bestMatch = 0;
uint32 bestDistance = uint32_max;
for(const auto &m : macroMap)
{
const auto &mapEvent = m.first;
if(event.command != mapEvent.command || event.note != mapEvent.note)
continue;
const uint32 diff1 = static_cast<uint32>(event.param) - mapEvent.param, diff2 = static_cast<uint32>(event.inst) - mapEvent.inst;
const uint32 distance = diff1 * diff1 + diff2 * diff2;
if(distance >= bestDistance)
continue;
bestMatch = m.second;
bestDistance = distance;
}
macroMap[event] = bestMatch;
return bestMatch;
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSymMOD(MemoryFileReader file, const uint64 *pfilesize) CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSymMOD(MemoryFileReader file, const uint64 *pfilesize)
{ {
MPT_UNREFERENCED_PARAMETER(pfilesize); MPT_UNREFERENCED_PARAMETER(pfilesize);
@ -1044,6 +1068,7 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
uint16 sampleBoost = 2500; uint16 sampleBoost = 2500;
bool isSymphoniePro = false; bool isSymphoniePro = false;
bool externalSamples = false; bool externalSamples = false;
bool unknownHunks = false;
std::vector<SymPosition> positions; std::vector<SymPosition> positions;
std::vector<SymSequence> sequences; std::vector<SymSequence> sequences;
std::vector<SymEvent> patternData; std::vector<SymEvent> patternData;
@ -1202,9 +1227,10 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
file.Skip(file.ReadUint32BE()); file.Skip(file.ReadUint32BE());
break; break;
// Unrecognized chunk/value type // Unrecognized chunk/value type (e.g. garbage at the end of Natsh1.SymMOD)
default: default:
return false; unknownHunks = true;
break;
} }
} }
@ -1212,6 +1238,8 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
return false; return false;
if((loadFlags & loadPatternData) && (positions.empty() || patternData.empty() || sequences.empty())) if((loadFlags & loadPatternData) && (positions.empty() || patternData.empty() || sequences.empty()))
return false; return false;
if(unknownHunks)
AddToLog(LogWarning, U_("Unknown hunks were found and ignored."));
// Let's hope noone is going to use the 256th instrument ;) // Let's hope noone is going to use the 256th instrument ;)
if(instruments.size() >= MAX_INSTRUMENTS) if(instruments.size() >= MAX_INSTRUMENTS)
@ -1641,10 +1669,9 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
case SymEvent::DSPEcho: case SymEvent::DSPEcho:
case SymEvent::DSPDelay: case SymEvent::DSPDelay:
#endif #endif
if(macroMap.count(event)) if(auto it = macroMap.find(event); it != macroMap.end() && it->second != 0)
{ {
m.command = CMD_MIDI; m.SetEffectCommand(CMD_MIDI, it->second);
m.param = macroMap[event];
} else if(macroMap.size() < m_MidiCfg.Zxx.size()) } else if(macroMap.size() < m_MidiCfg.Zxx.size())
{ {
uint8 param = static_cast<uint8>(macroMap.size()); uint8 param = static_cast<uint8>(macroMap.size());
@ -1656,6 +1683,9 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
if(event.command == SymEvent::DSPEcho || event.command == SymEvent::DSPDelay) if(event.command == SymEvent::DSPEcho || event.command == SymEvent::DSPDelay)
useDSP = true; useDSP = true;
} }
} else if(uint8 param = MapToClosestMidiMacro(event, macroMap))
{
m.SetEffectCommand(CMD_MIDI, param);
} }
break; break;

View file

@ -197,7 +197,9 @@ static std::pair<EffectCommand, uint8> TranslateULTCommands(const uint8 e, uint8
} }
static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) struct ULTEventResult { uint8 repeat = 0; ModCommand::COMMAND lostCommand = CMD_NONE; ModCommand::PARAM lostParam = 0; };
static ULTEventResult ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
{ {
uint8 repeat = 1; uint8 repeat = 1;
uint8 b = file.ReadUint8(); uint8 b = file.ReadUint8();
@ -222,7 +224,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset)); m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset));
if(offset > 0xFF) if(offset > 0xFF)
m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8)); m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat; return {repeat};
} else if(cmd1 == CMD_OFFSET) } else if(cmd1 == CMD_OFFSET)
{ {
uint32 offset = param1 * 4; uint32 offset = param1 * 4;
@ -231,7 +233,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
{ {
m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset)); m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset));
m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8)); m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat; return {repeat};
} }
} else if(cmd2 == CMD_OFFSET) } else if(cmd2 == CMD_OFFSET)
{ {
@ -241,7 +243,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
{ {
m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset)); m.SetEffectCommand(CMD_OFFSET, static_cast<ModCommand::PARAM>(offset));
m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8)); m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat; return {repeat};
} }
} else if(cmd1 == cmd2) } else if(cmd1 == cmd2)
{ {
@ -257,9 +259,8 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
// Combine slide commands, if possible // Combine slide commands, if possible
ModCommand::CombineEffects(cmd2, param2, cmd1, param1); ModCommand::CombineEffects(cmd2, param2, cmd1, param1);
m.FillInTwoCommands(cmd1, param1, cmd2, param2); const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2);
return {repeat, lostCommand.first, lostCommand.second};
return repeat;
} }
@ -388,8 +389,8 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName);
const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")};
m_modFormat.formatName = U_("UltraTracker"); m_modFormat.formatName = UL_("UltraTracker");
m_modFormat.type = U_("ult"); m_modFormat.type = UL_("ult");
m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1']; m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1'];
m_modFormat.charset = mpt::Charset::CP437; m_modFormat.charset = mpt::Charset::CP437;
@ -456,7 +457,10 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
ROWINDEX row = 0; ROWINDEX row = 0;
while(row < 64) while(row < 64)
{ {
int repeat = ReadULTEvent(evnote, file, fileHeader.version); const ULTEventResult eventResult = ReadULTEvent(evnote, file, fileHeader.version);
if(eventResult.lostCommand != CMD_NONE && ModCommand::IsGlobalCommand(eventResult.lostCommand, eventResult.lostParam))
Patterns[pat].WriteEffect(EffectWriter(eventResult.lostCommand, eventResult.lostParam).Row(row).RetryNextRow());
int repeat = eventResult.repeat;
if(repeat + row > 64) if(repeat + row > 64)
repeat = 64 - row; repeat = 64 - row;
if(repeat == 0) if(repeat == 0)

View file

@ -1135,8 +1135,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
#ifndef MODPLUG_NO_FILESAVE #ifndef MODPLUG_NO_FILESAVE
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0) #if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(15, 1, 0)
// work-around massively confused GCC 13 optimizer: // work-around massively confused GCC 13/14 optimizer:
// /usr/include/c++/13/bits/stl_algobase.h:437:30: warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' writing between 3 and 9223372036854775806 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=] // /usr/include/c++/13/bits/stl_algobase.h:437:30: warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' writing between 3 and 9223372036854775806 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
template <typename Tcont2, typename Tcont1> template <typename Tcont2, typename Tcont1>
static MPT_NOINLINE Tcont1 & gcc_append(Tcont1 & cont1, const Tcont2 & cont2) { static MPT_NOINLINE Tcont1 & gcc_append(Tcont1 & cont1, const Tcont2 & cont2) {
@ -1417,7 +1417,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
} }
} }
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0) #if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(15, 1, 0)
gcc_append(samples, additionalSamples); gcc_append(samples, additionalSamples);
#else #else
mpt::append(samples, additionalSamples); mpt::append(samples, additionalSamples);

View file

@ -41,6 +41,12 @@ void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType)
// So we have to disable the sustain loop if it was behind the normal loop. // So we have to disable the sustain loop if it was behind the normal loop.
dwFlags.reset(ENV_SUSTAIN); dwFlags.reset(ENV_SUSTAIN);
} }
if(!dwFlags[ENV_LOOP | ENV_SUSTAIN])
{
// XM has no automatic fade-out behaviour at the end of the envelope.
dwFlags.set(ENV_SUSTAIN);
nSustainStart = nSustainEnd = LastPoint();
}
// XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point // XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point
if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size()) if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size())
@ -73,11 +79,11 @@ int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int
if(empty()) if(empty())
return 0; return 0;
uint32 pt = size() - 1u; uint32 pt = LastPoint();
const int32 ENV_PRECISION = 1 << 16; const int32 ENV_PRECISION = 1 << 16;
// Checking where current 'tick' is relative to the envelope points. // Checking where current 'tick' is relative to the envelope points.
for(uint32 i = 0; i < size() - 1u; i++) for(uint32 i = 0; i < LastPoint(); i++)
{ {
if (position <= at(i).tick) if (position <= at(i).tick)
{ {
@ -129,12 +135,12 @@ void InstrumentEnvelope::Sanitize(uint8 maxValue)
it->tick = std::max(it->tick, (it - 1)->tick); it->tick = std::max(it->tick, (it - 1)->tick);
LimitMax(it->value, maxValue); LimitMax(it->value, maxValue);
} }
LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1)); LimitMax(nLoopEnd, LastPoint());
LimitMax(nLoopStart, nLoopEnd); LimitMax(nLoopStart, nLoopEnd);
LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1)); LimitMax(nSustainEnd, LastPoint());
LimitMax(nSustainStart, nSustainEnd); LimitMax(nSustainStart, nSustainEnd);
if(nReleaseNode != ENV_RELEASE_NODE_UNSET) if(nReleaseNode != ENV_RELEASE_NODE_UNSET)
LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1)); LimitMax(nReleaseNode, LastPoint());
} else } else
{ {
nLoopStart = 0; nLoopStart = 0;
@ -220,10 +226,11 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType)
} }
} }
// Limit fadeout length for IT // Limit fadeout length and precision for IT
if(toType & MOD_TYPE_IT) if(toType & MOD_TYPE_IT)
{ {
LimitMax(nFadeOut, 8192u); LimitMax(nFadeOut, 8192u);
nFadeOut = ((nFadeOut + 16) / 32) * 32;
} }
// MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats

View file

@ -61,6 +61,8 @@ struct InstrumentEnvelope : public std::vector<EnvelopeNode>
uint32 size() const { return static_cast<uint32>(std::vector<EnvelopeNode>::size()); } uint32 size() const { return static_cast<uint32>(std::vector<EnvelopeNode>::size()); }
uint8 LastPoint() const { return static_cast<uint8>(std::max(size(), uint32(1)) - 1); }
using std::vector<EnvelopeNode>::push_back; using std::vector<EnvelopeNode>::push_back;
void push_back(EnvelopeNode::tick_t tick, EnvelopeNode::value_t value) { emplace_back(tick, value); } void push_back(EnvelopeNode::tick_t tick, EnvelopeNode::value_t value) { emplace_back(tick, value); }
}; };

View file

@ -172,7 +172,7 @@ static mpt::ustring ReadMPG123String(const char (&str)[N])
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wframe-larger-than=16000" #pragma GCC diagnostic ignored "-Wframe-larger-than=16000"
#endif // MPT_COMPILER_GCC #endif // MPT_COMPILER_GCC
#if MPT_CLANG_AT_LEAST(13,0,0) #if (MPT_CLANG_AT_LEAST(13,0,0) && !defined(MPT_COMPILER_QUIRK_APPLE_CLANG)) || MPT_CLANG_AT_LEAST(13,1,0)
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wframe-larger-than" #pragma clang diagnostic ignored "-Wframe-larger-than"
#endif // MPT_COMPILER_CLANG #endif // MPT_COMPILER_CLANG
@ -180,7 +180,7 @@ static MPT_NOINLINE int mp3dec_decode_frame_no_inline(mp3dec_t *dec, const uint8
{ {
return mp3dec_decode_frame(dec, mp3, mp3_bytes, pcm, info); return mp3dec_decode_frame(dec, mp3, mp3_bytes, pcm, info);
} }
#if MPT_CLANG_AT_LEAST(13,0,0) #if (MPT_CLANG_AT_LEAST(13,0,0) && !defined(MPT_COMPILER_QUIRK_APPLE_CLANG)) || MPT_CLANG_AT_LEAST(13,1,0)
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif // MPT_COMPILER_CLANG #endif // MPT_COMPILER_CLANG
#if MPT_COMPILER_GCC #if MPT_COMPILER_GCC

View file

@ -988,6 +988,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
filenameModifier += P_(" (cross-fade)"); filenameModifier += P_(" (cross-fade)");
} }
const SmpLength origSampleLength = sample.nLength;
// Sample offset // Sample offset
if(region.offset && region.offset < sample.nLength) if(region.offset && region.offset < sample.nLength)
{ {
@ -1003,6 +1005,19 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
} }
LimitMax(sample.nLength, region.end); LimitMax(sample.nLength, region.end);
if(sample.nLength < origSampleLength && (origSampleLength - sample.nLength) >= 128 * 1024)
{
// If the sample was trimmed excessively, re-allocate to save memory.
// This is crucial for SFZs like those generated by Sforzando's SF2 conversion process,
// as the whole SF2 sample data chunk ends up in a single WAV file that is then referenced by each region and sliced accordingly.
if(auto newData = ModSample::AllocateSample(sample.nLength, sample.GetBytesPerSample()))
{
memcpy(newData, sample.samplev(), sample.nLength * sample.GetBytesPerSample());
sample.FreeSample();
sample.pData.pSample = newData;
}
}
if(region.invertPhase) if(region.invertPhase)
{ {
ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); ctrlSmp::InvertSample(sample, 0, sample.nLength, *this);

View file

@ -44,6 +44,12 @@ static uint32 GetLinearSlideUpTable (const CSoundFile *sndFile, uint32 i) {
static uint32 GetFineLinearSlideDownTable(const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideDownTable[i] : FineLinearSlideUpTable[i]; } static uint32 GetFineLinearSlideDownTable(const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideDownTable[i] : FineLinearSlideUpTable[i]; }
static uint32 GetFineLinearSlideUpTable (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideUpTable[i] : FineLinearSlideDownTable[i]; } static uint32 GetFineLinearSlideUpTable (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideUpTable[i] : FineLinearSlideDownTable[i]; }
// Minimum parameter of tempo command that is considered to be a BPM rather than a tempo slide
static constexpr TEMPO GetMinimumTempoParam(MODTYPE modType)
{
return (modType & (MOD_TYPE_MDL | MOD_TYPE_MED | MOD_TYPE_XM | MOD_TYPE_MOD)) ? TEMPO(1, 0) : TEMPO(32, 0);
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Length // Length
@ -678,7 +684,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
} }
const auto &specs = GetModSpecifications(); const auto &specs = GetModSpecifications();
if(tempo.GetInt() >= 0x20) if(tempo >= GetMinimumTempoParam(GetType()))
{ {
#if MPT_MSVC_BEFORE(2019, 0) #if MPT_MSVC_BEFORE(2019, 0)
// Work-around for VS2017 /std:c++17 /permissive- // Work-around for VS2017 /std:c++17 /permissive-
@ -5445,9 +5451,9 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const
param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart; param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart;
} }
if(GetType() == MOD_TYPE_MDL && chn.dwFlags[CHN_16BIT]) if((GetType() & (MOD_TYPE_MDL | MOD_TYPE_PTM)) && chn.dwFlags[CHN_16BIT])
{ {
// Digitrakker really uses byte offsets, not sample offsets. WTF! // Digitrakker and Polytracker use byte offsets, not sample offsets.
param /= 2u; param /= 2u;
} }
@ -5520,7 +5526,10 @@ void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) c
chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.set(CHN_PINGPONGFLAG);
chn.dwFlags.reset(CHN_LOOP); 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); SmpLength offset = param << 8;
if(GetType() == MOD_TYPE_PTM && chn.dwFlags[CHN_16BIT])
offset /= 2;
chn.position.Set((chn.nLength - 1) - std::min(offset, chn.nLength - SmpLength(1)), 0);
} }
} }
@ -5971,7 +5980,7 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI)
const CModSpecifications &specs = GetModSpecifications(); const CModSpecifications &specs = GetModSpecifications();
// Anything lower than the minimum tempo is considered to be a tempo slide // Anything lower than the minimum tempo is considered to be a tempo slide
const TEMPO minTempo = (GetType() & (MOD_TYPE_MDL | MOD_TYPE_MED | MOD_TYPE_MOD)) ? TEMPO(1, 0) : TEMPO(32, 0); const TEMPO minTempo = GetMinimumTempoParam(GetType());
if(setFromUI) if(setFromUI)
{ {

View file

@ -978,6 +978,7 @@ public:
bool LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool* pInterpretMptMade = nullptr); bool LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool* pInterpretMptMade = nullptr);
void LoadMPTMProperties(FileReader &file, uint16 cwtv); void LoadMPTMProperties(FileReader &file, uint16 cwtv);
static mpt::ustring GetImpulseTrackerVersion(uint16 cwtv, uint16 cmwt);
static mpt::ustring GetSchismTrackerVersion(uint16 cwtv, uint32 reserved); static mpt::ustring GetSchismTrackerVersion(uint16 cwtv, uint32 reserved);
// Reads extended instrument properties(XM/IT/MPTM). // Reads extended instrument properties(XM/IT/MPTM).

View file

@ -1592,6 +1592,11 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int32 &period, Tuning::NOTEI
uint8 note = (GetType() != MOD_TYPE_MOD) ? chn.nNote : static_cast<uint8>(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed)); uint8 note = (GetType() != MOD_TYPE_MOD) ? chn.nNote : static_cast<uint8>(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed));
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI)) if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI))
tick += 2; tick += 2;
// SFX uses a 0-1-2-0-2-1 pattern (fixed at 6 ticks per row)
if(GetType() == MOD_TYPE_SFX && tick > 3)
tick ^= 3;
switch(tick % 3) switch(tick % 3)
{ {
case 1: note += (chn.nArpeggio >> 4); break; case 1: note += (chn.nArpeggio >> 4); break;
@ -1614,7 +1619,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int32 &period, Tuning::NOTEI
} }
period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed);
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM | MOD_TYPE_OKT)) if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM | MOD_TYPE_OKT | MOD_TYPE_SFX))
{ {
// The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2. // The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2.
// This fixes the flute lead in MORPH.STM by Skaven, pattern 27. // This fixes the flute lead in MORPH.STM by Skaven, pattern 27.
@ -1638,7 +1643,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOT
if(chn.dwFlags[CHN_VIBRATO]) if(chn.dwFlags[CHN_VIBRATO])
{ {
const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS])); const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_MED)) && !(m_SongFlags[SONG_ITOLDEFFECTS]));
if(GetType() == MOD_TYPE_669) if(GetType() == MOD_TYPE_669)
{ {
@ -2472,15 +2477,10 @@ bool CSoundFile::ReadNote()
{ {
int32 pan = (m_MixerSettings.gnChannels >= 2) ? Clamp(chn.nRealPan, 0, 256) : 128; int32 pan = (m_MixerSettings.gnChannels >= 2) ? Clamp(chn.nRealPan, 0, 256) : 128;
int32 realvol; int32 realvol = (chn.nRealVolume * kChnMasterVol) / 128;
if(m_PlayConfig.getUseGlobalPreAmp())
{
realvol = (chn.nRealVolume * kChnMasterVol) / 128;
} else
{
// Extra attenuation required here if we're bypassing pre-amp. // Extra attenuation required here if we're bypassing pre-amp.
realvol = (chn.nRealVolume * kChnMasterVol) / 256; if(!m_PlayConfig.getUseGlobalPreAmp())
} realvol /= 2;
const PanningMode panningMode = m_PlayConfig.getPanningMode(); const PanningMode panningMode = m_PlayConfig.getPanningMode();
if(panningMode == PanningMode::SoftPanning || (panningMode == PanningMode::Undetermined && (m_MixerSettings.MixerFlags & SNDMIX_SOFTPANNING))) if(panningMode == PanningMode::SoftPanning || (panningMode == PanningMode::Undetermined && (m_MixerSettings.MixerFlags & SNDMIX_SOFTPANNING)))
@ -2501,6 +2501,7 @@ bool CSoundFile::ReadNote()
// you can never truly achieve 100% right panning in FT2, only 100% left. // you can never truly achieve 100% right panning in FT2, only 100% left.
// Test case: FT2PanLaw.xm // Test case: FT2PanLaw.xm
LimitMax(pan, 255); LimitMax(pan, 255);
const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536; const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536;
const int panR = XMPanningTable[pan]; const int panR = XMPanningTable[pan];
chn.newLeftVol = (realvol * panL) / 65536; chn.newLeftVol = (realvol * panL) / 65536;
@ -2636,11 +2637,11 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
// Check for volume commands // Check for volume commands
uint8 vol = 0xFF; uint8 vol = 0xFF;
if(chn.rowCommand.volcmd == VOLCMD_VOLUME) if(chn.rowCommand.volcmd == VOLCMD_VOLUME)
vol = std::min(chn.rowCommand.vol, uint8(64)); vol = std::min(chn.rowCommand.vol, uint8(64)) * 2u;
else if(chn.rowCommand.command == CMD_VOLUME) else if(chn.rowCommand.command == CMD_VOLUME)
vol = std::min(chn.rowCommand.param, uint8(64)); vol = std::min(chn.rowCommand.param, uint8(64)) * 2u;
else if(chn.rowCommand.command == CMD_VOLUME8) else if(chn.rowCommand.command == CMD_VOLUME8)
vol = static_cast<uint8>((chn.rowCommand.param + 3u) / 4u); vol = static_cast<uint8>((chn.rowCommand.param + 1u) / 2u);
const bool hasVolCommand = (vol != 0xFF); const bool hasVolCommand = (vol != 0xFF);
@ -2654,7 +2655,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
SendMIDINote(nChn, realNote, static_cast<uint16>(chn.nVolume)); SendMIDINote(nChn, realNote, static_cast<uint16>(chn.nVolume));
} else if(hasVolCommand) } else if(hasVolCommand)
{ {
pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol, nChn); pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol / 2u, nChn);
} }
return; return;
} }
@ -2668,7 +2669,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
switch(pIns->pluginVelocityHandling) switch(pIns->pluginVelocityHandling)
{ {
case PLUGIN_VELOCITYHANDLING_CHANNEL: case PLUGIN_VELOCITYHANDLING_CHANNEL:
velocity = chn.nVolume; velocity = hasVolCommand ? vol * 2 : chn.nVolume;
break; break;
default: default:
break; break;
@ -2696,11 +2697,11 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
switch(pIns->pluginVolumeHandling) switch(pIns->pluginVolumeHandling)
{ {
case PLUGIN_VOLUMEHANDLING_DRYWET: case PLUGIN_VOLUMEHANDLING_DRYWET:
if(hasVolCommand) pPlugin->SetDryRatio(1.0f - (2 * vol) / 127.0f); if(hasVolCommand) pPlugin->SetDryRatio(1.0f - vol / 127.0f);
else pPlugin->SetDryRatio(1.0f - (2 * defaultVolume) / 127.0f); else pPlugin->SetDryRatio(1.0f - (2 * defaultVolume) / 127.0f);
break; break;
case PLUGIN_VOLUMEHANDLING_MIDI: case PLUGIN_VOLUMEHANDLING_MIDI:
if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), static_cast<uint8>(2 * vol)), nChn); if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), vol), nChn);
else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast<uint8>(std::min(uint32(127), static_cast<uint32>(2 * defaultVolume))), nChn); else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast<uint8>(std::min(uint32(127), static_cast<uint32>(2 * defaultVolume))), nChn);
break; break;
default: default:

View file

@ -72,6 +72,7 @@ static constexpr ModFormatInfo modFormatInfo[] =
{ UL_("Digital Tracker"), "dtm" }, { UL_("Digital Tracker"), "dtm" },
{ UL_("Farandole Composer"), "far" }, { UL_("Farandole Composer"), "far" },
{ UL_("FM Tracker"), "fmt" }, { UL_("FM Tracker"), "fmt" },
{ UL_("ProTracker"), "fst" },
{ UL_("Imago Orpheus"), "imf" }, { UL_("Imago Orpheus"), "imf" },
{ UL_("Ice Tracker"), "ice" }, { UL_("Ice Tracker"), "ice" },
#ifdef MPT_EXTERNAL_SAMPLES #ifdef MPT_EXTERNAL_SAMPLES

View file

@ -327,7 +327,7 @@ constexpr CModSpecifications it_ =
255, // Max tempo 255, // Max tempo
1, // Min Speed 1, // Min Speed
255, // Max Speed 255, // Max Speed
1, // Min pattern rows 32, // Min pattern rows
200, // Max pattern rows 200, // Max pattern rows
25, // Max mod name length 25, // Max mod name length
25, // Max sample name length 25, // Max sample name length

View file

@ -37,21 +37,32 @@ CHANNELINDEX CPattern::GetNumChannels() const noexcept
bool CPattern::IsEmptyRow(ROWINDEX row) const noexcept bool CPattern::IsEmptyRow(ROWINDEX row) const noexcept
{ {
if(m_ModCommands.empty() || !IsValidRow(row)) if(m_ModCommands.empty() || !IsValidRow(row))
{
return true; return true;
}
for(const auto &m : GetRow(row)) for(const auto &m : GetRow(row))
{ {
if(!m.IsEmpty()) if(!m.IsEmpty())
{
return false; return false;
} }
}
return true; return true;
} }
// Check if the row contains any position jumps or pattern breaks.
bool CPattern::RowHasJump(ROWINDEX row) const noexcept
{
if(m_ModCommands.empty() || !IsValidRow(row))
return false;
for(const auto &m : GetRow(row))
{
if(m.command == CMD_PATTERNBREAK || m.command == CMD_POSITIONJUMP)
return true;
}
return false;
}
bool CPattern::SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure) noexcept bool CPattern::SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure) noexcept
{ {
if(rowsPerBeat < 1 if(rowsPerBeat < 1

View file

@ -60,6 +60,8 @@ public:
// Check if there is any note data on a given row. // Check if there is any note data on a given row.
bool IsEmptyRow(ROWINDEX row) const noexcept; bool IsEmptyRow(ROWINDEX row) const noexcept;
// Check if the row contains any position jumps or pattern breaks.
bool RowHasJump(ROWINDEX row) const noexcept;
// Allocate new pattern memory and replace old pattern data. // Allocate new pattern memory and replace old pattern data.
bool AllocatePattern(ROWINDEX rows); bool AllocatePattern(ROWINDEX rows);

View file

@ -238,7 +238,7 @@ void VSTPluginLib::WriteToCache() const
const std::string crcName = dllPath.ToUTF8(); const std::string crcName = dllPath.ToUTF8();
const mpt::crc32 crc(crcName); const mpt::crc32 crc(crcName);
const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc.result()); const mpt::ustring IDs = mpt::ufmt::HEX0<8>(static_cast<uint32>(pluginId1)) + mpt::ufmt::HEX0<8>(static_cast<uint32>(pluginId2)) + mpt::ufmt::HEX0<8>(crc.result());
mpt::PathString writePath = dllPath; mpt::PathString writePath = dllPath;
if(theApp.IsPortableMode()) if(theApp.IsPortableMode())
@ -570,19 +570,20 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, bool
pluginList.push_back(plug); pluginList.push_back(plug);
// Extract plugin IDs // Extract plugin IDs
uint32 id1 = 0, id2 = 0;
for(int i = 0; i < 16; i++) for(int i = 0; i < 16; i++)
{ {
int32 n = IDs[i] - '0'; uint32 n = IDs[i] - '0';
if (n > 9) n = IDs[i] + 10 - 'A'; if(n > 9)
n &= 0x0f; n = IDs[i] + 10 - 'A';
n &= 0x0F;
if (i < 8) if (i < 8)
{ id1 = (id1 << 4) | n;
plug->pluginId1 = (plug->pluginId1 << 4) | n; else
} else id2 = (id2 << 4) | n;
{
plug->pluginId2 = (plug->pluginId2 << 4) | n;
}
} }
plug->pluginId1 = id1;
plug->pluginId2 = id2;
const mpt::ustring flagKey = IDs + U_(".Flags"); const mpt::ustring flagKey = IDs + U_(".Flags");
plug->DecodeCacheFlags(cacheFile.Read<int32>(cacheSection, flagKey, 0)); plug->DecodeCacheFlags(cacheFile.Read<int32>(cacheSection, flagKey, 0));

View file

@ -25,7 +25,7 @@
#if defined(_M_ARM64) #if defined(_M_ARM64) || defined(_M_ARM64EC)
#define MPT_ARCH_AARCH64 1 #define MPT_ARCH_AARCH64 1
#elif defined(_M_ARM) #elif defined(_M_ARM)

View file

@ -50,7 +50,9 @@
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define MPT_COMPILER_MSVC 1 #define MPT_COMPILER_MSVC 1
#if (_MSC_VER >= 1940) #if (_MSC_VER >= 1941)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 11)
#elif (_MSC_VER >= 1940)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 10) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 10)
#elif (_MSC_VER >= 1939) #elif (_MSC_VER >= 1939)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 9) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 9)
@ -204,6 +206,16 @@
// detect compiler quirks
#if MPT_COMPILER_CLANG
#if defined(__APPLE__)
#define MPT_COMPILER_QUIRK_APPLE_CLANG
#endif
#endif
// detect compiler setting quirks // detect compiler setting quirks
#if MPT_COMPILER_GCC #if MPT_COMPILER_GCC

View file

@ -52,13 +52,14 @@
#define MPT_WIN_10_1903 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x07) // NTDDI_WIN10_19H1 1903/19H1 #define MPT_WIN_10_1903 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x07) // NTDDI_WIN10_19H1 1903/19H1
#define MPT_WIN_10_1909 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x08) // NTDDI_WIN10_VB 1909/19H2 #define MPT_WIN_10_1909 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x08) // NTDDI_WIN10_VB 1909/19H2
#define MPT_WIN_10_2004 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x09) // NTDDI_WIN10_MN 2004/20H1 #define MPT_WIN_10_2004 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x09) // NTDDI_WIN10_MN 2004/20H1
// 20H2 #define MPT_WIN_10_20H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0a) // NTDDI_WIN10_FE 20H2
// 21H1 #define MPT_WIN_10_21H1 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0b) // NTDDI_WIN10_CO 21H1
#define MPT_WIN_10_21H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0a) // NTDDI_WIN10_FE 21H2 #define MPT_WIN_10_21H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0c) // NTDDI_WIN10_NI 21H2
// 22H2 #define MPT_WIN_10_22H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0d) // NTDDI_WIN10_CU 22H2
#define MPT_WIN_11 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0b) // NTDDI_WIN10_CO 21H2 #define MPT_WIN_11 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0e) // NTDDI_WIN11_ZN 21H2
#define MPT_WIN_11_22H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0c) // NTDDI_WIN10_NI 22H2 #define MPT_WIN_11_22H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0f) // NTDDI_WIN11_GA 22H2
#define MPT_WIN_11_23H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x10) // NTDDI_WIN11_GE 23H2
// MPT_WIN_API_DESKTOP : Windows 8/10 Desktop Application (Win32) // MPT_WIN_API_DESKTOP : Windows 8/10 Desktop Application (Win32)
// MPT_WIN_API_UNIVERSAL : Windows 10 Store App / Universal App // MPT_WIN_API_UNIVERSAL : Windows 10 Store App / Universal App
@ -227,13 +228,25 @@ static_assert(NTDDI_WIN10_VB == MPT_WIN_10_1909);
static_assert(NTDDI_WIN10_MN == MPT_WIN_10_2004); static_assert(NTDDI_WIN10_MN == MPT_WIN_10_2004);
#endif #endif
#ifdef NTDDI_WIN10_FE #ifdef NTDDI_WIN10_FE
static_assert(NTDDI_WIN10_FE == MPT_WIN_10_21H2); static_assert(NTDDI_WIN10_FE == MPT_WIN_10_20H2);
#endif #endif
#ifdef NTDDI_WIN10_CO #ifdef NTDDI_WIN10_CO
static_assert(NTDDI_WIN10_CO == MPT_WIN_11); static_assert(NTDDI_WIN10_CO == MPT_WIN_10_21H1);
#endif #endif
#ifdef NTDDI_WIN10_NI #ifdef NTDDI_WIN10_NI
static_assert(NTDDI_WIN10_NI == MPT_WIN_11_22H2); static_assert(NTDDI_WIN10_NI == MPT_WIN_10_21H2);
#endif
#ifdef NTDDI_WIN10_CU
static_assert(NTDDI_WIN10_CU == MPT_WIN_10_22H2);
#endif
#ifdef NTDDI_WIN11_ZN
static_assert(NTDDI_WIN11_ZN == MPT_WIN_11);
#endif
#ifdef NTDDI_WIN11_GA
static_assert(NTDDI_WIN11_GA == MPT_WIN_11_22H2);
#endif
#ifdef NTDDI_WIN11_GE
static_assert(NTDDI_WIN11_GE == MPT_WIN_11_23H2);
#endif #endif
#endif #endif
#if defined(WINAPI_FAMILY) #if defined(WINAPI_FAMILY)

View file

@ -312,4 +312,10 @@
#if MPT_OS_ANDROID && MPT_LIBCXX_LLVM_BEFORE(17000)
#define MPT_LIBCXX_QUIRK_NO_NUMBERS
#endif
#endif // MPT_BASE_DETECT_QUIRKS_HPP #endif // MPT_BASE_DETECT_QUIRKS_HPP

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ /* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_EMPTY_HPP #ifndef MPT_BASE_MATH_HPP
#define MPT_BASE_EMPTY_HPP #define MPT_BASE_MATH_HPP
@ -85,4 +85,4 @@ inline T safe_clamp(T v, T lo, T hi) {
#endif // MPT_BASE_EMPTY_HPP #endif // MPT_BASE_MATH_HPP

View file

@ -6,9 +6,10 @@
#include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_quirks.hpp"
#include "mpt/base/namespace.hpp" #include "mpt/base/namespace.hpp"
#if MPT_CXX_AT_LEAST(20) #if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_NUMBERS)
#include <numbers> #include <numbers>
#else #else
#include <type_traits> #include <type_traits>
@ -25,7 +26,7 @@ inline namespace MPT_INLINE_NS {
namespace numbers { namespace numbers {
#if MPT_CXX_AT_LEAST(20) #if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_NUMBERS)
template <typename T> template <typename T>
inline constexpr T e_v = std::numbers::e_v<T>; inline constexpr T e_v = std::numbers::e_v<T>;

View file

@ -131,18 +131,20 @@ public:
static mpt::osinfo::windows::Version FromSDK() noexcept { static mpt::osinfo::windows::Version FromSDK() noexcept {
// Initialize to used SDK version // Initialize to used SDK version
#if MPT_WINNT_AT_LEAST(MPT_WIN_11_22H2) #if MPT_WINNT_AT_LEAST(MPT_WIN_11_23H2)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22631, 0);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_11_22H2)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22621, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22621, 0);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_11) // 21H2 #elif MPT_WINNT_AT_LEAST(MPT_WIN_11) // 21H2
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22000, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22000, 0);
//#elif // 22H2 #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_22H2)
// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19045, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19045, 0);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_10_21H2) #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_21H2)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19044, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19044, 0);
//#elif // 21H1 #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_21H1)
// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19043, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19043, 0);
//#elif // 20H2 #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_20H2)
// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19042, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19042, 0);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_10_2004) #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_2004)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19041, 0); return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19041, 0);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_10_1909) #elif MPT_WINNT_AT_LEAST(MPT_WIN_10_1909)

View file

@ -22,6 +22,7 @@
#if !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) #if !defined(MPT_LIBCXX_QUIRK_NO_CHRONO)
#include <chrono> #include <chrono>
#endif // !MPT_LIBCXX_QUIRK_NO_CHRONO #endif // !MPT_LIBCXX_QUIRK_NO_CHRONO
#include <iterator>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <random> #include <random>

View file

@ -7,6 +7,7 @@
#include "mpt/base/macros.hpp" #include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp" #include "mpt/base/namespace.hpp"
#include "mpt/base/numeric.hpp"
#include "mpt/random/seed.hpp" #include "mpt/random/seed.hpp"
#include <memory> #include <memory>
@ -47,7 +48,7 @@ struct engine_traits<std::mt19937> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd); std::unique_ptr<mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)>> values = std::make_unique<mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)>>(rd);
std::seed_seq seed(values->begin(), values->end()); std::seed_seq seed(values->begin(), values->end());
return rng_type(seed); return rng_type(seed);
} }
@ -65,7 +66,7 @@ struct engine_traits<std::mt19937_64> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd); std::unique_ptr<mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)>> values = std::make_unique<mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)>>(rd);
std::seed_seq seed(values->begin(), values->end()); std::seed_seq seed(values->begin(), values->end());
return rng_type(seed); return rng_type(seed);
} }
@ -83,7 +84,7 @@ struct engine_traits<std::ranlux24_base> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd); mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)> values(rd);
std::seed_seq seed(values.begin(), values.end()); std::seed_seq seed(values.begin(), values.end());
return rng_type(seed); return rng_type(seed);
} }
@ -101,7 +102,7 @@ struct engine_traits<std::ranlux48_base> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd); mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)> values(rd);
std::seed_seq seed(values.begin(), values.end()); std::seed_seq seed(values.begin(), values.end());
return rng_type(seed); return rng_type(seed);
} }
@ -119,7 +120,7 @@ struct engine_traits<std::ranlux24> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd); mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)> values(rd);
std::seed_seq seed(values.begin(), values.end()); std::seed_seq seed(values.begin(), values.end());
return rng_type(seed); return rng_type(seed);
} }
@ -137,7 +138,7 @@ struct engine_traits<std::ranlux48> {
} }
template <typename Trd> template <typename Trd>
static inline rng_type make(Trd & rd) { static inline rng_type make(Trd & rd) {
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd); mpt::seed_seq_values<mpt::align_up<std::size_t>(seed_bits, sizeof(unsigned int) * 8) / (sizeof(unsigned int) * 8)> values(rd);
std::seed_seq seed(values.begin(), values.end()); std::seed_seq seed(values.begin(), values.end());
return rng_type(seed); return rng_type(seed);
} }

View file

@ -8,8 +8,11 @@
#include "mpt/base/namespace.hpp" #include "mpt/base/namespace.hpp"
#include "mpt/random/engine.hpp" #include "mpt/random/engine.hpp"
#include <limits>
#include <type_traits> #include <type_traits>
#include <cstddef>
namespace mpt { namespace mpt {
inline namespace MPT_INLINE_NS { inline namespace MPT_INLINE_NS {

View file

@ -14,6 +14,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <iterator>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>

View file

@ -1537,6 +1537,7 @@
<string>dtm</string> <string>dtm</string>
<string>far</string> <string>far</string>
<string>fmt</string> <string>fmt</string>
<string>fst</string>
<string>imf</string> <string>imf</string>
<string>ice</string> <string>ice</string>
<string>j2b</string> <string>j2b</string>