Updated libopenmpt to version 0.5.9

CQTexperiment
Christopher Snowhill 2021-05-16 16:54:35 -07:00
parent 7181656ae9
commit 24dfaf7d5c
43 changed files with 283 additions and 226 deletions

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=14580 MPT_SVNVERSION=15019
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.8 MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.9
MPT_SVNDATE=2021-04-11T14:02:18.903044Z MPT_SVNDATE=2021-05-16T14:59:54.252327Z

View File

@ -42,8 +42,8 @@ else ifeq ($(EMSCRIPTEN_TARGET),all)
# emits native wasm AND javascript with full wasm optimizations. # emits native wasm AND javascript with full wasm optimizations.
# as of emscripten 1.38, this is equivalent to default. # as of emscripten 1.38, this is equivalent to default.
CPPFLAGS += -DMPT_BUILD_WASM CPPFLAGS += -DMPT_BUILD_WASM
CXXFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 CXXFLAGS +=
CFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 CFLAGS +=
LDFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
@ -51,8 +51,8 @@ LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
else ifeq ($(EMSCRIPTEN_TARGET),audioworkletprocessor) else ifeq ($(EMSCRIPTEN_TARGET),audioworkletprocessor)
# emits an es6 module in a single file suitable for use in an AudioWorkletProcessor # emits an es6 module in a single file suitable for use in an AudioWorkletProcessor
CPPFLAGS += -DMPT_BUILD_WASM -DMPT_BUILD_AUDIOWORKLETPROCESSOR CPPFLAGS += -DMPT_BUILD_WASM -DMPT_BUILD_AUDIOWORKLETPROCESSOR
CXXFLAGS += -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s MODULARIZE=1 -s EXPORT_ES6=1 -s SINGLE_FILE=1 CXXFLAGS +=
CFLAGS += -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s MODULARIZE=1 -s EXPORT_ES6=1 -s SINGLE_FILE=1 CFLAGS +=
LDFLAGS += -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s MODULARIZE=1 -s EXPORT_ES6=1 -s SINGLE_FILE=1 LDFLAGS += -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s MODULARIZE=1 -s EXPORT_ES6=1 -s SINGLE_FILE=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
@ -60,8 +60,8 @@ LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
else ifeq ($(EMSCRIPTEN_TARGET),wasm) else ifeq ($(EMSCRIPTEN_TARGET),wasm)
# emits native wasm. # emits native wasm.
CPPFLAGS += -DMPT_BUILD_WASM CPPFLAGS += -DMPT_BUILD_WASM
CXXFLAGS += -s WASM=1 CXXFLAGS +=
CFLAGS += -s WASM=1 CFLAGS +=
LDFLAGS += -s WASM=1 LDFLAGS += -s WASM=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
@ -69,16 +69,16 @@ LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
else ifeq ($(EMSCRIPTEN_TARGET),js) else ifeq ($(EMSCRIPTEN_TARGET),js)
# emits only plain javascript with plain javascript focused optimizations. # emits only plain javascript with plain javascript focused optimizations.
CPPFLAGS += -DMPT_BUILD_ASMJS CPPFLAGS += -DMPT_BUILD_ASMJS
CXXFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 CXXFLAGS +=
CFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 CFLAGS +=
LDFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
endif endif
CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -ffast-math
CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math -fno-strict-aliasing CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -ffast-math -fno-strict-aliasing
LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'" LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'"
CFLAGS_SILENT += -Wno-\#warnings CFLAGS_SILENT += -Wno-\#warnings

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#define OPENMPT_VERSION_SVNVERSION "14580" #define OPENMPT_VERSION_SVNVERSION "15019"
#define OPENMPT_VERSION_REVISION 14580 #define OPENMPT_VERSION_REVISION 15019
#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.5.8" #define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.9"
#define OPENMPT_VERSION_DATE "2021-04-11T14:02:18.903044Z" #define OPENMPT_VERSION_DATE "2021-05-16T14:59:54.252327Z"
#define OPENMPT_VERSION_IS_PACKAGE 1 #define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -30,6 +30,7 @@
#include "mptTime.h" #include "mptTime.h"
#include "mptLibrary.h" #include "mptLibrary.h"
#include <stdexcept>
#include <vector> #include <vector>
#include <cstdlib> #include <cstdlib>
@ -230,4 +231,17 @@ std::string getenv(const std::string &env_var, const std::string &def = std::str
#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST) #endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST)
#if MPT_OS_WINDOWS
template <typename Tstring, typename Tbuf, typename Tsize>
Tstring ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(const Tbuf *buf, Tsize sizeBytes)
{
// REG_SZ may contain a single NUL terminator, multiple NUL terminators, or no NUL terminator at all
return Tstring(reinterpret_cast<const typename Tstring::value_type*>(buf), reinterpret_cast<const typename Tstring::value_type*>(buf) + (sizeBytes / sizeof(typename Tstring::value_type))).c_str();
}
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_END OPENMPT_NAMESPACE_END

View File

@ -98,6 +98,19 @@ static cpuid_result cpuid(uint32 function)
} }
static cpuid_result cpuidex(uint32 function_a, uint32 function_c)
{
cpuid_result result;
int CPUInfo[4];
__cpuidex(CPUInfo, function_a, function_c);
result.a = CPUInfo[0];
result.b = CPUInfo[1];
result.c = CPUInfo[2];
result.d = CPUInfo[3];
return result;
}
void InitProcSupport() void InitProcSupport()
{ {
@ -150,6 +163,11 @@ void InitProcSupport()
if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2; if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2;
if(StandardFeatureFlags.c & (1<<28)) ProcSupport |= PROCSUPPORT_AVX; if(StandardFeatureFlags.c & (1<<28)) ProcSupport |= PROCSUPPORT_AVX;
} }
if(VendorString.a >= 0x00000007u)
{
cpuid_result ExtendedFeatures = cpuidex(0x00000007u, 0x00000000u);
if(ExtendedFeatures.b & (1<< 5)) ProcSupport |= PROCSUPPORT_AVX2;
}
cpuid_result ExtendedVendorString = cpuid(0x80000000u); cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u) if(ExtendedVendorString.a >= 0x80000001u)
@ -160,11 +178,6 @@ void InitProcSupport()
if(ExtendedVendorString.a >= 0x80000004u) if(ExtendedVendorString.a >= 0x80000004u)
{ {
mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4(); mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4();
if(ExtendedVendorString.a >= 0x80000007u)
{
cpuid_result ExtendedFeatures = cpuid(0x80000007u);
if(ExtendedFeatures.b & (1<< 5)) ProcSupport |= PROCSUPPORT_AVX2;
}
} }
} }

View File

@ -140,6 +140,8 @@
// <atomic> // <atomic>
#include "../common/misc_util.h" #include "../common/misc_util.h"
// <stdexcept>
// <vector>
// for std::abs // for std::abs
#include <cstdlib> #include <cstdlib>

View File

@ -357,23 +357,6 @@ mpt::ustring GetBuildFeaturesString()
; ;
#endif #endif
#ifdef MODPLUG_TRACKER #ifdef MODPLUG_TRACKER
if constexpr(mpt::arch_bits == 64)
{
if (true
&& (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64)
&& (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP64)
) {
retval += UL_(" WIN64OLD");
}
} else if constexpr(mpt::arch_bits == 32)
{
if (true
&& (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP)
&& (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP)
) {
retval += UL_(" WIN32OLD");
}
}
retval += UL_("") retval += UL_("")
#if defined(UNICODE) #if defined(UNICODE)
UL_(" UNICODE") UL_(" UNICODE")

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 29 #define VER_MAJOR 29
#define VER_MINOR 09 #define VER_MINOR 10
#define VER_MINORMINOR 00 #define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END OPENMPT_NAMESPACE_END

View File

@ -5,6 +5,26 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This For fully detailed change log, please see the source repository directly. This
is just a high-level summary. is just a high-level summary.
### libopenmpt 0.5.9 (2021-05-16)
* `Makefile` `CONFIG=emscripten` does not pass linker options to the compiler
any more, which caused latest Emscripten to issue warnings.
* The retrigger effect didn't work correctly for OPL instruments in some cases
depending on the chosen output sample rate.
* S3M: Emulate IT short sample retrigger quirk in S3M files saved with
Impulse Tracker and Schism Tracker.
* ProTracker 3.6 doesn't support sample swapping. It is now disabled when
loading a ProTracker 3.6 IFF module.
* Some files with "hidden" garbage patterns were not played correctly since
libopenmpt 0.5.3.
* MOD: E9x retrigger now works the same way as in ProTracker 2.
* MDL: Improve auto-vibrato accuracy.
* 669: Reject files that contain any pattern tempo higher than 15.
* Reduce memory consumption of malformed XPK-compressed files.
* mpg123: Update to v1.27.2 (2021-05-08).
### libopenmpt 0.5.8 (2021-04-11) ### libopenmpt 0.5.8 (2021-04-11)
* [**Sec**] Possible null-pointer dereference read caused by a sequence of * [**Sec**] Possible null-pointer dereference read caused by a sequence of

