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 6b8f5df721
commit 58cbda594a
61 changed files with 684 additions and 336 deletions

View file

@ -66,6 +66,7 @@
# ONLY_TEST=0 Only build the test suite.
# STRICT=0 Treat warnings as errors.
# MODERN=0 Pass more modern compiler options.
# ANCIENT=0 Pass compiler options compatible with older versions.
# NATIVE=0 Optimize for system CPU.
# STDCXX=c++17 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 += --std=c11 --std=c++17
CPPCHECK_FLAGS += --library=build/cppcheck/glibc-workarounds.cfg
CPPCHECK_FLAGS += --quiet
CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]'
CPPCHECK_FLAGS += --check-level=exhaustive
@ -1083,11 +1085,15 @@ CPPFLAGS += -DLIBOPENMPT_BUILD
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 src/mpt/src/*.cpp)) \
SOUNDLIB_CXX_SOURCES += \
$(COMMON_CXX_SOURCES) \
$(sort $(wildcard src/openmpt/soundbase/*.cpp)) \
$(sort $(wildcard soundlib/*.cpp)) \
$(sort $(wildcard soundlib/plugins/*.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/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/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/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
@ -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/stb_vorbis/stb_vorbis.c bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/STBVORB.TXT --native-eol CRLF
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
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

View file

@ -6,8 +6,45 @@ include $(CLEAR_VARS)
LOCAL_MODULE := openmpt
ifeq ($(NDK_MAJOR),)
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

View file

@ -1,6 +1,42 @@
ifeq ($(NDK_MAJOR),)
APP_CFLAGS := -std=c17
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_STL := c++_shared

View file

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

View file

@ -1,9 +1,9 @@
ifeq ($(origin CC),default)
CC = emcc -c
CC = emcc
endif
ifeq ($(origin CXX),default)
CXX = em++ -c
CXX = em++
endif
ifeq ($(origin LD),default)
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
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
CFLAGS_WARNINGS += -Wframe-larger-than=4000
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
endif
#CXXFLAGS_WARNINGS += -Wfloat-equal
#CXXFLAGS_WARNINGS += -Wdocumentation
@ -21,10 +26,14 @@ CFLAGS_SILENT += -Wno-cast-align
CFLAGS_SILENT += -Wno-cast-qual
CFLAGS_SILENT += -Wno-double-promotion
CFLAGS_SILENT += -Wno-float-conversion
ifneq ($(ANCIENT),1)
CFLAGS_SILENT += -Wno-frame-larger-than
endif
CFLAGS_SILENT += -Wno-missing-prototypes
CFLAGS_SILENT += -Wno-sign-compare
ifneq ($(ANCIENT),1)
CFLAGS_SILENT += -Wno-unused-but-set-variable
endif
CFLAGS_SILENT += -Wno-unused-function
CFLAGS_SILENT += -Wno-unused-parameter
CFLAGS_SILENT += -Wno-unused-variable

View file

@ -1,10 +1,10 @@
#pragma once
#define OPENMPT_VERSION_SVNVERSION "21223"
#define OPENMPT_VERSION_REVISION 21223
#define OPENMPT_VERSION_SVNVERSION "21953"
#define OPENMPT_VERSION_REVISION 21953
#define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9"
#define OPENMPT_VERSION_DATE "2024-07-21T12:01:13.335584Z"
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.11"
#define OPENMPT_VERSION_DATE "2024-10-26T13:09:27.804941Z"
#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_os.hpp"
#include "mpt/base/detect_quirks.hpp"
@ -229,14 +230,14 @@
#endif
#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_INTRINSICS_SSE
#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

View file

@ -18,6 +18,8 @@
#include "mpt/base/array.hpp"
#include "mpt/base/bit.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/memory.hpp"
#include "mpt/base/numeric.hpp"
@ -115,7 +117,7 @@ namespace Util {
// MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul.
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())
{
return static_cast<int64>(a) * b;
@ -130,7 +132,7 @@ namespace Util {
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())
{
return static_cast<uint64>(a) * b;

View file

@ -615,9 +615,11 @@ mpt::ustring GetFullCreditsString()
"\n"
"Additional contributors:\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"
"kode54 (https://kode54.net/)\n"
"Revenant (https://revenant1.net/)\n"
"SYRiNX\n"
"xaimus (http://xaimus.com/)\n"
"\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.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 31
#define VER_MINOR 09
#define VER_MINOR 11
#define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END

View file

@ -1,6 +1,12 @@
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:
* 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 */

View file

@ -8,6 +8,10 @@
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define MINIMP3_MAX_SAMPLES_PER_FRAME (1152*2)
typedef struct
@ -22,10 +26,6 @@ typedef struct
unsigned char header[4], reserv_buf[511];
} mp3dec_t;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void mp3dec_init(mp3dec_t *dec);
#ifndef MINIMP3_FLOAT_OUTPUT
typedef int16_t mp3d_sample_t;
@ -176,11 +176,7 @@ end:
#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)))
typedef float32x4_t f4;
#if 1 /* OpenMPT */
static int have_simd(void)
#else /* OpenMPT */
static int have_simd()
#endif /* OpenMPT */
{ /* TODO: detect neon for !MINIMP3_ONLY_SIMD */
return 1;
}
@ -195,7 +191,7 @@ static int have_simd()
#define HAVE_SIMD 0
#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
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 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++)
{
@ -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)
{
float kl, kr, s = HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
if (HDR_TEST_MPEG1(hdr))
if (mpeg1)
{
kl = g_pan[2*ipos];
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)
{
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);
if (i + HDR_SIZE > mp3_bytes)
return nmatch > 0;
if (hdr_is_null_or_tag(hdr + i))
return nmatch > 0;
if (!hdr_compare(hdr, hdr + i))
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)
{
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);
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_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++)
{
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;
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]);
memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
memset(scratch.grbuf, 0, sizeof(scratch.grbuf));
pcm += 384*info->channels;
}
if (bs_frame->pos > bs_frame->limit)

View file

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

View file

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=7
LIBOPENMPT_VERSION_PATCH=9
LIBOPENMPT_VERSION_PATCH=11
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=4
LIBOPENMPT_LTVER_REVISION=9
LIBOPENMPT_LTVER_REVISION=11
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 [] ) {
#endif
#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());
_crt0_startup_flags &= ~_CRT0_FLAG_LOCK_MEMORY; /* disable automatic locking for all further memory allocations */
#endif /* MPT_OS_DJGPP */
std::vector<mpt::ustring> args;
#if MPT_OS_WINDOWS && defined(UNICODE)
@ -2301,6 +2301,10 @@ static int main( int argc, char * argv [] ) {
return 0;
} catch ( silent_exit_exception & ) {
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 ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout();
@ -2468,6 +2472,10 @@ static int main( int argc, char * argv [] ) {
#endif
} catch ( silent_exit_exception & ) {
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 ) {
std_err << MPT_USTRING("error: ") << mpt::get_exception_text<mpt::ustring>( e ) << lf;
std_err.writeout();

View file

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

View file

@ -117,7 +117,7 @@ private:
case match_recurse:
break;
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 ) ) {
return matched_result( format, format_info, subformat_info, match_mode );
} else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) {
@ -126,7 +126,7 @@ private:
}
break;
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 ) ) {
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 ) ) ) {
@ -135,7 +135,7 @@ private:
}
break;
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 );
}
break;

View file

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

View file

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

View file