View File

@ -559,14 +559,14 @@ LIBOPENMPT_API double openmpt_could_open_probability2( openmpt_stream_callbacks
LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void);
/*! Probe for module formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ /*! Probe for module formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */
#define OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES 0x1ul #define OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES 0x1ull
/*! Probe for module-specific container formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ /*! Probe for module-specific container formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */
#define OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS 0x2ul #define OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS 0x2ull
/*! Probe for the default set of formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ /*! Probe for the default set of formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */
#define OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT ( OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES | OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS ) #define OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT ( OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES | OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS )
/*! Probe for no formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ /*! Probe for no formats in openmpt_probe_file_header() or openmpt_probe_file_header_without_filesize(). \since 0.3.0 */
#define OPENMPT_PROBE_FILE_HEADER_FLAGS_NONE 0x0ul #define OPENMPT_PROBE_FILE_HEADER_FLAGS_NONE 0x0ull
/*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */ /*! Possible return values fo openmpt_probe_file_header() and openmpt_probe_file_header_without_filesize(). \since 0.3.0 */
#define OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS 1 #define OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS 1

View File

@ -103,7 +103,7 @@
* threads for internal use. * threads for internal use.
* - You must ensure to only ever access a particular libopenmpt object via * - You must ensure to only ever access a particular libopenmpt object via
* non-const member functions from a single thread at a time. * non-const member functions from a single thread at a time.
* - You may access a particular libopenmpt objects concurrently from different * - You may access a particular libopenmpt object concurrently from different
* threads when using only const member functions from all threads. * threads when using only const member functions from all threads.
* - Consecutive accesses can happen from different threads. * - Consecutive accesses can happen from different threads.
* - Different objects can be accessed concurrently from different threads. * - Different objects can be accessed concurrently from different threads.
@ -273,16 +273,16 @@ LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API double could_open_propability( std
LIBOPENMPT_CXX_API std::size_t probe_file_header_get_recommended_size(); LIBOPENMPT_CXX_API std::size_t probe_file_header_get_recommended_size();
//! Probe for module formats in openmpt::probe_file_header(). \since 0.3.0 //! Probe for module formats in openmpt::probe_file_header(). \since 0.3.0
static const std::uint64_t probe_file_header_flags_modules = 0x1ul; static const std::uint64_t probe_file_header_flags_modules = 0x1ull;
//! Probe for module-specific container formats in openmpt::probe_file_header(). \since 0.3.0 //! Probe for module-specific container formats in openmpt::probe_file_header(). \since 0.3.0
static const std::uint64_t probe_file_header_flags_containers = 0x2ul; static const std::uint64_t probe_file_header_flags_containers = 0x2ull;
//! Probe for the default set of formats in openmpt::probe_file_header(). \since 0.3.0 //! Probe for the default set of formats in openmpt::probe_file_header(). \since 0.3.0
static const std::uint64_t probe_file_header_flags_default = probe_file_header_flags_modules | probe_file_header_flags_containers; static const std::uint64_t probe_file_header_flags_default = probe_file_header_flags_modules | probe_file_header_flags_containers;
//! Probe for no formats in openmpt::probe_file_header(). \since 0.3.0 //! Probe for no formats in openmpt::probe_file_header(). \since 0.3.0
static const std::uint64_t probe_file_header_flags_none = 0x0ul; static const std::uint64_t probe_file_header_flags_none = 0x0ull;
//! Possible return values for openmpt::probe_file_header(). \since 0.3.0 //! Possible return values for openmpt::probe_file_header(). \since 0.3.0
enum probe_file_header_result { enum probe_file_header_result {

View File

@ -215,7 +215,7 @@ namespace openmpt {
void module_ext_impl::set_instrument_mute_status( std::int32_t instrument, bool mute ) { void module_ext_impl::set_instrument_mute_status( std::int32_t instrument, bool mute ) {
const bool instrument_mode = get_num_instruments() != 0; const bool instrument_mode = get_num_instruments() != 0;
const int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
if ( instrument < 0 || instrument >= max_instrument ) { if ( instrument < 0 || instrument >= max_instrument ) {
throw openmpt::exception("invalid instrument"); throw openmpt::exception("invalid instrument");
} }
@ -230,7 +230,7 @@ namespace openmpt {
bool module_ext_impl::get_instrument_mute_status( std::int32_t instrument ) const { bool module_ext_impl::get_instrument_mute_status( std::int32_t instrument ) const {
const bool instrument_mode = get_num_instruments() != 0; const bool instrument_mode = get_num_instruments() != 0;
const int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
if ( instrument < 0 || instrument >= max_instrument ) { if ( instrument < 0 || instrument >= max_instrument ) {
throw openmpt::exception("invalid instrument"); throw openmpt::exception("invalid instrument");
} }
@ -246,7 +246,7 @@ namespace openmpt {
std::int32_t module_ext_impl::play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) { std::int32_t module_ext_impl::play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) {
const bool instrument_mode = get_num_instruments() != 0; const bool instrument_mode = get_num_instruments() != 0;
const int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples(); const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
if ( instrument < 0 || instrument >= max_instrument ) { if ( instrument < 0 || instrument >= max_instrument ) {
throw openmpt::exception("invalid instrument"); throw openmpt::exception("invalid instrument");
} }
@ -261,15 +261,15 @@ namespace openmpt {
free_channel = MAX_CHANNELS - 1; free_channel = MAX_CHANNELS - 1;
ModChannel &chn = m_sndFile->m_PlayState.Chn[free_channel]; ModChannel &chn = m_sndFile->m_PlayState.Chn[free_channel];
chn.Reset(ModChannel::resetTotal, *m_sndFile, CHANNELINDEX_INVALID); chn.Reset(ModChannel::resetTotal, *m_sndFile, CHANNELINDEX_INVALID, CHN_MUTE);
chn.nMasterChn = 0; // remove NNA association chn.nMasterChn = 0; // remove NNA association
chn.nNewNote = chn.nLastNote = static_cast<uint8>(note); chn.nNewNote = chn.nLastNote = static_cast<uint8>(note);
chn.ResetEnvelopes(); chn.ResetEnvelopes();
m_sndFile->InstrumentChange(chn, instrument + 1); m_sndFile->InstrumentChange(chn, instrument + 1);
chn.nFadeOutVol = 0x10000; chn.nFadeOutVol = 0x10000;
m_sndFile->NoteChange(chn, note, false, true, true); m_sndFile->NoteChange(chn, note, false, true, true);
chn.nPan = mpt::saturate_round<int32_t>( Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 ); chn.nPan = mpt::saturate_round<std::int32_t>( Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 );
chn.nVolume = mpt::saturate_round<int32_t>( Clamp( volume * 256.0, 0.0, 256.0 ) ); chn.nVolume = mpt::saturate_round<std::int32_t>( Clamp( volume * 256.0, 0.0, 256.0 ) );
// Remove channel from list of mixed channels to fix https://bugs.openmpt.org/view.php?id=209 // Remove channel from list of mixed channels to fix https://bugs.openmpt.org/view.php?id=209
// This is required because a previous note on the same channel might have just stopped playing, // This is required because a previous note on the same channel might have just stopped playing,

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */ /*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 5 #define OPENMPT_API_VERSION_MINOR 5
/*! \brief libopenmpt patch version number */ /*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 8 #define OPENMPT_API_VERSION_PATCH 9
/*! \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=5 LIBOPENMPT_VERSION_MINOR=5
LIBOPENMPT_VERSION_PATCH=8 LIBOPENMPT_VERSION_PATCH=9
LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2 LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=8 LIBOPENMPT_LTVER_REVISION=9
LIBOPENMPT_LTVER_AGE=2 LIBOPENMPT_LTVER_AGE=2

View File

@ -1748,7 +1748,7 @@ XMPIN * WINAPI XMPIN_GetInterface( DWORD face, InterfaceProc faceproc ) {
} }
#pragma comment(linker, "/EXPORT:XMPIN_GetInterface=_XMPIN_GetInterface@8") #pragma comment(linker, "/EXPORT:XMPIN_GetInterface=_XMPIN_GetInterface@8")
}; // extern "C" } // extern "C"
#ifdef _MFC_VER #ifdef _MFC_VER

View File

@ -280,7 +280,7 @@ void CMegaBass::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, ui
if(nChannels >= 2) if(nChannels >= 2)
{ {
X86_StereoDCRemoval(MixSoundBuffer, count, nDCRFlt_Y1lf, nDCRFlt_X1lf, nDCRFlt_Y1rf, nDCRFlt_X1rf); X86_StereoDCRemoval(MixSoundBuffer, count, nDCRFlt_Y1lf, nDCRFlt_X1lf, nDCRFlt_Y1rf, nDCRFlt_X1rf);
if(nChannels > 2) X86_StereoDCRemoval(MixSoundBuffer, count, nDCRFlt_Y1lb, nDCRFlt_X1lb, nDCRFlt_Y1rb, nDCRFlt_X1rb); if(nChannels > 2) X86_StereoDCRemoval(MixRearBuffer, count, nDCRFlt_Y1lb, nDCRFlt_X1lb, nDCRFlt_Y1rb, nDCRFlt_X1rb);
int *px = MixSoundBuffer; int *px = MixSoundBuffer;
int *py = MixRearBuffer; int *py = MixRearBuffer;
int x1 = nXBassFlt_X1; int x1 = nXBassFlt_X1;

View File

@ -48,24 +48,12 @@ struct XPK_BufferBounds
{ {
const uint8 *pSrcBeg; const uint8 *pSrcBeg;
std::size_t SrcSize; std::size_t SrcSize;
uint8 *pDstBeg;
std::size_t DstSize;
inline uint8 SrcRead(std::size_t index) inline uint8 SrcRead(std::size_t index)
{ {
if(index >= SrcSize) throw XPK_error(); if(index >= SrcSize) throw XPK_error();
return pSrcBeg[index]; return pSrcBeg[index];
} }
inline void DstWrite(std::size_t index, uint8 value)
{
if(index >= DstSize) throw XPK_error();
pDstBeg[index] = value;
}
inline uint8 DstRead(std::size_t index)
{
if(index >= DstSize) throw XPK_error();
return pDstBeg[index];
}
}; };
static int32 bfextu(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) static int32 bfextu(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs)
@ -119,23 +107,17 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &un
int32 cp, cup1, type; int32 cp, cup1, type;
std::size_t c; std::size_t c;
std::size_t src; std::size_t src;
std::size_t dst;
std::size_t phist = 0; std::size_t phist = 0;
std::size_t dstmax = len;
unpackedData.resize(len); unpackedData.reserve(std::min(static_cast<uint32>(len), std::min(srcLen, uint32_max / 20u) * 20u));
XPK_BufferBounds bufs; XPK_BufferBounds bufs;
bufs.pSrcBeg = src_; bufs.pSrcBeg = src_;
bufs.SrcSize = srcLen; bufs.SrcSize = srcLen;
bufs.pDstBeg = mpt::byte_cast<uint8 *>(unpackedData.data());
bufs.DstSize = len;
src = 0; src = 0;
dst = 0;
c = src; c = src;
while (len > 0) while(len > 0)
{ {
type = bufs.SrcRead(c+0); type = bufs.SrcRead(c+0);
cp = (bufs.SrcRead(c+4)<<8) | (bufs.SrcRead(c+5)); // packed cp = (bufs.SrcRead(c+4)<<8) | (bufs.SrcRead(c+5)); // packed
@ -146,12 +128,11 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &un
if (type == 0) if (type == 0)
{ {
// RAW chunk // RAW chunk
if(cp < 0) throw XPK_error(); if(cp < 0 || cp > len) throw XPK_error();
for(int32 i = 0; i < cp; ++i) for(int32 i = 0; i < cp; ++i)
{ {
bufs.DstWrite(dst + i, bufs.SrcRead(c + i)); unpackedData.push_back(bufs.SrcRead(c + i));
} }
dst+=cp;
c+=cp; c+=cp;
len -= cp; len -= cp;
continue; continue;
@ -164,14 +145,14 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &un
#endif #endif
break; break;
} }
LimitMax(cup1, len);
len -= cup1; len -= cup1;
cp = (cp + 3) & 0xfffc; cp = (cp + 3) & 0xfffc;
c += cp; c += cp;
d0 = d1 = d2 = a2 = 0; d0 = d1 = d2 = a2 = 0;
d3 = bufs.SrcRead(src); src++; d3 = bufs.SrcRead(src); src++;
bufs.DstWrite(dst, (uint8)d3); unpackedData.push_back(static_cast<char>(d3));
if (dst < dstmax) dst++;
cup1--; cup1--;
while (cup1 > 0) while (cup1 > 0)
@ -230,8 +211,7 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &un
d4 = bfexts(src,d0,d6,bufs); d4 = bfexts(src,d0,d6,bufs);
d0 += d6; d0 += d6;
d3 -= d4; d3 -= d4;
bufs.DstWrite(dst, (uint8)d3); unpackedData.push_back(static_cast<char>(d3));
if (dst < dstmax) dst++;
cup1--; cup1--;
d5--; d5--;
} }
@ -243,7 +223,6 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &un
d2 -= d6; d2 -= d6;
} }
} }
unpackedData.resize(bufs.DstSize - len);
return !unpackedData.empty(); return !unpackedData.empty();
l75a: l75a:
@ -287,22 +266,24 @@ l79e:
l7a6: l7a6:
d6 += d4; d6 += d4;
l7a8: l7a8:
if (bfextu(src,d0,1,bufs)) goto l7c4; if(bfextu(src, d0, 1, bufs))
d0 += 1; {
if (bfextu(src,d0,1,bufs)) goto l7bc; d5 = 12;
d5 = 8; a5 = -0x100;
a5 = 0; } else
goto l7ca; {
d0 += 1;
if(bfextu(src, d0, 1, bufs))
{
d5 = 14;
a5 = -0x1100;
} else
{
d5 = 8;
a5 = 0;
}
}
l7bc:
d5 = 14;
a5 = -0x1100;
goto l7ca;
l7c4:
d5 = 12;
a5 = -0x100;
l7ca:
d0 += 1; d0 += 1;
d4 = bfextu(src,d0,d5,bufs); d4 = bfextu(src,d0,d5,bufs);
d0 += d5; d0 += d5;
@ -314,13 +295,15 @@ l7ca:
if (d1 < 0) d1 = 0; if (d1 < 0) d1 = 0;
} }
d6 += 2; d6 += 2;
phist = dst + a5 - d4 - 1; phist = unpackedData.size() + a5 - d4 - 1;
if(phist >= unpackedData.size())
throw XPK_error();
while ((d6 >= 0) && (cup1 > 0)) while ((d6 >= 0) && (cup1 > 0))
{ {
d3 = bufs.DstRead(phist); phist++; d3 = unpackedData[phist];
bufs.DstWrite(dst, (uint8)d3); phist++;
if (dst < dstmax) dst++; unpackedData.push_back(static_cast<char>(d3));
cup1--; cup1--;
d6--; d6--;
} }

View File

@ -59,6 +59,8 @@ struct MixLoopState
{ {
samplePointer = static_cast<const int8 *>(chn.pCurrentSample); samplePointer = static_cast<const int8 *>(chn.pCurrentSample);
lookaheadPointer = nullptr; lookaheadPointer = nullptr;
if(!samplePointer)
return;
if(chn.nLoopEnd < InterpolationMaxLookahead) if(chn.nLoopEnd < InterpolationMaxLookahead)
lookaheadStart = chn.nLoopStart; lookaheadStart = chn.nLoopStart;
else else
@ -94,7 +96,8 @@ struct MixLoopState
int32 nLoopStart = chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0; int32 nLoopStart = chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0;
SamplePosition nInc = chn.increment; SamplePosition nInc = chn.increment;
if ((nSamples <= 0) || nInc.IsZero() || (!chn.nLength)) return 0; if(nSamples <= 0 || nInc.IsZero() || !chn.nLength || !samplePointer)
return 0;
// Part 1: Making sure the play position is valid, and if necessary, invert the play direction in case we reached a loop boundary of a ping-pong loop. // Part 1: Making sure the play position is valid, and if necessary, invert the play direction in case we reached a loop boundary of a ping-pong loop.
chn.pCurrentSample = samplePointer; chn.pCurrentSample = samplePointer;

View File

@ -448,7 +448,7 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr
if(mptSmp.uFlags[CHN_PANNING]) dfp |= ITSample::enablePanning; if(mptSmp.uFlags[CHN_PANNING]) dfp |= ITSample::enablePanning;
// Sample Format / Loop Flags // Sample Format / Loop Flags
if(mptSmp.HasSampleData()) if(mptSmp.HasSampleData() && !mptSmp.uFlags[CHN_ADLIB])
{ {
flags = ITSample::sampleDataPresent; flags = ITSample::sampleDataPresent;
if(mptSmp.uFlags[CHN_LOOP]) flags |= ITSample::sampleLoop; if(mptSmp.uFlags[CHN_LOOP]) flags |= ITSample::sampleLoop;
@ -498,12 +498,16 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr
if((vid | vis) != 0 && (fromType & MOD_TYPE_XM)) if((vid | vis) != 0 && (fromType & MOD_TYPE_XM))
{ {
// Sweep is upside down in XM // Sweep is upside down in XM
vir = 255 - vir; if(mptSmp.nVibSweep != 0)
vir = mpt::saturate_cast<decltype(vir)::base_type>(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep));
else
vir = 255;
} }
if(mptSmp.uFlags[CHN_ADLIB]) if(mptSmp.uFlags[CHN_ADLIB])
{ {
length = 12; length = 12;
flags = ITSample::sampleDataPresent;
cvt = ITSample::cvtOPLInstrument; cvt = ITSample::cvtOPLInstrument;
} else if(mptSmp.uFlags[SMP_KEEPONDISK]) } else if(mptSmp.uFlags[SMP_KEEPONDISK])
{ {
@ -668,9 +672,9 @@ uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime)
if((cwtv & 0xFFF) >= 0x0208) if((cwtv & 0xFFF) >= 0x0208)
{ {
editTime ^= 0x4954524B; // 'ITRK' editTime ^= 0x4954524B; // 'ITRK'
editTime = (editTime >> 7) | (editTime << (32 - 7)); editTime = mpt::rotr(editTime, 7);
editTime = ~editTime + 1; editTime = ~editTime + 1;
editTime = (editTime << 4) | (editTime >> (32 - 4)); editTime = mpt::rotl(editTime, 4);
editTime ^= 0x4A54484C; // 'JTHL' editTime ^= 0x4A54484C; // 'JTHL'
} }
return editTime; return editTime;

View File

@ -63,7 +63,7 @@ struct ITFileHeader
uint8le pwd; // Pitch Wheel Depth uint8le pwd; // Pitch Wheel Depth
uint16le msglength; // Length of Song Message uint16le msglength; // Length of Song Message
uint32le msgoffset; // Offset of Song Message in File (IT crops message after first null) uint32le msgoffset; // Offset of Song Message in File (IT crops message after first null)
uint32le reserved; // Some IT versions save an edit timer here. ChibiTracker writes "CHBI" here. OpenMPT writes "OMPT" here in some cases, see Load_it.cpp uint32le reserved; // Some IT versions save an edit timer here. ChibiTracker writes "CHBI" here. OpenMPT and Schism Tracker save extended version information here.
uint8le chnpan[64]; // Initial Channel Panning uint8le chnpan[64]; // Initial Channel Panning
uint8le chnvol[64]; // Initial Channel Volume uint8le chnvol[64]; // Initial Channel Volume
}; };

View File

@ -74,17 +74,13 @@ static bool ValidateHeader(const _669FileHeader &fileHeader)
for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++) for(std::size_t i = 0; i < CountOf(fileHeader.breaks); i++)
{ {
if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE) if(fileHeader.orders[i] >= 128 && fileHeader.orders[i] < 0xFE)
{
return false; return false;
}
if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0) if(fileHeader.orders[i] < 128 && fileHeader.tempoList[i] == 0)
{
return false; return false;
} if(fileHeader.tempoList[i] > 15)
return false;
if(fileHeader.breaks[i] >= 64) if(fileHeader.breaks[i] >= 64)
{
return false; return false;
}
} }
return true; return true;
} }
@ -242,6 +238,7 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags)
m->param = effect[chn] & 0x0F; m->param = effect[chn] & 0x0F;
// Weird stuff happening in corehop.669 with effects > 8... they seem to do the same thing as if the high bit wasn't set, but the sample also behaves strangely.
uint8 command = effect[chn] >> 4; uint8 command = effect[chn] >> 4;
if(command < static_cast<uint8>(CountOf(effTrans))) if(command < static_cast<uint8>(CountOf(effTrans)))
{ {

View File

@ -478,9 +478,12 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
{ {
// OpenMPT Version number (Major.Minor) // OpenMPT Version number (Major.Minor)
// This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used.
m_dwLastSavedWithVersion = Version((fileHeader.cwtv & 0x0FFF) << 16); uint32 mptVersion = (fileHeader.cwtv & 0x0FFF) << 16;
if(!memcmp(&fileHeader.reserved, "OMPT", 4)) if(!memcmp(&fileHeader.reserved, "OMPT", 4))
interpretModPlugMade = true; interpretModPlugMade = true;
else if(mptVersion >= 0x01'29'00'00)
mptVersion |= fileHeader.reserved & 0xFFFF;
m_dwLastSavedWithVersion = Version(mptVersion);
} else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888) } else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888)
{ {
// OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format) // OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format)
@ -1416,8 +1419,8 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
} else } else
{ {
// IT // IT
uint32 vVersion = Version::Current().GetRawVersion(); const uint32 mptVersion = Version::Current().GetRawVersion();
itHeader.cwtv = 0x5000 | (uint16)((vVersion >> 16) & 0x0FFF); // format: txyy (t = tracker ID, x = version major, yy = version minor), e.g. 0x5117 (OpenMPT = 5, 117 = v1.17) itHeader.cwtv = 0x5000 | static_cast<uint16>((mptVersion >> 16) & 0x0FFF); // format: txyy (t = tracker ID, x = version major, yy = version minor), e.g. 0x5117 (OpenMPT = 5, 117 = v1.17)
itHeader.cmwt = 0x0214; // Common compatible tracker :) itHeader.cmwt = 0x0214; // Common compatible tracker :)
// Hack from schism tracker: // Hack from schism tracker:
for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++) for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++)
@ -1429,11 +1432,10 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
} }
} }
if(!compatibilityExport) if(compatibilityExport)
{ itHeader.reserved = mptVersion & 0xFFFF;
// This way, we indicate that the file might contain OpenMPT hacks. Compatibility export puts 0 here. else
memcpy(&itHeader.reserved, "OMPT", 4); memcpy(&itHeader.reserved, "OMPT", 4);
}
} }
itHeader.flags = ITFileHeader::useStereoPlayback | ITFileHeader::useMIDIPitchController; itHeader.flags = ITFileHeader::useStereoPlayback | ITFileHeader::useMIDIPitchController;

View File

@ -635,8 +635,13 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
mptSmp.nPan = std::min(static_cast<uint16>(sampleHeader.panning * 2), uint16(254)); mptSmp.nPan = std::min(static_cast<uint16>(sampleHeader.panning * 2), uint16(254));
mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3]; mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3];
mptSmp.nVibSweep = sampleHeader.vibSweep; mptSmp.nVibSweep = sampleHeader.vibSweep;
mptSmp.nVibDepth = sampleHeader.vibDepth; mptSmp.nVibDepth = (sampleHeader.vibDepth + 3u) / 4u;
mptSmp.nVibRate = sampleHeader.vibSpeed; mptSmp.nVibRate = sampleHeader.vibSpeed;
// Convert to IT-like vibrato sweep
if(mptSmp.nVibSweep != 0)
mptSmp.nVibSweep = mpt::saturate_cast<decltype(mptSmp.nVibSweep)>(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep));
else
mptSmp.nVibSweep = 255;
if(sampleHeader.panEnvFlags & 0x40) if(sampleHeader.panEnvFlags & 0x40)
mptSmp.uFlags.set(CHN_PANNING); mptSmp.uFlags.set(CHN_PANNING);
} }

View File

@ -552,7 +552,7 @@ static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, co
// Parse the order list to determine how many patterns are used in the file. // Parse the order list to determine how many patterns are used in the file.
static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, SmpLength wowSampleLen = 0) static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, SmpLength wowSampleLen, bool validateHiddenPatterns)
{ {
PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128 PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128
PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length) PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length)
@ -590,7 +590,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN
if(ValidateMODPatternData(file, 16, true)) if(ValidateMODPatternData(file, 16, true))
numChannels = 8; numChannels = 8;
file.Seek(patternStartOffset); file.Seek(patternStartOffset);
} else if(numPatterns != officialPatterns && numChannels == 4 && !wowSampleLen) } else if(numPatterns != officialPatterns && validateHiddenPatterns)
{ {
// Fix SoundTracker modules where "hidden" patterns should be ignored. // Fix SoundTracker modules where "hidden" patterns should be ignored.
// razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b) // razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b)
@ -913,7 +913,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
} }
// Get number of patterns (including some order list sanity checks) // Get number of patterns (including some order list sanity checks)
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, wowSampleLen); PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, wowSampleLen, false);
if(maybeWOW && GetNumChannels() == 8) if(maybeWOW && GetNumChannels() == 8)
{ {
// M.K. with 8 channels = Mod's Grave // M.K. with 8 channels = Mod's Grave
@ -1511,7 +1511,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
file.ReadStruct(fileHeader); file.ReadStruct(fileHeader);
ReadOrderFromArray(Order(), fileHeader.orderList); ReadOrderFromArray(Order(), fileHeader.orderList);
PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels); PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, 0, true);
// Most likely just a file with lots of NULs at the start // Most likely just a file with lots of NULs at the start
if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1)
@ -2201,7 +2201,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags)
m_SongFlags.set(SONG_PT_MODE); m_SongFlags.set(SONG_PT_MODE);
m_playBehaviour.set(kMODIgnorePanning); m_playBehaviour.set(kMODIgnorePanning);
m_playBehaviour.set(kMODOneShotLoops); m_playBehaviour.set(kMODOneShotLoops);
m_playBehaviour.set(kMODSampleSwap); m_playBehaviour.reset(kMODSampleSwap);
return ok; return ok;
} }

View File

@ -721,12 +721,12 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
// MT2 only ever calls effGetChunk for programs, and OpenMPT uses the defaultProgram value to determine // MT2 only ever calls effGetChunk for programs, and OpenMPT uses the defaultProgram value to determine
// whether it should use effSetChunk for programs or banks... // whether it should use effSetChunk for programs or banks...
mixPlug.defaultProgram = -1; mixPlug.defaultProgram = -1;
LimitMax(vstHeader.n, Util::MaxValueOfType(dataSize) - 4); LimitMax(vstHeader.n, std::numeric_limits<decltype(dataSize)>::max() - 4);
dataSize = vstHeader.n + 4; dataSize = vstHeader.n + 4;
} else } else
{ {
mixPlug.defaultProgram = vstHeader.programNr; mixPlug.defaultProgram = vstHeader.programNr;
LimitMax(vstHeader.n, (Util::MaxValueOfType(dataSize) / 4u) - 1); LimitMax(vstHeader.n, (std::numeric_limits<decltype(dataSize)>::max() / 4u) - 1);
dataSize = vstHeader.n * 4 + 4; dataSize = vstHeader.n * 4 + 4;
} }
mixPlug.pluginData.resize(dataSize); mixPlug.pluginData.resize(dataSize);

View File

@ -284,6 +284,8 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
m_FileHistory.push_back(hist); m_FileHistory.push_back(hist);
} }
nonCompatTracker = true; nonCompatTracker = true;
m_playBehaviour.set(kITRetrigger);
m_playBehaviour.set(kITShortSampleRetrig);
m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough
m_nMinPeriod = 1; m_nMinPeriod = 1;
break; break;
@ -297,13 +299,19 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved2); madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved2);
m_nMinPeriod = 1; m_nMinPeriod = 1;
isSchism = true; isSchism = true;
if(fileHeader.cwtv >= SchismVersionFromDate<2016, 05, 13>::Version(S3MFileHeader::trkSchismTracker))
m_playBehaviour.set(kITShortSampleRetrig);
} }
nonCompatTracker = true; nonCompatTracker = true;
break; break;
case S3MFileHeader::trkOpenMPT: case S3MFileHeader::trkOpenMPT:
madeWithTracker = U_("OpenMPT"); {
formatTrackerStr = true; uint32 mptVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16;
m_dwLastSavedWithVersion = Version((fileHeader.cwtv & S3MFileHeader::versionMask) << 16); if(mptVersion >= 0x01'29'00'00)
mptVersion |= fileHeader.reserved2;
m_dwLastSavedWithVersion = Version(mptVersion);
madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
}
break; break;
case S3MFileHeader::trkBeRoTracker: case S3MFileHeader::trkBeRoTracker:
madeWithTracker = U_("BeRoTracker"); madeWithTracker = U_("BeRoTracker");
@ -676,7 +684,9 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
// Version info following: ST3.20 = 0x1320 // Version info following: ST3.20 = 0x1320
// Most significant nibble = Tracker ID, see S3MFileHeader::S3MTrackerVersions // Most significant nibble = Tracker ID, see S3MFileHeader::S3MTrackerVersions
// Following: One nibble = Major version, one byte = Minor version (hex) // Following: One nibble = Major version, one byte = Minor version (hex)
fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast<uint16>((Version::Current().GetRawVersion() >> 16) & S3MFileHeader::versionMask); const uint32 mptVersion = Version::Current().GetRawVersion();
fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast<uint16>((mptVersion >> 16) & S3MFileHeader::versionMask);
fileHeader.reserved2 = static_cast<uint16>(mptVersion);
fileHeader.formatVersion = S3MFileHeader::newVersion; fileHeader.formatVersion = S3MFileHeader::newVersion;
memcpy(fileHeader.magic, "SCRM", 4); memcpy(fileHeader.magic, "SCRM", 4);

View File

@ -12,7 +12,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "MixerLoops.h" #include "MixerLoops.h"
#include "..//soundbase/SampleBuffer.h" #include "../soundbase/SampleBuffer.h"
#include "Snd_defs.h" #include "Snd_defs.h"
#include "ModChannel.h" #include "ModChannel.h"
#ifdef ENABLE_SSE2 #ifdef ENABLE_SSE2

View File

@ -15,7 +15,7 @@
OPENMPT_NAMESPACE_BEGIN OPENMPT_NAMESPACE_BEGIN
void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel) void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag)
{ {
if(resetMask & resetSetPosBasic) if(resetMask & resetSetPosBasic)
{ {
@ -81,6 +81,11 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI
dwFlags = sndFile.ChnSettings[sourceChannel].dwFlags; dwFlags = sndFile.ChnSettings[sourceChannel].dwFlags;
nPan = sndFile.ChnSettings[sourceChannel].nPan; nPan = sndFile.ChnSettings[sourceChannel].nPan;
nGlobalVol = sndFile.ChnSettings[sourceChannel].nVolume; nGlobalVol = sndFile.ChnSettings[sourceChannel].nVolume;
if(dwFlags[CHN_MUTE])
{
dwFlags.reset(CHN_MUTE);
dwFlags.set(muteFlag);
}
} else } else
{ {
dwFlags.reset(); dwFlags.reset();

View File

@ -174,7 +174,7 @@ struct ModChannel
resetTotal = resetSetPosFull, resetTotal = resetSetPosFull,
}; };
void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel); void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag);
void Stop(); void Stop();
bool IsSamplePlaying() const noexcept { return !increment.IsZero(); } bool IsSamplePlaying() const noexcept { return !increment.IsZero(); }

View File

@ -457,22 +457,21 @@ void ModSample::TransposeToFrequency()
} }
// Return tranpose.finetune as 25.7 fixed point value. // Return a pair of {tranpose, finetune}
int32 ModSample::FrequencyToTranspose(uint32 freq) std::pair<int8, int8> ModSample::FrequencyToTranspose(uint32 freq)
{ {
if(!freq) if(!freq)
return 0; return {};
else
return mpt::saturate_round<int32>(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); const auto f2t = mpt::saturate_round<int32>(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2)));
const auto fine = std::div(Clamp(f2t, -16384, 16383), int32(128));
return {static_cast<int8>(fine.quot), static_cast<int8>(fine.rem)};
} }
void ModSample::FrequencyToTranspose() void ModSample::FrequencyToTranspose()
{ {
const int f2t = Clamp(FrequencyToTranspose(nC5Speed), -16384, 16383); std::tie(RelativeTone, nFineTune) = FrequencyToTranspose(nC5Speed);
const auto fine = std::div(f2t, 128);
RelativeTone = static_cast<int8>(fine.quot);
nFineTune = static_cast<int8>(fine.rem);
} }

View File

@ -146,7 +146,7 @@ struct ModSample
// Transpose <-> Frequency conversions // Transpose <-> Frequency conversions
static uint32 TransposeToFrequency(int transpose, int finetune = 0); static uint32 TransposeToFrequency(int transpose, int finetune = 0);
void TransposeToFrequency(); void TransposeToFrequency();
static int32 FrequencyToTranspose(uint32 freq); static std::pair<int8, int8> FrequencyToTranspose(uint32 freq);
void FrequencyToTranspose(); void FrequencyToTranspose();
// Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up)

View File

@ -84,7 +84,7 @@ struct S3MFileHeader
uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set)
uint8le ultraClicks; // Number of channels used for ultra click removal uint8le ultraClicks; // Number of channels used for ultra click removal
uint8le usePanningTable; // 0xFC => read extended panning table uint8le usePanningTable; // 0xFC => read extended panning table
uint16le reserved2; // Schism Tracker uses this for its extended version information uint16le reserved2; // Schism Tracker and OpenMPT use this for their extended version information
uint32le reserved3; // Impulse Tracker hides its edit timer here uint32le reserved3; // Impulse Tracker hides its edit timer here
uint16le reserved4; uint16le reserved4;
uint16le special; // Pointer to special custom data (unused) uint16le special; // Pointer to special custom data (unused)

View File

@ -249,7 +249,7 @@ std::vector<FileType> CSoundFile::GetMediaFoundationFileTypes()
std::wstring guid = std::wstring(valueNameBuf); std::wstring guid = std::wstring(valueNameBuf);
mpt::ustring description = mpt::ToUnicode(std::wstring(reinterpret_cast<WCHAR*>(valueData))); mpt::ustring description = mpt::ToUnicode(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<std::wstring>(valueData, valueDataLen));
description = mpt::String::Replace(description, U_("Byte Stream Handler"), U_("Files")); description = mpt::String::Replace(description, U_("Byte Stream Handler"), U_("Files"));
description = mpt::String::Replace(description, U_("ByteStreamHandler"), U_("Files")); description = mpt::String::Replace(description, U_("ByteStreamHandler"), U_("Files"));

View File

@ -268,7 +268,7 @@ enum SongFlags
}; };
DECLARE_FLAGSET(SongFlags) DECLARE_FLAGSET(SongFlags)
#define SONG_FILE_FLAGS (SONG_FASTVOLSLIDES|SONG_ITOLDEFFECTS|SONG_ITCOMPATGXX|SONG_LINEARSLIDES|SONG_EXFILTERRANGE|SONG_AMIGALIMITS|SONG_S3MOLDVIBRATO|SONG_PT_MODE|SONG_ISAMIGA) #define SONG_FILE_FLAGS (SONG_FASTVOLSLIDES|SONG_ITOLDEFFECTS|SONG_ITCOMPATGXX|SONG_LINEARSLIDES|SONG_EXFILTERRANGE|SONG_AMIGALIMITS|SONG_S3MOLDVIBRATO|SONG_PT_MODE|SONG_ISAMIGA|SONG_IMPORTED)
#define SONG_PLAY_FLAGS (~SONG_FILE_FLAGS) #define SONG_PLAY_FLAGS (~SONG_FILE_FLAGS)
// Global Options (Renderer) // Global Options (Renderer)

View File

@ -93,9 +93,10 @@ public:
state->m_nMusicTempo = sndFile.m_nDefaultTempo; state->m_nMusicTempo = sndFile.m_nDefaultTempo;
state->m_nGlobalVolume = sndFile.m_nDefaultGlobalVolume; state->m_nGlobalVolume = sndFile.m_nDefaultGlobalVolume;
chnSettings.assign(sndFile.GetNumChannels(), ChnSettings()); chnSettings.assign(sndFile.GetNumChannels(), ChnSettings());
const auto muteFlag = CSoundFile::GetChannelMuteFlag();
for(CHANNELINDEX chn = 0; chn < sndFile.GetNumChannels(); chn++) for(CHANNELINDEX chn = 0; chn < sndFile.GetNumChannels(); chn++)
{ {
state->Chn[chn].Reset(ModChannel::resetTotal, sndFile, chn); state->Chn[chn].Reset(ModChannel::resetTotal, sndFile, chn, muteFlag);
state->Chn[chn].nOldGlobalVolSlide = 0; state->Chn[chn].nOldGlobalVolSlide = 0;
state->Chn[chn].nOldChnVolSlide = 0; state->Chn[chn].nOldChnVolSlide = 0;
state->Chn[chn].nNote = state->Chn[chn].nNewNote = state->Chn[chn].nLastNote = NOTE_NONE; state->Chn[chn].nNote = state->Chn[chn].nNewNote = state->Chn[chn].nLastNote = NOTE_NONE;
@ -592,7 +593,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
if(chn.rowCommand.vol) if(chn.rowCommand.vol)
{ {
const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0); const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0);
chn.nPortamentoSlide = porta * 4; chn.nPortamentoSlide = porta;
if(clearEffectCommand) if(clearEffectCommand)
command = CMD_NONE; command = CMD_NONE;
} }
@ -791,7 +792,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
break; break;
// Tone-Portamento // Tone-Portamento
case CMD_TONEPORTAMENTO: case CMD_TONEPORTAMENTO:
if (param) chn.nPortamentoSlide = param << 2; if (param) chn.nPortamentoSlide = param;
break; break;
// Offset // Offset
case CMD_OFFSET: case CMD_OFFSET:
@ -2192,6 +2193,12 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol)) if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol))
return CHANNELINDEX_INVALID; return CHANNELINDEX_INVALID;
if(srcChn.dwFlags[CHN_ADLIB] && m_opl)
{
m_opl->NoteCut(nChn, false);
return CHANNELINDEX_INVALID;
}
const CHANNELINDEX nnaChn = GetNNAChannel(nChn); const CHANNELINDEX nnaChn = GetNNAChannel(nChn);
if(nnaChn == CHANNELINDEX_INVALID) if(nnaChn == CHANNELINDEX_INVALID)
return CHANNELINDEX_INVALID; return CHANNELINDEX_INVALID;
@ -2211,10 +2218,6 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
srcChn.position.Set(0); srcChn.position.Set(0);
srcChn.nROfs = srcChn.nLOfs = 0; srcChn.nROfs = srcChn.nLOfs = 0;
srcChn.rightVol = srcChn.leftVol = 0; srcChn.rightVol = srcChn.leftVol = 0;
if(srcChn.dwFlags[CHN_ADLIB] && m_opl)
{
m_opl->NoteCut(nChn);
}
return nnaChn; return nnaChn;
} }
if(instr > GetNumInstruments()) if(instr > GetNumInstruments())
@ -4193,18 +4196,17 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const
chn.nOldPortaUp = chn.nOldPortaDown = static_cast<uint8>(param); chn.nOldPortaUp = chn.nOldPortaDown = static_cast<uint8>(param);
} }
if(param)
chn.nPortamentoSlide = param;
if(chn.HasCustomTuning()) if(chn.HasCustomTuning())
{ {
//Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando) //Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando)
//to slide per row(not per tick). //to slide per row(not per tick).
const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0;
if(param) if(chn.nPortamentoSlide == 0)
chn.nPortamentoSlide = param; return;
else
if(chn.nPortamentoSlide == 0)
return;
if((chn.nPortamentoDest > 0 && chn.nPortamentoSlide < 0) || if((chn.nPortamentoDest > 0 && chn.nPortamentoSlide < 0) ||
(chn.nPortamentoDest < 0 && chn.nPortamentoSlide > 0)) (chn.nPortamentoDest < 0 && chn.nPortamentoSlide > 0))
@ -4242,47 +4244,49 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const
|| (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669))
|| (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1])
|| (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]); || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]);
if(GetType() == MOD_TYPE_PLM && param >= 0xF0)
int32 delta = chn.nPortamentoSlide;
if(GetType() == MOD_TYPE_PLM && delta >= 0xF0)
{ {
param -= 0xF0; delta -= 0xF0;
doPorta = chn.isFirstTick; doPorta = chn.isFirstTick;
} }
if(param) if(GetType() == MOD_TYPE_669)
{ {
if(GetType() == MOD_TYPE_669) delta *= 10;
{
param *= 10;
}
chn.nPortamentoSlide = param * 4;
} }
if(chn.nPeriod && chn.nPortamentoDest && doPorta) if(chn.nPeriod && chn.nPortamentoDest && doPorta)
{ {
if (chn.nPeriod < chn.nPortamentoDest) if (chn.nPeriod < chn.nPortamentoDest)
{ {
int32 delta = chn.nPortamentoSlide;
if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM)
{ {
uint32 n = chn.nPortamentoSlide / 4; uint32 n = delta;
if (n > 255) n = 255; if (n > 255) n = 255;
// Return (a*b+c/2)/c - no divide error // Return (a*b+c/2)/c - no divide error
// Table is 65536*2(n/192) // Table is 65536*2(n/192)
delta = Util::muldivr(chn.nPeriod, LinearSlideUpTable[n], 65536) - chn.nPeriod; delta = Util::muldivr(chn.nPeriod, LinearSlideUpTable[n], 65536) - chn.nPeriod;
if (delta < 1) delta = 1; if (delta < 1) delta = 1;
} else
{
delta *= 4;
} }
chn.nPeriod += delta; chn.nPeriod += delta;
if (chn.nPeriod > chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; if (chn.nPeriod > chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest;
} else } else
if (chn.nPeriod > chn.nPortamentoDest) if (chn.nPeriod > chn.nPortamentoDest)
{ {
int32 delta = -chn.nPortamentoSlide;
if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM)
{ {
uint32 n = chn.nPortamentoSlide / 4; uint32 n = delta;
if (n > 255) n = 255; if (n > 255) n = 255;
delta = Util::muldivr(chn.nPeriod, LinearSlideDownTable[n], 65536) - chn.nPeriod; delta = Util::muldivr(chn.nPeriod, LinearSlideDownTable[n], 65536) - chn.nPeriod;
if (delta > -1) delta = -1; if (delta > -1) delta = -1;
} else
{
delta *= -4;
} }
chn.nPeriod += delta; chn.nPeriod += delta;
if (chn.nPeriod < chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; if (chn.nPeriod < chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest;
@ -5562,6 +5566,15 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
if(retrigCount && !(retrigCount % retrigSpeed)) if(retrigCount && !(retrigCount % retrigSpeed))
doRetrig = true; doRetrig = true;
retrigCount++; retrigCount++;
} else if(GetType() == MOD_TYPE_MOD)
{
// ProTracker-style retrigger
// Test case: PTRetrigger.mod
const auto tick = m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed;
if(!tick && chn.rowCommand.IsNote())
return;
if(retrigSpeed && !(tick % retrigSpeed))
doRetrig = true;
} else if(GetType() == MOD_TYPE_MTM) } else if(GetType() == MOD_TYPE_MTM)
{ {
// In MultiTracker, E9x retriggers the last note at exactly the x-th tick of the row // In MultiTracker, E9x retriggers the last note at exactly the x-th tick of the row
@ -5624,8 +5637,8 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
} }
uint32 note = chn.nNewNote; uint32 note = chn.nNewNote;
int32 oldPeriod = chn.nPeriod; int32 oldPeriod = chn.nPeriod;
const bool retrigAdlib = chn.dwFlags[CHN_ADLIB] && m_playBehaviour[kOPLRealRetrig]; // ST3 doesn't retrigger OPL notes
if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && retrigAdlib) if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && (!chn.dwFlags[CHN_ADLIB] || GetType() != MOD_TYPE_S3M || m_playBehaviour[kOPLRealRetrig]))
CheckNNA(nChn, 0, note, true); CheckNNA(nChn, 0, note, true);
bool resetEnv = false; bool resetEnv = false;
if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
@ -5638,11 +5651,6 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
if(param < 0x100) if(param < 0x100)
resetEnv = true; resetEnv = true;
} }
if(retrigAdlib && chn.pModSample && m_opl)
{
m_opl->NoteCut(nChn);
m_opl->Patch(nChn, chn.pModSample->adlib);
}
const bool fading = chn.dwFlags[CHN_NOTEFADE]; const bool fading = chn.dwFlags[CHN_NOTEFADE];
const auto oldPrevNoteOffset = chn.prevNoteOffset; const auto oldPrevNoteOffset = chn.prevNoteOffset;

View File

@ -478,11 +478,13 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
} }
// Adjust channels // Adjust channels
for(CHANNELINDEX ich = 0; ich < MAX_BASECHANNELS; ich++) const auto muteFlag = GetChannelMuteFlag();
for(CHANNELINDEX chn = 0; chn < MAX_BASECHANNELS; chn++)
{ {
LimitMax(ChnSettings[ich].nVolume, uint16(64)); LimitMax(ChnSettings[chn].nVolume, uint16(64));
if (ChnSettings[ich].nPan > 256) ChnSettings[ich].nPan = 128; if(ChnSettings[chn].nPan > 256)
m_PlayState.Chn[ich].Reset(ModChannel::resetTotal, *this, ich); ChnSettings[chn].nPan = 128;
m_PlayState.Chn[chn].Reset(ModChannel::resetTotal, *this, chn, muteFlag);
} }
// Checking samples, load external samples // Checking samples, load external samples
@ -770,8 +772,9 @@ double CSoundFile::GetCurrentBPM() const
void CSoundFile::ResetPlayPos() void CSoundFile::ResetPlayPos()
{ {
const auto muteFlag = GetChannelMuteFlag();
for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i); m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag);
visitedSongRows.Initialize(true); visitedSongRows.Initialize(true);
m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED); m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED);
@ -1055,7 +1058,6 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
{ {
playBehaviour.set(kOPLFlexibleNoteOff); playBehaviour.set(kOPLFlexibleNoteOff);
playBehaviour.set(kOPLwithNNA); playBehaviour.set(kOPLwithNNA);
playBehaviour.set(kOPLRealRetrig);
} }
break; break;
@ -1176,7 +1178,6 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kITDoNotOverrideChannelPan); playBehaviour.set(kITDoNotOverrideChannelPan);
playBehaviour.set(kITDCTBehaviour); playBehaviour.set(kITDCTBehaviour);
playBehaviour.set(kOPLwithNNA); playBehaviour.set(kOPLwithNNA);
playBehaviour.set(kOPLRealRetrig);
break; break;
case MOD_TYPE_S3M: case MOD_TYPE_S3M:
@ -1293,10 +1294,11 @@ const char *CSoundFile::GetInstrumentName(INSTRUMENTINDEX nInstr) const
bool CSoundFile::InitChannel(CHANNELINDEX nChn) bool CSoundFile::InitChannel(CHANNELINDEX nChn)
{ {
if(nChn >= MAX_BASECHANNELS) return true; if(nChn >= MAX_BASECHANNELS)
return true;
ChnSettings[nChn].Reset(); ChnSettings[nChn].Reset();
m_PlayState.Chn[nChn].Reset(ModChannel::resetTotal, *this, nChn); m_PlayState.Chn[nChn].Reset(ModChannel::resetTotal, *this, nChn, GetChannelMuteFlag());
#ifdef MODPLUG_TRACKER #ifdef MODPLUG_TRACKER
if(GetpModDoc() != nullptr) if(GetpModDoc() != nullptr)
@ -1775,6 +1777,16 @@ const CModSpecifications& CSoundFile::GetModSpecifications(const MODTYPE type)
} }
ChannelFlags CSoundFile::GetChannelMuteFlag()
{
#ifdef MODPLUG_TRACKER
return (TrackerSettings::Instance().m_dwPatternSetup & PATTERN_SYNCMUTE) ? CHN_SYNCMUTE : CHN_MUTE;
#else
return CHN_MUTE;
#endif
}
// Find an unused sample slot. If it is going to be assigned to an instrument, targetInstrument should be specified. // Find an unused sample slot. If it is going to be assigned to an instrument, targetInstrument should be specified.
// SAMPLEINDEX_INVLAID is returned if no free sample slot could be found. // SAMPLEINDEX_INVLAID is returned if no free sample slot could be found.
SAMPLEINDEX CSoundFile::GetNextFreeSample(INSTRUMENTINDEX targetInstrument, SAMPLEINDEX start) const SAMPLEINDEX CSoundFile::GetNextFreeSample(INSTRUMENTINDEX targetInstrument, SAMPLEINDEX start) const

View File

@ -462,16 +462,17 @@ public:
ResamplingMode m_nResampling; // Resampling mode (if overriding the globally set resampling) ResamplingMode m_nResampling; // Resampling mode (if overriding the globally set resampling)
int32 m_nRepeatCount = 0; // -1 means repeat infinitely. int32 m_nRepeatCount = 0; // -1 means repeat infinitely.
ORDERINDEX m_nMaxOrderPosition; ORDERINDEX m_nMaxOrderPosition;
ModChannelSettings ChnSettings[MAX_BASECHANNELS]; // Initial channels settings ModChannelSettings ChnSettings[MAX_BASECHANNELS]; // Initial channels settings
CPatternContainer Patterns; CPatternContainer Patterns;
ModSequenceSet Order; // Pattern sequences (order lists) ModSequenceSet Order; // Pattern sequences (order lists)
protected: protected:
ModSample Samples[MAX_SAMPLES]; // Sample Headers ModSample Samples[MAX_SAMPLES];
public: public:
ModInstrument *Instruments[MAX_INSTRUMENTS]; // Instrument Headers ModInstrument *Instruments[MAX_INSTRUMENTS]; // Instrument Headers
MIDIMacroConfig m_MidiCfg; // MIDI Macro config table MIDIMacroConfig m_MidiCfg; // MIDI Macro config table
#ifndef NO_PLUGINS #ifndef NO_PLUGINS
SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins
uint32 m_loadedPlugins = 0; // Not a PLUGINDEX because number of loaded plugins may exceed MAX_MIXPLUGINS during MIDI conversion
#endif #endif
mpt::charbuf<MAX_SAMPLENAME> m_szNames[MAX_SAMPLES]; // Sample names mpt::charbuf<MAX_SAMPLENAME> m_szNames[MAX_SAMPLES]; // Sample names
@ -720,6 +721,8 @@ public:
const CModSpecifications& GetModSpecifications() const {return *m_pModSpecs;} const CModSpecifications& GetModSpecifications() const {return *m_pModSpecs;}
static const CModSpecifications& GetModSpecifications(const MODTYPE type); static const CModSpecifications& GetModSpecifications(const MODTYPE type);
static ChannelFlags GetChannelMuteFlag();
#ifdef MODPLUG_TRACKER #ifdef MODPLUG_TRACKER
void PatternTranstionChnSolo(const CHANNELINDEX chnIndex); void PatternTranstionChnSolo(const CHANNELINDEX chnIndex);
void PatternTransitionChnUnmuteAll(); void PatternTransitionChnUnmuteAll();

View File

@ -206,18 +206,6 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget
{ {
MPT_ASSERT_ALWAYS(m_MixerSettings.IsValid()); MPT_ASSERT_ALWAYS(m_MixerSettings.IsValid());
bool mixPlugins = false;
#ifndef NO_PLUGINS
for(const auto &plug : m_MixPlugins)
{
if(plug.pMixPlugin)
{
mixPlugins = true;
break;
}
}
#endif // NO_PLUGINS
samplecount_t countRendered = 0; samplecount_t countRendered = 0;
samplecount_t countToRender = count; samplecount_t countToRender = count;
@ -303,14 +291,16 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget
m_opl->Mix(MixSoundBuffer, countChunk, m_OPLVolumeFactor * m_nVSTiVolume / 48); m_opl->Mix(MixSoundBuffer, countChunk, m_OPLVolumeFactor * m_nVSTiVolume / 48);
} }
#ifndef NO_REVERB #ifndef NO_REVERB
m_Reverb.Process(MixSoundBuffer, countChunk); m_Reverb.Process(MixSoundBuffer, countChunk);
#endif // NO_REVERB #endif // NO_REVERB
if(mixPlugins) #ifndef NO_PLUGINS
if(m_loadedPlugins)
{ {
ProcessPlugins(countChunk); ProcessPlugins(countChunk);
} }
#endif // NO_PLUGINS
if(m_MixerSettings.gnChannels == 1) if(m_MixerSettings.gnChannels == 1)
{ {
@ -653,8 +643,9 @@ bool CSoundFile::ProcessRow()
visitedSongRows.Initialize(true); visitedSongRows.Initialize(true);
} }
// When jumping to the next subsong, stop all playing notes from the previous song... // When jumping to the next subsong, stop all playing notes from the previous song...
const auto muteFlag = CSoundFile::GetChannelMuteFlag();
for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i); m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag);
StopAllVsti(); StopAllVsti();
// ...and the global playback information. // ...and the global playback information.
m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
@ -2348,7 +2339,7 @@ bool CSoundFile::ReadNote()
} }
// Volume ramping // Volume ramping
chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0); chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0 && !chn.dwFlags[CHN_ADLIB]);
constexpr uint8 VUMETER_DECAY = 4; constexpr uint8 VUMETER_DECAY = 4;
chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0; chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0;

View File

@ -656,13 +656,18 @@ void CSoundFile::UpgradeModule()
} }
} }
if(GetType() == MOD_TYPE_MPT && GetNumInstruments() && m_dwLastSavedWithVersion >= MPT_V("1.28.00.20") && m_dwLastSavedWithVersion <= MPT_V("1.29.55.00")) if(GetType() & (MOD_TYPE_MPT | MOD_TYPE_S3M))
{ {
for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++)
{ {
if(Samples[i].uFlags[CHN_ADLIB]) if(Samples[i].uFlags[CHN_ADLIB])
{ {
m_playBehaviour.set(kOPLNoResetAtEnvelopeEnd); if(GetType() == MOD_TYPE_MPT && GetNumInstruments() && m_dwLastSavedWithVersion >= MPT_V("1.28.00.20") && m_dwLastSavedWithVersion <= MPT_V("1.29.00.55"))
m_playBehaviour.set(kOPLNoResetAtEnvelopeEnd);
if(GetType() == MOD_TYPE_S3M && m_dwLastSavedWithVersion < MPT_V("1.29"))
m_playBehaviour.set(kOPLRealRetrig);
else if(GetType() != MOD_TYPE_S3M)
m_playBehaviour.reset(kOPLRealRetrig);
break; break;
} }
} }

View File

@ -330,9 +330,7 @@ void XMSample::ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compa
relnote = mptSmp.RelativeTone; relnote = mptSmp.RelativeTone;
} else } else
{ {
int f2t = ModSample::FrequencyToTranspose(mptSmp.nC5Speed); std::tie(relnote, finetune) = ModSample::FrequencyToTranspose(mptSmp.nC5Speed);
relnote = static_cast<int8>(f2t / 128);
finetune = static_cast<int8>(f2t & 0x7F);
} }
flags = 0; flags = 0;

View File

@ -44,6 +44,7 @@ IMixPlugin::IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN
, m_SndFile(sndFile) , m_SndFile(sndFile)
, m_pMixStruct(mixStruct) , m_pMixStruct(mixStruct)
{ {
m_SndFile.m_loadedPlugins++;
m_MixState.pMixBuffer = (mixsample_t *)((((intptr_t)m_MixBuffer) + 7) & ~7); m_MixState.pMixBuffer = (mixsample_t *)((((intptr_t)m_MixBuffer) + 7) & ~7);
while(m_pMixStruct != &(m_SndFile.m_MixPlugins[m_nSlot]) && m_nSlot < MAX_MIXPLUGINS - 1) while(m_pMixStruct != &(m_SndFile.m_MixPlugins[m_nSlot]) && m_nSlot < MAX_MIXPLUGINS - 1)
{ {
@ -71,6 +72,7 @@ IMixPlugin::~IMixPlugin()
if (m_pPrev) m_pPrev->m_pNext = m_pNext; if (m_pPrev) m_pPrev->m_pNext = m_pNext;
m_pPrev = nullptr; m_pPrev = nullptr;
m_pNext = nullptr; m_pNext = nullptr;
m_SndFile.m_loadedPlugins--;
} }

View File

@ -392,9 +392,7 @@ void CVstPluginManager::EnumerateDirectXDMOs()
if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize)) if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize))
{ {
mpt::String::SetNullTerminator(name); VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(DMOPlugin::Create, true, mpt::PathString::FromNative(Util::GUIDToString(clsid)), mpt::PathString::FromNative(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(name, datasize)));
VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(DMOPlugin::Create, true, mpt::PathString::FromNative(Util::GUIDToString(clsid)), mpt::PathString::FromNative(name));
if(plug != nullptr) if(plug != nullptr)
{ {
try try

View File

@ -3607,7 +3607,7 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved)
VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES);
VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), mixLevelsCompatible); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), mixLevelsCompatible);
VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeClassic); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeClassic);
VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MPT_V("1.27.00.00")); VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version::Current() : MPT_V("1.27.00.00"));
VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0);
// Channels // Channels