@ -616,15 +616,22 @@ CDLSBank::CDLSBank()
bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
{
RIFFChunkID riff;
if(filename.empty()) return false;
mpt::ifstream f(filename, std::ios::binary);
if(!f)
{
if(filename.empty())
return false;
}
MemsetZero(riff);
mpt::IO::Read(f, riff);
mpt::IO::InputFile f(filename, false);
if(!f.IsValid())
return false;
return IsDLSBank(GetFileReader(f));
}
bool CDLSBank::IsDLSBank(FileReader file)
{
file.Rewind();
RIFFChunkID riff;
if(!file.ReadStruct(riff))
return false;
// Check for embedded DLS sections
if(riff.id_RIFF == IFFID_FORM)
{
@ -632,28 +639,31 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
do
{
uint32 len = mpt::bit_cast<uint32be>(riff.riff_len);
if (len <= 4) break;
if (riff.id_DLS == IFFID_XDLS)
if(len <= 4) break;
if(riff.id_DLS == IFFID_XDLS)
{
mpt::IO::Read(f, riff);
break;
if(!file.ReadStruct(riff))
return false;
break; // found it
}
if((len % 2u) != 0)
len++;
if (!mpt::IO::SeekRelative(f, len-4)) break;
} while (mpt::IO::Read(f, riff));
if(!file.Skip(len - 4))
return false;
} while(file.ReadStruct(riff));
} else if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID)
{
for (;;)
{
if(!mpt::IO::Read(f, riff))
break;
if (riff.id_DLS == IFFID_DLS)
break; // found it
if(!file.ReadStruct(riff))
return false;
if(riff.id_DLS == IFFID_DLS || riff.id_DLS == IFFID_sfbk)
break; // found it
int len = riff.riff_len;
if((len % 2u) != 0)
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)
@ -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];
if(pIns == nullptr)
@ -747,7 +757,7 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co
|| FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns))
{
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.
if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames)))
@ -1394,7 +1404,7 @@ bool CDLSBank::Open(const mpt::PathString &filename)
{
if(filename.empty()) return false;
m_szFileName = filename;
mpt::IO::InputFile f(filename, SettingCacheCompleteFileBeforeLoading());
mpt::IO::InputFile f(filename, false);
if(!f.IsValid()) return false;
return Open(GetFileReader(f));
}
@ -1408,12 +1418,8 @@ bool CDLSBank::Open(FileReader file)
m_szFileName = file.GetOptionalFileName().value();
file.Rewind();
size_t dwMemLength = file.GetLength();
size_t dwMemPos = 0;
if(!file.CanRead(256))
{
return false;
}
RIFFChunkID riff;
file.ReadStruct(riff);
@ -1422,11 +1428,9 @@ bool CDLSBank::Open(FileReader file)
{
while(file.ReadStruct(riff))
{
if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_DLS)
{
file.SkipBack(sizeof(riff));
if(riff.id_RIFF == IFFID_RIFF && (riff.id_DLS == IFFID_DLS || riff.id_DLS == IFFID_sfbk))
break;
}
uint32 len = riff.riff_len;
if((len % 2u) != 0)
len++;
@ -1469,13 +1473,12 @@ bool CDLSBank::Open(FileReader file)
m_WaveForms.clear();
m_Envelopes.clear();
nInsDef = 0;
if (dwMemLength > 8 + riff.riff_len + dwMemPos) dwMemLength = 8 + riff.riff_len + dwMemPos;
bool applyPaddingToSampleChunk = true;
while(file.CanRead(sizeof(IFFCHUNK)))
{
IFFCHUNK chunkHeader;
file.ReadStruct(chunkHeader);
dwMemPos = file.GetPosition();
const auto chunkStartPos = file.GetPosition();
FileReader chunk = file.ReadChunk(chunkHeader.len);
bool applyPadding = (chunkHeader.len % 2u) != 0;
@ -1531,7 +1534,7 @@ bool CDLSBank::Open(FileReader file)
if (((listid == IFFID_wvpl) && (m_nType & SOUNDBANK_TYPE_DLS))
|| ((listid == IFFID_sdta) && (m_nType & SOUNDBANK_TYPE_SF2)))
{
m_dwWavePoolOffset = dwMemPos + 4;
m_dwWavePoolOffset = chunkStartPos + 4;
#ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Wave Pool offset: {}")(m_dwWavePoolOffset));
#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();
length = 0;
std::vector<uint8> waveData;
if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset)
{
#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()));
#endif
return false;
return waveData;
}
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if(nRgn >= dlsIns.Regions.size())
@ -1704,7 +1706,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
#ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size()));
#endif
return false;
return waveData;
}
uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink;
if(nWaveLink >= m_WaveForms.size())
@ -1712,29 +1714,34 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
#ifdef DLSBANK_LOG
MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Invalid wavelink id: nWaveLink={} nWaveForms={}")(nWaveLink, m_WaveForms.size()));
#endif
return false;
return waveData;
}
mpt::ifstream f(m_szFileName, std::ios::binary);
if(!f)
std::unique_ptr<mpt::IO::InputFile> inputFile;
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);
if(mpt::IO::SeekAbsolute(f, sampleOffset))
auto sampleOffset = mpt::saturate_cast<FileReader::pos_type>(m_WaveForms[nWaveLink] + m_dwWavePoolOffset);
if(f.Seek(sampleOffset))
{
if (m_nType & SOUNDBANK_TYPE_SF2)
{
if (m_SamplesEx[nWaveLink].dwLen)
{
if (mpt::IO::SeekRelative(f, 8))
if (f.Skip(8))
{
length = m_SamplesEx[nWaveLink].dwLen;
try
{
waveData.assign(length + 8, 0);
mpt::IO::ReadRaw(f, waveData.data(), length);
f.ReadVector(waveData, m_SamplesEx[nWaveLink].dwLen);
} catch(mpt::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
{
LISTChunk chunk;
if(mpt::IO::Read(f, chunk))
if(f.ReadStruct(chunk))
{
if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4))
{
length = chunk.len + 8;
try
{
waveData.assign(chunk.len + sizeof(IFFCHUNK), 0);
memcpy(waveData.data(), &chunk, sizeof(chunk));
mpt::IO::ReadRaw(f, waveData.data() + sizeof(chunk), length - sizeof(chunk));
f.SkipBack(sizeof(chunk));
f.ReadVector(waveData, chunk.len + sizeof(IFFCHUNK));
} catch(mpt::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;
if(nIns >= m_Instruments.size())
@ -1777,9 +1780,8 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if(nRgn >= dlsIns.Regions.size())
return false;
if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
return false;
if(dwLen < 16)
const std::vector<uint8> waveData = ExtractWaveForm(nIns, nRgn, file);
if(waveData.size() < 16)
return false;
ok = false;
@ -1798,10 +1800,10 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
#endif
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))
{
sample.nLength = dwLen / 2;
sample.nLength = mpt::saturate_cast<SmpLength>(waveData.size() / 2);
SampleIO(
SampleIO::_16bit,
SampleIO::mono,
@ -1822,8 +1824,8 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
hasWaveform = sample.HasSampleData();
} else
{
FileReader file(mpt::as_span(pWaveForm.data(), dwLen));
hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk);
FileReader wavChunk(mpt::as_span(waveData));
hasWaveform = sndFile.ReadWAVSample(nSample, wavChunk, false, &wsmpChunk);
if(dlsIns.szName[0])
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;
@ -2194,7 +2196,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
// Load the sample
if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData())
{
ExtractSample(sndFile, nSmp, nIns, nRgn, transpose);
ExtractSample(sndFile, nSmp, nIns, nRgn, transpose, file);
extractedSamples.insert(rgn.nWaveLink);
}
} 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 offsetNew = (pan1 < pan2) ? 0 : 1;
std::vector<uint8> pWaveForm;
uint32 dwLen = 0;
if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
const std::vector<uint8> waveData = ExtractWaveForm(nIns, nRgn, file);
if(waveData.empty())
continue;
extractedSamples.insert(rgn.nWaveLink);
@ -2230,8 +2231,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
// Now read the other channel
if(m_SamplesEx[m_Instruments[nIns].Regions[nRgn].nWaveLink].compressed)
{
FileReader file{mpt::as_span(pWaveForm)};
if(sndFile.ReadSampleFromFile(nSmp, file, false, false))
FileReader smpChunk{mpt::as_span(waveData)};
if(sndFile.ReadSampleFromFile(nSmp, smpChunk, false, false))
{
pDest = sampleCopy.sample16() + offsetNew;
const SmpLength copyLength = std::min(sample.nLength, sampleCopy.nLength);
@ -2242,10 +2243,10 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
}
} else
{
SmpLength len = std::min(dwLen / 2u, sampleCopy.nLength);
const std::byte *src = mpt::byte_cast<const std::byte *>(pWaveForm.data());
SmpLength len = std::min(mpt::saturate_cast<SmpLength>(waveData.size() / 2u), sampleCopy.nLength);
const std::byte *src = mpt::byte_cast<const std::byte *>(waveData.data());
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 = sampleCopy;

View file

@ -129,6 +129,7 @@ public:
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(FileReader file);
static uint32 MakeMelodicCode(uint32 bank, uint32 instr) { return ((bank << 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()); }
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;
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;
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) const;
bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const;
bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0, FileReader *file = nullptr) const;
bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn, FileReader *file = nullptr) const;
const char *GetRegionName(uint32 nIns, uint32 nRgn) const;
uint16 GetPanning(uint32 ins, uint32 region) const;
// Internal Loader Functions
protected:
std::vector<uint8> ExtractWaveForm(uint32 nIns, uint32 nRgn, FileReader *file) const;
bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk);
bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk);
bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info);

View file

@ -248,7 +248,15 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
uint8 command = effect[chn] >> 4;
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];
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif
} else
{
m->command = CMD_NONE;

View file

@ -24,7 +24,7 @@ struct DMFFileHeader
{
char signature[4]; // "DDMF"
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 composer[20];
uint8 creationDay;

View file

@ -126,6 +126,8 @@ static std::vector<std::byte> DecompressDSymLZW(FileReader &file, uint32 size)
// Align length to 4 bytes
file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3)));
// cppcheck false-positive
// cppcheck-suppress returnDanglingLifetime
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.
mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv, uint32 reserved)
{
@ -1027,6 +1047,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
auto patData = Patterns[pat].begin();
ROWINDEX row = 0;
ModCommand dummy{};
while(row < numRows && patternData.CanRead(1))
{
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.
ModCommand dummy{};
ModCommand &m = ch < m_nChannels ? patData[ch] : dummy;
if(chnMask[ch] & 0x10)
@ -1138,8 +1158,18 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
{
const auto [command, param] = patternData.ReadArray<uint8, 2>();
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 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
lastValue[ch].command = m.command;
lastValue[ch].param = m.param;
@ -1229,19 +1259,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
madeWithTracker = U_("Unknown");
} else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty())
{
if(fileHeader.cmwt > 0x0214)
{
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)));
}
madeWithTracker = GetImpulseTrackerVersion(fileHeader.cwtv, fileHeader.cmwt);
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
@ -1739,6 +1757,10 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
// Write pattern header
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;
uint16le patinfo[4];
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.
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;
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++)
{
// 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);
if (command) b |= 8;
}
if(writePatternBreak && !(b & 8))
{
b |= 8;
command = 'C' ^ 0x40;
writePatternBreak = false;
}
// Packing information
if (b)
{
@ -1895,11 +1924,25 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
break;
} else
{
dwPos += len;
writeSize += (uint16)len;
writeSize += static_cast<uint16>(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);
patinfo[0] = writeSize;

View file

@ -411,12 +411,12 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
switch(command)
{
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;
case 0x08: // Hold and decay
break;
case 0x09: // Set secondary speed
if(param > 0 && param <= 20)
if(param > 0 && param <= 0x20)
m.SetEffectCommand(CMD_SPEED, param);
break;
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)};
}
}
#ifdef MODPLUG_TRACKER
if(m.param < 0x20)
m.param = 0x20;
#endif // MODPLUG_TRACKER
} else switch(param)
{
case 0xF1: // Play note twice
@ -495,7 +491,7 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo);
break;
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;
case 0x15: // Set finetune
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)
return false;
const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN);
const bool freePan = !hardwareMixSamples && (header.flags3 & MMD2Song::FLAG3_FREEPAN);
if(header.volAdjust)
preamp = Util::muldivr_unsigned(preamp, std::min<uint16>(header.volAdjust, 800), 100);
if (freePan)
@ -1470,8 +1466,8 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
order[from] = pat;
}
Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast<ModCommand::PARAM>(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow());
if(pat >= numPatterns)
numPatterns = pat + 1;
if(pat >= basePattern && (pat - basePattern) >= numPatterns)
numPatterns = static_cast<PATTERNINDEX>(pat - basePattern + 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_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(ChnSettings[chn].nPan)).Channel(chn).RetryNextRow());
}
if(firstPat >= numPatterns)
numPatterns = firstPat + 1;
if(firstPat >= basePattern && (firstPat - basePattern) >= numPatterns)
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;
FileReader *bankFile = nullptr;
if(CDLSBank::IsDLSBank(file.GetOptionalFileName().value_or(P_(""))))
if(CDLSBank::IsDLSBank(file))
{
// Soundfont embedded in MIDI file
embeddedBank = std::make_unique<CDLSBank>();
embeddedBank->Open(file.GetOptionalFileName().value_or(P_("")));
embeddedBank->Open(file);
bankFile = &file;
} else
{
// Soundfont with same name as MIDI file
@ -1353,7 +1355,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
else if(pIns->nMidiProgram)
midiCode = (pIns->nMidiProgram - 1) & 0x7F;
if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80))
if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80, bankFile))
{
continue;
}

View file

@ -2451,6 +2451,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const
continue;
}
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);
size_t eventByte = 0;
@ -2466,6 +2467,11 @@ bool CSoundFile::SaveMod(std::ostream &f) const
command = 0x0C;
param = std::min(m.vol, uint8(64));
}
if(writePatternBreak && !command && !param)
{
command = 0x0D;
writePatternBreak = false;
}
uint16 period = 0;
// Convert note to period

View file

@ -30,7 +30,7 @@ struct OktIffChunk
};
uint32be signature; // IFF chunk name
uint32be chunksize; // chunk size without header
uint32be chunkSize; // Chunk size without header
};
MPT_BINARY_STRUCT(OktIffChunk, 8)
@ -38,11 +38,11 @@ MPT_BINARY_STRUCT(OktIffChunk, 8)
struct OktSample
{
char name[20];
uint32be length; // length in bytes
uint32be length; // Length in bytes
uint16be loopStart; // *2 for real value
uint16be loopLength; // ditto
uint16be volume; // default volume
uint16be type; // 7-/8-bit sample
uint16be volume; // Default volume
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)
@ -51,7 +51,8 @@ MPT_BINARY_STRUCT(OktSample, 32)
// Parse the sample header block
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++)
{
@ -66,6 +67,7 @@ static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile)
mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u;
mptSmp.nLength = oktSmp.length & ~1;
mptSmp.cues[0] = oktSmp.type; // Temporary storage for pattern reader, will be reset later
mptSmp.cues[1] = 0;
// Parse loops
const SmpLength loopStart = oktSmp.loopStart * 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)
{
m.note = note + (NOTE_MIDDLEC - 13);
if(pairedChn[chn] && m.note >= NOTE_MIDDLEC + 22)
m.note = NOTE_MIDDLEC + 21;
m.instr = instr + 1;
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
if(pairedChn[chn] && sample.nVolume < 256)
{
m.volcmd = VOLCMD_VOLUME;
m.vol = 64;
}
m.SetVolumeCommand(VOLCMD_VOLUME, 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((sample.cues[0] == 1 && pairedChn[chn] != 0) || (sample.cues[0] == 0 && pairedChn[chn] == 0))
{
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)
if(param)
{
m.command = CMD_PORTAMENTOUP;
m.param = param;
m.SetEffectCommand(CMD_PORTAMENTOUP, param);
}
break;
case 2: // 2 Portamento Up (Period)
if(param)
{
m.command = CMD_PORTAMENTODOWN;
m.param = param;
}
m.SetEffectCommand(CMD_PORTAMENTODOWN, param);
break;
case 10: // A Arpeggio 1 (down, orig, up)
if(param)
{
m.command = CMD_ARPEGGIO;
m.param = (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4);
}
m.SetEffectCommand(CMD_ARPEGGIO, (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4));
break;
case 11: // B Arpeggio 2 (orig, up, orig, down)
if(param)
{
m.command = CMD_ARPEGGIO;
m.param = (param & 0xF0) | InvertArpeggioParam(param & 0x0F);
}
m.SetEffectCommand(CMD_ARPEGGIO, (param & 0xF0) | InvertArpeggioParam(param & 0x0F));
break;
// This one is close enough to "standard" arpeggio -- I think!
case 12: // C Arpeggio 3 (up, up, orig)
if(param)
{
m.command = CMD_ARPEGGIO;
m.param = param;
}
m.SetEffectCommand(CMD_ARPEGGIO, param);
break;
case 13: // D Slide Down (Notes)
if(param)
{
m.command = CMD_NOTESLIDEDOWN;
m.param = 0x10 | std::min(uint8(0x0F), param);
}
m.SetEffectCommand(CMD_NOTESLIDEDOWN, 0x10 | std::min(uint8(0x0F), param));
break;
case 30: // U Slide Up (Notes)
if(param)
{
m.command = CMD_NOTESLIDEUP;
m.param = 0x10 | std::min(uint8(0x0F), param);
}
m.SetEffectCommand(CMD_NOTESLIDEUP, 0x10 | std::min(uint8(0x0F), param));
break;
// Fine Slides are only implemented for libopenmpt. For OpenMPT,
// 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.
case 21: // L Slide Down Once (Notes)
if(param)
{
m.command = CMD_NOTESLIDEDOWN;
m.param = 0x50 | std::min(uint8(0x0F), param);
}
m.SetEffectCommand(CMD_NOTESLIDEDOWN, 0x50 | std::min(uint8(0x0F), param));
break;
case 17: // H Slide Up Once (Notes)
if(param)
{
m.command = CMD_NOTESLIDEUP;
m.param = 0x50 | std::min(uint8(0x0F), param);
}
m.SetEffectCommand(CMD_NOTESLIDEUP, 0x50 | std::min(uint8(0x0F), param));
break;
case 15: // F Set Filter <>00:ON
m.command = CMD_MODCMDEX;
m.param = !!param;
m.SetEffectCommand(CMD_MODCMDEX, !!param);
break;
case 25: // P Pos Jump
m.command = CMD_POSITIONJUMP;
m.param = param;
m.SetEffectCommand(CMD_POSITIONJUMP, param);
break;
case 27: // R Release sample (apparently not listed in the help!)
m.Clear();
m.note = NOTE_KEYOFF;
m.instr = 0;
break;
case 28: // S Speed
if(param < 0x20)
{
m.command = CMD_SPEED;
m.param = param;
}
m.SetEffectCommand(CMD_SPEED, param);
break;
case 31: // V Volume
// Volume on mixed channels is permanent, on hardware channels it behaves like in regular MODs
if(param & 0x0F)
{
m.command = pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE;
m.param = param & 0x0F;
}
m.SetEffectCommand(pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE, param & 0x0F);
switch(param >> 4)
{
@ -260,13 +235,11 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
case 0: case 1: case 2: case 3:
if(pairedChn[chn])
{
m.command = CMD_CHANNELVOLUME;
m.param = param;
m.SetEffectCommand(CMD_CHANNELVOLUME, param);
} else
{
m.volcmd = VOLCMD_VOLUME;
m.vol = param;
m.command = CMD_NONE;
m.SetVolumeCommand(VOLCMD_VOLUME, param);
m.SetEffectCommand(oldCmd, oldParam);
}
break;
case 5: // Normal slide up
@ -280,7 +253,7 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
break;
default:
// Junk.
m.command = CMD_NONE;
m.SetEffectCommand(oldCmd, oldParam);
break;
}
@ -293,7 +266,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
{
other.SetVolumeCommand(volCmd);
}
other.SetEffectCommand(m);
if(ModCommand::GetEffectWeight(other.command) < ModCommand::GetEffectWeight(m.command))
{
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;
@ -359,7 +339,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
if(!file.ReadStruct(iffHead))
break;
FileReader chunk = file.ReadChunk(iffHead.chunksize);
FileReader chunk = file.ReadChunk(iffHead.chunkSize);
if(!chunk.IsValid())
continue;
@ -467,12 +447,14 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
// Read samples
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))
break;
ModSample &mptSample = Samples[smp];
const bool needCopy = mptSample.cues[1] != 0;
mptSample.SetDefaultCuePoints();
if(mptSample.nLength == 0)
continue;
@ -487,6 +469,20 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
SampleIO::signedPCM)
.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++;
}

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.charset = mpt::Charset::CP437;
SetMixLevels(MixLevels::CompatibleFT2);
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
m_nChannels = fileHeader.numChannels;
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:
// Don't be surprised about the strange formula, this is directly translated from original disassembly...
m.command = CMD_S3MCMDEX;
m.param = 0x80 | ((std::max<uint8>(m.param >> 3, 1u) - 1u) & 0x0F);
m.SetEffectCommand(CMD_S3MCMDEX, static_cast<ModCommand::PARAM>(0x80 | ((std::max<uint8>(m.param >> 3, 1u) - 1u) & 0x0F)));
break;
case CMD_GLOBALVOLUME:
m.param = std::min(m.param, uint8(0x40)) * 2u;
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:
break;
}
}
if(b & 0x80)
{
m.volcmd = VOLCMD_VOLUME;
m.vol = file.ReadUint8();
m.SetVolumeCommand(VOLCMD_VOLUME, 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.
// 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.
madeWithTracker = UL_("Scream Tracker");
formatTrackerStr = true;
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");
formatTrackerStr = true;
}
}
break;
case S3MFileHeader::trkImagoOrpheus:
@ -297,17 +304,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
nonCompatTracker = true;
break;
case S3MFileHeader::trkImpulseTracker:
if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14)
{
madeWithTracker = UL_("Impulse Tracker");
formatTrackerStr = true;
} else if(fileHeader.cwtv == S3MFileHeader::trkIT1_old)
{
if(fileHeader.cwtv == S3MFileHeader::trkIT1_old)
madeWithTracker = UL_("Impulse Tracker 1.03"); // Could also be 1.02, maybe? I don't have that one
} else
{
madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14);
}
else
madeWithTracker = GetImpulseTrackerVersion(fileHeader.cwtv, 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
@ -724,7 +725,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
else if(m.param > 0x08)
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
m.command = CMD_DUMMY;
@ -930,6 +931,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
const auto rowBase = Patterns[pat].GetRow(row);
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++)
{
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)
{

View file

@ -54,7 +54,7 @@ MPT_BINARY_STRUCT(SFXOrderHeader, 130)
struct SFXSampleHeader
{
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 volume;
uint16be loopStart;
@ -64,7 +64,7 @@ struct SFXSampleHeader
void ConvertToMPT(ModSample &mptSmp, uint32 length) const
{
mptSmp.Initialize(MOD_TYPE_MOD);
mptSmp.nLength = length;
mptSmp.nLength = (loopLength > 1) ? length : (oneshotLength * 2u);
mptSmp.nFineTune = MOD2XMFineTune(finetune);
mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
@ -434,14 +434,18 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
// Reading samples
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::_8bit,
SampleIO::mono,
SampleIO::littleEndian,
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.type = U_("stm");
m_modFormat.madeWithTracker = MPT_UFORMAT("Scream Tracker {}.{}")(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor));
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_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)
{
MPT_UNREFERENCED_PARAMETER(pfilesize);
@ -1044,6 +1068,7 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
uint16 sampleBoost = 2500;
bool isSymphoniePro = false;
bool externalSamples = false;
bool unknownHunks = false;
std::vector<SymPosition> positions;
std::vector<SymSequence> sequences;
std::vector<SymEvent> patternData;
@ -1202,9 +1227,10 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
file.Skip(file.ReadUint32BE());
break;
// Unrecognized chunk/value type
// Unrecognized chunk/value type (e.g. garbage at the end of Natsh1.SymMOD)
default:
return false;
unknownHunks = true;
break;
}
}
@ -1212,6 +1238,8 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
return false;
if((loadFlags & loadPatternData) && (positions.empty() || patternData.empty() || sequences.empty()))
return false;
if(unknownHunks)
AddToLog(LogWarning, U_("Unknown hunks were found and ignored."));
// Let's hope noone is going to use the 256th instrument ;)
if(instruments.size() >= MAX_INSTRUMENTS)
@ -1641,10 +1669,9 @@ bool CSoundFile::ReadSymMOD(FileReader &file, ModLoadingFlags loadFlags)
case SymEvent::DSPEcho:
case SymEvent::DSPDelay:
#endif
if(macroMap.count(event))
if(auto it = macroMap.find(event); it != macroMap.end() && it->second != 0)
{
m.command = CMD_MIDI;
m.param = macroMap[event];
m.SetEffectCommand(CMD_MIDI, it->second);
} else if(macroMap.size() < m_MidiCfg.Zxx.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)
useDSP = true;
}
} else if(uint8 param = MapToClosestMidiMacro(event, macroMap))
{
m.SetEffectCommand(CMD_MIDI, param);
}
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 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));
if(offset > 0xFF)
m.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat;
return {repeat};
} else if(cmd1 == CMD_OFFSET)
{
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.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat;
return {repeat};
}
} 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.SetVolumeCommand(VOLCMD_OFFSET, static_cast<ModCommand::VOL>(offset >> 8));
return repeat;
return {repeat};
}
} else if(cmd1 == cmd2)
{
@ -257,9 +259,8 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
// Combine slide commands, if possible
ModCommand::CombineEffects(cmd2, param2, cmd1, param1);
m.FillInTwoCommands(cmd1, param1, cmd2, param2);
return repeat;
const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2);
return {repeat, lostCommand.first, lostCommand.second};
}
@ -388,8 +389,8 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
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")};
m_modFormat.formatName = U_("UltraTracker");
m_modFormat.type = U_("ult");
m_modFormat.formatName = UL_("UltraTracker");
m_modFormat.type = UL_("ult");
m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1'];
m_modFormat.charset = mpt::Charset::CP437;
@ -456,7 +457,10 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
ROWINDEX row = 0;
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)
repeat = 64 - row;
if(repeat == 0)

View file

@ -1135,8 +1135,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
#ifndef MODPLUG_NO_FILESAVE
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0)
// work-around massively confused GCC 13 optimizer:
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(15, 1, 0)
// 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=]
template <typename Tcont2, typename Tcont1>
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);
#else
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.
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
if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size())
@ -73,11 +79,11 @@ int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int
if(empty())
return 0;
uint32 pt = size() - 1u;
uint32 pt = LastPoint();
const int32 ENV_PRECISION = 1 << 16;
// 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)
{
@ -129,12 +135,12 @@ void InstrumentEnvelope::Sanitize(uint8 maxValue)
it->tick = std::max(it->tick, (it - 1)->tick);
LimitMax(it->value, maxValue);
}
LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1));
LimitMax(nLoopEnd, LastPoint());
LimitMax(nLoopStart, nLoopEnd);
LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1));
LimitMax(nSustainEnd, LastPoint());
LimitMax(nSustainStart, nSustainEnd);
if(nReleaseNode != ENV_RELEASE_NODE_UNSET)
LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1));
LimitMax(nReleaseNode, LastPoint());
} else
{
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)
{
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

View file

@ -61,6 +61,8 @@ struct InstrumentEnvelope : public std::vector<EnvelopeNode>
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;
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 ignored "-Wframe-larger-than=16000"
#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 ignored "-Wframe-larger-than"
#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);
}
#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
#endif // MPT_COMPILER_CLANG
#if MPT_COMPILER_GCC

View file

@ -988,6 +988,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
filenameModifier += P_(" (cross-fade)");
}
const SmpLength origSampleLength = sample.nLength;
// Sample offset
if(region.offset && region.offset < sample.nLength)
{
@ -1003,6 +1005,19 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
}
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)
{
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 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
@ -678,7 +684,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
}
const auto &specs = GetModSpecifications();
if(tempo.GetInt() >= 0x20)
if(tempo >= GetMinimumTempoParam(GetType()))
{
#if MPT_MSVC_BEFORE(2019, 0)
// 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;
}
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;
}
@ -5520,7 +5526,10 @@ void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) c
chn.dwFlags.set(CHN_PINGPONGFLAG);
chn.dwFlags.reset(CHN_LOOP);
chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length.
chn.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();
// 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)
{

View file

@ -978,6 +978,7 @@ public:
bool LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool* pInterpretMptMade = nullptr);
void LoadMPTMProperties(FileReader &file, uint16 cwtv);
static mpt::ustring GetImpulseTrackerVersion(uint16 cwtv, uint16 cmwt);
static mpt::ustring GetSchismTrackerVersion(uint16 cwtv, uint32 reserved);
// 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));
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI))
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)
{
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);
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.
// 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])
{
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)
{
@ -2472,15 +2477,10 @@ bool CSoundFile::ReadNote()
{
int32 pan = (m_MixerSettings.gnChannels >= 2) ? Clamp(chn.nRealPan, 0, 256) : 128;
int32 realvol;
if(m_PlayConfig.getUseGlobalPreAmp())
{
realvol = (chn.nRealVolume * kChnMasterVol) / 128;
} else
{
// Extra attenuation required here if we're bypassing pre-amp.
realvol = (chn.nRealVolume * kChnMasterVol) / 256;
}
int32 realvol = (chn.nRealVolume * kChnMasterVol) / 128;
// Extra attenuation required here if we're bypassing pre-amp.
if(!m_PlayConfig.getUseGlobalPreAmp())
realvol /= 2;
const PanningMode panningMode = m_PlayConfig.getPanningMode();
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.
// Test case: FT2PanLaw.xm
LimitMax(pan, 255);
const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536;
const int panR = XMPanningTable[pan];
chn.newLeftVol = (realvol * panL) / 65536;
@ -2636,11 +2637,11 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
// Check for volume commands
uint8 vol = 0xFF;
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)
vol = std::min(chn.rowCommand.param, uint8(64));
vol = std::min(chn.rowCommand.param, uint8(64)) * 2u;
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);
@ -2654,7 +2655,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
SendMIDINote(nChn, realNote, static_cast<uint16>(chn.nVolume));
} else if(hasVolCommand)
{
pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol, nChn);
pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol / 2u, nChn);
}
return;
}
@ -2668,7 +2669,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
switch(pIns->pluginVelocityHandling)
{
case PLUGIN_VELOCITYHANDLING_CHANNEL:
velocity = chn.nVolume;
velocity = hasVolCommand ? vol * 2 : chn.nVolume;
break;
default:
break;
@ -2696,11 +2697,11 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
switch(pIns->pluginVolumeHandling)
{
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);
break;
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);
break;
default:

View file

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

View file

@ -327,7 +327,7 @@ constexpr CModSpecifications it_ =
255, // Max tempo
1, // Min Speed
255, // Max Speed
1, // Min pattern rows
32, // Min pattern rows
200, // Max pattern rows
25, // Max mod 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
{
if(m_ModCommands.empty() || !IsValidRow(row))
{
return true;
}
for(const auto &m : GetRow(row))
{
if(!m.IsEmpty())
{
return false;
}
}
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
{
if(rowsPerBeat < 1

View file

@ -60,6 +60,8 @@ public:
// Check if there is any note data on a given row.
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.
bool AllocatePattern(ROWINDEX rows);

View file

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

View file

@ -50,7 +50,9 @@
#elif defined(_MSC_VER)
#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)
#elif (_MSC_VER >= 1939)
#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
#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_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
// 20H2
// 21H1
#define MPT_WIN_10_21H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0a) // NTDDI_WIN10_FE 21H2
// 22H2
#define MPT_WIN_10_20H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0a) // NTDDI_WIN10_FE 20H2
#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, 0x0c) // NTDDI_WIN10_NI 21H2
#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_22H2 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x0c) // NTDDI_WIN10_NI 22H2
#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, 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_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);
#endif
#ifdef NTDDI_WIN10_FE
static_assert(NTDDI_WIN10_FE == MPT_WIN_10_21H2);
static_assert(NTDDI_WIN10_FE == MPT_WIN_10_20H2);
#endif
#ifdef NTDDI_WIN10_CO
static_assert(NTDDI_WIN10_CO == MPT_WIN_11);
static_assert(NTDDI_WIN10_CO == MPT_WIN_10_21H1);
#endif
#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
#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

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_EMPTY_HPP
#define MPT_BASE_EMPTY_HPP
#ifndef MPT_BASE_MATH_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_quirks.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>
#else
#include <type_traits>
@ -25,7 +26,7 @@ inline namespace MPT_INLINE_NS {
namespace numbers {
#if MPT_CXX_AT_LEAST(20)
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_NUMBERS)
template <typename 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 {
// 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);
#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);
//#elif // 22H2
// 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_22H2)
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)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19044, 0);
//#elif // 21H1
// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19043, 0);
//#elif // 20H2
// 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_21H1)
return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19043, 0);
#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);
#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);
#elif MPT_WINNT_AT_LEAST(MPT_WIN_10_1909)

View file

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

View file

@ -7,6 +7,7 @@
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/numeric.hpp"
#include "mpt/random/seed.hpp"
#include <memory>
@ -47,7 +48,7 @@ struct engine_traits<std::mt19937> {
}
template <typename Trd>
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());
return rng_type(seed);
}
@ -65,7 +66,7 @@ struct engine_traits<std::mt19937_64> {
}
template <typename Trd>
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());
return rng_type(seed);
}
@ -83,7 +84,7 @@ struct engine_traits<std::ranlux24_base> {
}
template <typename Trd>
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());
return rng_type(seed);
}
@ -101,7 +102,7 @@ struct engine_traits<std::ranlux48_base> {
}
template <typename Trd>
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());
return rng_type(seed);
}
@ -119,7 +120,7 @@ struct engine_traits<std::ranlux24> {
}
template <typename Trd>
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());
return rng_type(seed);
}
@ -137,7 +138,7 @@ struct engine_traits<std::ranlux48> {
}
template <typename Trd>
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());
return rng_type(seed);
}

View file

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

View file

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

View file

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