Update libopenmpt to version 0.5.6

This commit is contained in:
Christopher Snowhill 2021-03-14 14:55:49 -07:00
parent ba7aaec69c
commit 9a427cf03c
50 changed files with 331 additions and 148 deletions

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2020, OpenMPT contributors Copyright (c) 2004-2021, OpenMPT contributors
Copyright (c) 1997-2003, Olivier Lapicque Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved. All rights reserved.

View file

@ -170,36 +170,24 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`.
- emscripten (on Unix-like systems): - emscripten (on Unix-like systems):
libopenmpt has been tested and verified to work with emscripten 1.38.5
or later. Earlier versions are not supported.
Run: Run:
# generates WebAssembly with dynamic heap growth # generates WebAssembly with JavaScript fallback
make CONFIG=emscripten EMSCRIPTEN_TARGET=all
or
# generates WebAssembly
make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm
or or
# generates asm.js with a fixed size 128MB heap # generates JavaScript with compatibility for older VMs
make CONFIG=emscripten EMSCRIPTEN_TARGET=asmjs128m
or
# generates asm.js with a fixed default size heap (as of Emscripten
# 1.38.11, this amounts to 16MB)
make CONFIG=emscripten EMSCRIPTEN_TARGET=asmjs
or
# generates JavaScript with dynamic heap growth and with
# compatibility for older VMs
make CONFIG=emscripten EMSCRIPTEN_TARGET=js make CONFIG=emscripten EMSCRIPTEN_TARGET=js
Running the test suite on the command line is also supported by using Running the test suite on the command line is also supported by using
node.js. Version 8.9.1 or greater has been tested. Earlier versions node.js. Depending on how your distribution calls the `node.js` binary,
might or might not work. Depending on how your distribution calls the you might have to edit `build/make/config-emscripten.mk`.
`node.js` binary, you might have to edit
`build/make/config-emscripten.mk`.
- DJGPP / DOS - DJGPP / DOS
@ -229,7 +217,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`.
- other compilers: - other compilers:
To compile libopenmpt with other C++14 compliant compilers, run: To compile libopenmpt with other C++17 compliant compilers, run:
make CONFIG=generic make CONFIG=generic

View file

@ -1,4 +1,4 @@
MPT_SVNVERSION=13932 MPT_SVNVERSION=14311
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.4 MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6
MPT_SVNDATE=2020-11-29T15:01:39.790705Z MPT_SVNDATE=2021-03-14T15:27:41.476009Z

View file

@ -48,6 +48,15 @@ LDFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
else ifeq ($(EMSCRIPTEN_TARGET),audioworkletprocessor)
# emits an es6 module in a single file suitable for use in an 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
CFLAGS += -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
else ifeq ($(EMSCRIPTEN_TARGET),wasm) else ifeq ($(EMSCRIPTEN_TARGET),wasm)
# emits native wasm. # emits native wasm.
CPPFLAGS += -DMPT_BUILD_WASM CPPFLAGS += -DMPT_BUILD_WASM

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#define OPENMPT_VERSION_SVNVERSION "13932" #define OPENMPT_VERSION_SVNVERSION "14311"
#define OPENMPT_VERSION_REVISION 13932 #define OPENMPT_VERSION_REVISION 14311
#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.4" #define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6"
#define OPENMPT_VERSION_DATE "2020-11-29T15:01:39.790705Z" #define OPENMPT_VERSION_DATE "2021-03-14T15:27:41.476009Z"
#define OPENMPT_VERSION_IS_PACKAGE 1 #define OPENMPT_VERSION_IS_PACKAGE 1

View file

@ -309,6 +309,12 @@
#endif #endif
#if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR)
#define MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
#define MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
#endif
#if MPT_OS_DJGPP #if MPT_OS_DJGPP
#define MPT_COMPILER_QUIRK_NO_WCHAR #define MPT_COMPILER_QUIRK_NO_WCHAR
#endif #endif

View file

@ -27,6 +27,8 @@ namespace mpt
{ {
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
template <typename T> template <typename T>
static T log2(T x) static T log2(T x)
{ {
@ -58,6 +60,8 @@ static MPT_CONSTEXPR14_FUN bool is_mask(T x)
return false; return false;
} }
#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
namespace { namespace {
template <typename T> struct default_hash { }; template <typename T> struct default_hash { };
@ -91,6 +95,7 @@ static T generate_timeseed()
hash(std::begin(bytes), std::end(bytes)); hash(std::begin(bytes), std::end(bytes));
} }
#if !defined(MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK)
{ {
uint64be time; uint64be time;
time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock().now().time_since_epoch()).count();
@ -98,6 +103,7 @@ static T generate_timeseed()
std::memcpy(bytes, &time, sizeof(time)); std::memcpy(bytes, &time, sizeof(time));
hash(std::begin(bytes), std::end(bytes)); hash(std::begin(bytes), std::end(bytes));
} }
#endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
return static_cast<T>(hash.result()); return static_cast<T>(hash.result());
@ -126,8 +132,11 @@ crand::result_type crand::operator()()
#endif // MODPLUG_TRACKER #endif // MODPLUG_TRACKER
sane_random_device::sane_random_device() sane_random_device::sane_random_device()
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
: rd_reliable(false) : rd_reliable(false)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ {
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
try try
{ {
prd = std::make_unique<std::random_device>(); prd = std::make_unique<std::random_device>();
@ -140,6 +149,7 @@ sane_random_device::sane_random_device()
rd_reliable = false; rd_reliable = false;
} }
if(!rd_reliable) if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ {
init_fallback(); init_fallback();
} }
@ -147,8 +157,11 @@ sane_random_device::sane_random_device()
sane_random_device::sane_random_device(const std::string & token_) sane_random_device::sane_random_device(const std::string & token_)
: token(token_) : token(token_)
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
, rd_reliable(false) , rd_reliable(false)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ {
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
try try
{ {
prd = std::make_unique<std::random_device>(token); prd = std::make_unique<std::random_device>(token);
@ -161,6 +174,7 @@ sane_random_device::sane_random_device(const std::string & token_)
rd_reliable = false; rd_reliable = false;
} }
if(!rd_reliable) if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ {
init_fallback(); init_fallback();
} }
@ -198,6 +212,7 @@ sane_random_device::result_type sane_random_device::operator()()
{ {
mpt::lock_guard<mpt::mutex> l(m); mpt::lock_guard<mpt::mutex> l(m);
result_type result = 0; result_type result = 0;
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
if(prd) if(prd)
{ {
try try
@ -244,6 +259,7 @@ sane_random_device::result_type sane_random_device::operator()()
rd_reliable = false; rd_reliable = false;
} }
if(!rd_reliable) if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ // std::random_device is unreliable { // std::random_device is unreliable
// XOR the generated random number with more entropy from the time-seeded // XOR the generated random number with more entropy from the time-seeded
// PRNG. // PRNG.

View file

@ -449,8 +449,10 @@ class sane_random_device
private: private:
mpt::mutex m; mpt::mutex m;
std::string token; std::string token;
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
std::unique_ptr<std::random_device> prd; std::unique_ptr<std::random_device> prd;
bool rd_reliable; bool rd_reliable;
#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
std::unique_ptr<std::mt19937> rd_fallback; std::unique_ptr<std::mt19937> rd_fallback;
public: public:
typedef unsigned int result_type; typedef unsigned int result_type;

View file

@ -582,12 +582,12 @@ mpt::ustring GetFullCreditsString()
"libopenmpt (based on OpenMPT / ModPlug Tracker)\n" "libopenmpt (based on OpenMPT / ModPlug Tracker)\n"
#endif #endif
"\n" "\n"
"Copyright \xC2\xA9 2004-2020 Contributors\n" "Copyright \xC2\xA9 2004-2021 Contributors\n"
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
"\n" "\n"
"Contributors:\n" "Contributors:\n"
"Johannes Schultz (2008-2020)\n" "Johannes Schultz (2008-2021)\n"
"J\xC3\xB6rn Heusipp (2012-2020)\n" "J\xC3\xB6rn Heusipp (2012-2021)\n"
"Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n"
"Robin Fernandes (2004-2007)\n" "Robin Fernandes (2004-2007)\n"
"Sergiy Pylypenko (2007)\n" "Sergiy Pylypenko (2007)\n"
@ -777,7 +777,7 @@ mpt::ustring GetFullCreditsString()
mpt::ustring GetLicenseString() mpt::ustring GetLicenseString()
{ {
return MPT_UTF8( return MPT_UTF8(
"Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 2004-2021, OpenMPT contributors" "\n"
"Copyright (c) 1997-2003, Olivier Lapicque" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n"
"All rights reserved." "\n" "All rights reserved." "\n"
"" "\n" "" "\n"

View file

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

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2020, OpenMPT contributors Copyright (c) 2004-2021, OpenMPT contributors
Copyright (c) 1997-2003, Olivier Lapicque Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved. All rights reserved.

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2020, OpenMPT contributors Copyright (c) 2004-2021, OpenMPT contributors
Copyright (c) 1997-2003, Olivier Lapicque Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved. All rights reserved.

View file

@ -7,6 +7,7 @@ Modifications:
fails to compile. fails to compile.
* Macro redefinition of alloca with mingw-w64 has been fixed. * Macro redefinition of alloca with mingw-w64 has been fixed.
* Macro redefinition of STB_VORBIS_NO_STDIO has been fixed. * Macro redefinition of STB_VORBIS_NO_STDIO has been fixed.
* Bugfix https://github.com/nothings/stb/pull/1064 has been applied.
Modifications are always additions and have been marked with // OpenMPT. Modifications are always additions and have been marked with // OpenMPT.
For building, premake is used to generate Visual Studio project files. For building, premake is used to generate Visual Studio project files.

View file

@ -3652,7 +3652,12 @@ static int start_decoder(vorb *f)
//user comments //user comments
f->comment_list_length = get32_packet(f); f->comment_list_length = get32_packet(f);
f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length));
#if 0 // OpenMPT
if (f->comment_list == NULL) return error(f, VORBIS_outofmem); if (f->comment_list == NULL) return error(f, VORBIS_outofmem);
#else // OpenMPT
// Bugfix from https://github.com/nothings/stb/pull/1064 // OpenMPT
if (f->comment_list_length > 0 && f->comment_list == NULL) return error(f, VORBIS_outofmem); // OpenMPT
#endif // OpenMPT
for(i=0; i < f->comment_list_length; ++i) { for(i=0; i < f->comment_list_length; ++i) {
len = get32_packet(f); len = get32_packet(f);

View file

@ -981,7 +981,7 @@ Declare Function openmpt_module_read_interleaved_stereo(ByVal module As openmpt_
\param module The module handle to work on. \param module The module handle to work on.
\param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
\param count Number of audio frames to render per channel. \param count Number of audio frames to render per channel.
\param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
\return The number of frames actually rendered. \return The number of frames actually rendered.
\retval 0 The end of song has been reached. \retval 0 The end of song has been reached.
\remarks The output buffers are only written to up to the returned number of elements. \remarks The output buffers are only written to up to the returned number of elements.
@ -1011,7 +1011,7 @@ Declare Function openmpt_module_read_interleaved_float_stereo(ByVal module As op
\param module The module handle to work on. \param module The module handle to work on.
\param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
\param count Number of audio frames to render per channel. \param count Number of audio frames to render per channel.
\param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
\return The number of frames actually rendered. \return The number of frames actually rendered.
\retval 0 The end of song has been reached. \retval 0 The end of song has been reached.
\remarks The output buffers are only written to up to the returned number of elements. \remarks The output buffers are only written to up to the returned number of elements.

View file

@ -5,6 +5,36 @@ 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.6 (2021-03-14)
* AMS: Avoid allocating excessive amount of memory for compressed song message
in malformed files.
* S3M: Some samples or OPL patches were imported with a too high sample rate
if module was saved with Scream Tracker 3.
* vorbis: Update to v1.3.7 (2020-07-04).
### libopenmpt 0.5.5 (2021-01-31)
* [**New**] `Makefile` `CONFIG=emscripten` now supports
`EMSCRIPTEN_TARGET=audioworkletprocessor` which builds an ES6 module in
a single file with reduced dependencies suitable to be used in an
AudioWorkletProcessor.
* [**Bug**] stb_vorbis: Fix decoding of Vorbis streams without comments which
affected most Vorbis samples since stb_vorbis v1.20.
* `openmpt::ext::interactive::set_pitch_factor` wasn't applied to OPL voices.
* OPL channel state (in particular current patch) is now updated when seeking.
* The FT2 tremolo quirk is now also applied to MOD files. FT2 just copied the
quirky code from ProTracker!
* DMF: Preserve effects better in some situations where there is more than one
effect in a pattern cell.
* DMF: Improve import of finetune effect with parameters larger than +/-15.
* mpg123: Update to v1.26.4 (2020-12-24).
* pugixml: Update to v1.11.4 (2020-12-22).
### libopenmpt 0.5.4 (2020-11-29) ### libopenmpt 0.5.4 (2020-11-29)
* AMS: An upper bound for uncompressed sample size is now established to * AMS: An upper bound for uncompressed sample size is now established to

View file

@ -210,7 +210,7 @@ static void config( HWND hwndParent ) {
static void about( HWND hwndParent ) { static void about( HWND hwndParent ) {
std::ostringstream about; std::ostringstream about;
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
about << " Copyright (c) 2013-2020 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " Copyright (c) 2013-2021 OpenMPT developers (https://lib.openmpt.org/)" << std::endl;
about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
about << std::endl; about << std::endl;
about << openmpt::string::get( "contact" ) << std::endl; about << openmpt::string::get( "contact" ) << std::endl;

View file

@ -1071,7 +1071,7 @@ LIBOPENMPT_API size_t openmpt_module_read_interleaved_stereo( openmpt_module * m
* \param mod The module handle to work on. * \param mod The module handle to work on.
* \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
* \param count Number of audio frames to render per channel. * \param count Number of audio frames to render per channel.
* \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). * \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
* \return The number of frames actually rendered. * \return The number of frames actually rendered.
* \retval 0 The end of song has been reached. * \retval 0 The end of song has been reached.
* \remarks The output buffers are only written to up to the returned number of elements. * \remarks The output buffers are only written to up to the returned number of elements.
@ -1099,7 +1099,7 @@ LIBOPENMPT_API size_t openmpt_module_read_interleaved_float_stereo( openmpt_modu
* \param mod The module handle to work on. * \param mod The module handle to work on.
* \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. * \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
* \param count Number of audio frames to render per channel. * \param count Number of audio frames to render per channel.
* \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). * \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
* \return The number of frames actually rendered. * \return The number of frames actually rendered.
* \retval 0 The end of song has been reached. * \retval 0 The end of song has been reached.
* \remarks The output buffers are only written to up to the returned number of elements. * \remarks The output buffers are only written to up to the returned number of elements.

View file

@ -750,7 +750,7 @@ public:
/*! /*!
\param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
\param count Number of audio frames to render per channel. \param count Number of audio frames to render per channel.
\param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
\return The number of frames actually rendered. \return The number of frames actually rendered.
\retval 0 The end of song has been reached. \retval 0 The end of song has been reached.
\remarks The output buffers are only written to up to the returned number of elements. \remarks The output buffers are only written to up to the returned number of elements.
@ -776,7 +776,7 @@ public:
/*! /*!
\param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced. \param samplerate Sample rate to render output. Should be in [8000,192000], but this is not enforced.
\param count Number of audio frames to render per channel. \param count Number of audio frames to render per channel.
\param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved suad surround output in the order (L,R,RL,RR). \param interleaved_quad Pointer to a buffer of at least count*4 elements that receives the interleaved quad surround output in the order (L,R,RL,RR).
\return The number of frames actually rendered. \return The number of frames actually rendered.
\retval 0 The end of song has been reached. \retval 0 The end of song has been reached.
\remarks The output buffers are only written to up to the returned number of elements. \remarks The output buffers are only written to up to the returned number of elements.
@ -1119,7 +1119,7 @@ public:
\param value The value that should be set. \param value The value that should be set.
\throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
\sa openmpt::module::get_ctls \sa openmpt::module::get_ctls
\deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_float(), or openmpt::module::ctl_set_string(). \deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_floatingpoint(), or openmpt::module::ctl_set_string().
*/ */
LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value ); LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value );
//! Set ctl boolean value //! Set ctl boolean value

View file

@ -1734,12 +1734,12 @@ int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char *
openmpt::interface::check_pointer( interface ); openmpt::interface::check_pointer( interface );
std::memset( interface, 0, interface_size ); std::memset( interface, 0, interface_size );
int result = 0; int result = 0;
if ( !strcmp( interface_id, "" ) ) { if ( !std::strcmp( interface_id, "" ) ) {
result = 0; result = 0;
} else if ( !strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS ) && ( interface_size == sizeof( openmpt_module_ext_interface_pattern_vis ) ) ) { } else if ( !std::strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS ) && ( interface_size == sizeof( openmpt_module_ext_interface_pattern_vis ) ) ) {
openmpt_module_ext_interface_pattern_vis * i = static_cast< openmpt_module_ext_interface_pattern_vis * >( interface ); openmpt_module_ext_interface_pattern_vis * i = static_cast< openmpt_module_ext_interface_pattern_vis * >( interface );
i->get_pattern_row_channel_volume_effect_type = &get_pattern_row_channel_volume_effect_type; i->get_pattern_row_channel_volume_effect_type = &get_pattern_row_channel_volume_effect_type;
i->get_pattern_row_channel_effect_type = &get_pattern_row_channel_effect_type; i->get_pattern_row_channel_effect_type = &get_pattern_row_channel_effect_type;
@ -1747,7 +1747,7 @@ int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char *
} else if ( !strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE ) && ( interface_size == sizeof( openmpt_module_ext_interface_interactive ) ) ) { } else if ( !std::strcmp( interface_id, LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE ) && ( interface_size == sizeof( openmpt_module_ext_interface_interactive ) ) ) {
openmpt_module_ext_interface_interactive * i = static_cast< openmpt_module_ext_interface_interactive * >( interface ); openmpt_module_ext_interface_interactive * i = static_cast< openmpt_module_ext_interface_interactive * >( interface );
i->set_current_speed = &set_current_speed; i->set_current_speed = &set_current_speed;
i->set_current_tempo = &set_current_tempo; i->set_current_tempo = &set_current_tempo;

View file

@ -66,8 +66,8 @@ MPT_WARNING("Warning: libopenmpt built in non thread-safe mode because mutexes a
MPT_WARNING("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") MPT_WARNING("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.")
#endif // MINGW #endif // MINGW
#if MPT_CLANG_AT_LEAST(5,0,0) && defined(__powerpc__) && !defined(__powerpc64__) #if MPT_CLANG_AT_LEAST(5,0,0) && MPT_CLANG_BEFORE(11,0,0) && defined(__powerpc__) && !defined(__powerpc64__)
MPT_WARNING("Warning: libopenmpt is known to trigger bad code generation with Clang 5 or later on powerpc (32bit) when using -O3. See <https://bugs.llvm.org/show_bug.cgi?id=46683>.") MPT_WARNING("Warning: libopenmpt is known to trigger bad code generation with Clang 5..10 on powerpc (32bit) when using -O3. See <https://bugs.llvm.org/show_bug.cgi?id=46683>.")
#endif #endif
#endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS
@ -1106,9 +1106,9 @@ double module_impl::set_position_seconds( double seconds ) {
} else { } else {
subsong = &subsongs[m_current_subsong]; subsong = &subsongs[m_current_subsong];
} }
m_sndFile->SetCurrentOrder( static_cast<ORDERINDEX>( subsong->start_order ) );
GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back(); GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder; m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
m_sndFile->SetCurrentOrder( t.lastOrder );
m_sndFile->m_PlayState.m_nNextRow = t.lastRow; m_sndFile->m_PlayState.m_nNextRow = t.lastRow;
m_currentPositionSeconds = base_seconds + t.duration; m_currentPositionSeconds = base_seconds + t.duration;
return m_currentPositionSeconds; return m_currentPositionSeconds;

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 4 #define OPENMPT_API_VERSION_PATCH 6
/*! \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=4 LIBOPENMPT_VERSION_PATCH=6
LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2 LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=4 LIBOPENMPT_LTVER_REVISION=6
LIBOPENMPT_LTVER_AGE=2 LIBOPENMPT_LTVER_AGE=2

View file

@ -465,7 +465,7 @@ static void clear_current_timeinfo() {
static void WINAPI openmpt_About( HWND win ) { static void WINAPI openmpt_About( HWND win ) {
std::ostringstream about; std::ostringstream about;
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
about << " Copyright (c) 2013-2020 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " Copyright (c) 2013-2021 OpenMPT developers (https://lib.openmpt.org/)" << std::endl;
about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
about << std::endl; about << std::endl;
about << openmpt::string::get( "contact" ) << std::endl; about << openmpt::string::get( "contact" ) << std::endl;

View file

@ -8,7 +8,7 @@
*/ */
static const char * const license = static const char * const license =
"Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 2004-2021, OpenMPT contributors" "\n"
"Copyright (c) 1997-2003, Olivier Lapicque" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n"
"All rights reserved." "\n" "All rights reserved." "\n"
"" "\n" "" "\n"
@ -74,6 +74,7 @@ static const char * const license =
#include <conio.h> #include <conio.h>
#include <fcntl.h> #include <fcntl.h>
#include <io.h> #include <io.h>
#include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <windows.h> #include <windows.h>
@ -459,7 +460,7 @@ static std::string seconds_to_string( double time ) {
static void show_info( std::ostream & log, bool verbose ) { static void show_info( std::ostream & log, bool verbose ) {
log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl; log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl;
log << "Copyright (c) 2013-2020 OpenMPT developers <https://lib.openmpt.org/>" << std::endl; log << "Copyright (c) 2013-2021 OpenMPT developers <https://lib.openmpt.org/>" << std::endl;
if ( !verbose ) { if ( !verbose ) {
log << std::endl; log << std::endl;
return; return;
@ -536,7 +537,7 @@ static void show_info( std::ostream & log, bool verbose ) {
static void show_man_version( textout & log ) { static void show_man_version( textout & log ) {
log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl; log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl;
log << std::endl; log << std::endl;
log << "Copyright (c) 2013-2020 OpenMPT developers <https://lib.openmpt.org/>" << std::endl; log << "Copyright (c) 2013-2021 OpenMPT developers <https://lib.openmpt.org/>" << std::endl;
} }
static void show_short_version( textout & log ) { static void show_short_version( textout & log ) {
@ -2288,8 +2289,13 @@ static int main( int argc, char * argv [] ) {
#endif #endif
textout_dummy dummy_log; textout_dummy dummy_log;
#if defined(WIN32) #if defined(WIN32)
#if defined(UNICODE)
textout_ostream_console std_out( std::wcout, STD_OUTPUT_HANDLE ); textout_ostream_console std_out( std::wcout, STD_OUTPUT_HANDLE );
textout_ostream_console std_err( std::wclog, STD_ERROR_HANDLE ); textout_ostream_console std_err( std::wclog, STD_ERROR_HANDLE );
#else
textout_ostream_console std_out( std::cout, STD_OUTPUT_HANDLE );
textout_ostream_console std_err( std::clog, STD_ERROR_HANDLE );
#endif
#else #else
textout_ostream std_out( std::cout ); textout_ostream std_out( std::cout );
textout_ostream std_err( std::clog ); textout_ostream std_err( std::clog );

View file

@ -890,11 +890,19 @@ public:
class textout_ostream_console : public textout { class textout_ostream_console : public textout {
private: private:
#if defined(UNICODE)
std::wostream & s; std::wostream & s;
#else
std::ostream & s;
#endif
HANDLE handle; HANDLE handle;
bool console; bool console;
public: public:
#if defined(UNICODE)
textout_ostream_console( std::wostream & s_, DWORD stdHandle_ ) textout_ostream_console( std::wostream & s_, DWORD stdHandle_ )
#else
textout_ostream_console( std::ostream & s_, DWORD stdHandle_ )
#endif
: s(s_) : s(s_)
, handle(GetStdHandle( stdHandle_ )) , handle(GetStdHandle( stdHandle_ ))
, console(IsConsole( stdHandle_ )) , console(IsConsole( stdHandle_ ))

View file

@ -1698,6 +1698,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
if (usUnityNote > 0x7F) usUnityNote = 60; if (usUnityNote > 0x7F) usUnityNote = 60;
int steps = (60 + transpose - usUnityNote) * 128 + sFineTune; int steps = (60 + transpose - usUnityNote) * 128 + sFineTune;
sample.Transpose(steps * (1.0 / (12.0 * 128.0))); sample.Transpose(steps * (1.0 / (12.0 * 128.0)));
sample.RelativeTone = 0;
Limit(lVolume, 16, 256); Limit(lVolume, 16, 256);
sample.nGlobalVol = (uint8)(lVolume / 4); // 0-64 sample.nGlobalVol = (uint8)(lVolume / 4); // 0-64

View file

@ -903,12 +903,9 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
// Text // Text
// Read composer name // Read composer name
uint8 composerLength = file.ReadUint8(); if(std::string composer; file.ReadSizedString<uint8le, mpt::String::spacePadded>(composer))
if(composerLength)
{ {
std::string str; m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, composer);
file.ReadString<mpt::String::spacePadded>(str, composerLength);
m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, str);
} }
// Channel names // Channel names
@ -926,11 +923,13 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
} }
if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader))) if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader)))
{ {
const size_t textLength = descriptionHeader.packedLen - sizeof(descriptionHeader); const uint32 textLength = descriptionHeader.packedLen - static_cast<uint32>(sizeof(descriptionHeader));
std::vector<uint8> textIn; std::vector<uint8> textIn;
file.ReadVector(textIn, textLength); file.ReadVector(textIn, textLength);
// In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85
const uint32 maxLength = std::min(textLength, Util::MaxValueOfType(textLength) / 85u) * 85u;
std::string textOut; std::string textOut;
textOut.reserve(descriptionHeader.unpackedLen); textOut.reserve(std::min(maxLength, descriptionHeader.unpackedLen.get()));
size_t readLen = 0; size_t readLen = 0;
while(readLen < textLength) while(readLen < textLength)

View file

@ -630,10 +630,19 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion,
switch(effect2) switch(effect2)
{ {
case 1: // Note Finetune case 1: // Note Finetune (1/16th of a semitone signed 8-bit value, not 1/128th as the interface claims)
effect2 = static_cast<ModCommand::COMMAND>(effectParam2 < 128 ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); effect2 = (effectParam2 < 128) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN;
if(effectParam2 > 128) effectParam2 = 255 - effectParam2 + 1; if(effectParam2 >= 128)
effectParam2 = 0xF0 | std::min(uint8(0x0F), effectParam2); // Well, this is not too accurate... effectParam2 = ~effectParam2 + 1;
if(effectParam2 >= 16 && m->IsNote())
{
if(effect2 == CMD_PORTAMENTOUP)
m->note = static_cast<ModCommand::NOTE>(std::min(m->note + effectParam2 / 16, static_cast<int>(NOTE_MAX)));
else
m->note = static_cast<ModCommand::NOTE>(std::max(m->note - effectParam2 / 16, static_cast<int>(NOTE_MIN)));
effectParam2 %= 16u;
}
effectParam2 = 0xF0 | std::min(uint8(0x0F), effectParam2);
break; break;
case 2: // Note Delay (wtf is the difference to Sample Delay?) case 2: // Note Delay (wtf is the difference to Sample Delay?)
effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks);
@ -654,7 +663,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion,
case 4: // Portamento Up case 4: // Portamento Up
case 5: // Portamento Down case 5: // Portamento Down
effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, true); effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, true);
effect2 = static_cast<ModCommand::COMMAND>(effect2 == 4 ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); effect2 = (effect2 == 4) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN;
useMem2 = true; useMem2 = true;
break; break;
case 6: // Portamento to Note case 6: // Portamento to Note
@ -811,6 +820,12 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion,
// Prefer instrument effects over any other effects // Prefer instrument effects over any other effects
if(effect1 != CMD_NONE) if(effect1 != CMD_NONE)
{ {
ModCommand::TwoRegularCommandsToMPT(effect3, effectParam3, effect1, effectParam1);
if(m->volcmd == VOLCMD_NONE && effect3 != VOLCMD_NONE)
{
m->volcmd = effect3;
m->vol = effectParam3;
}
m->command = effect1; m->command = effect1;
m->param = effectParam1; m->param = effectParam1;
} else if(effect3 != CMD_NONE) } else if(effect3 != CMD_NONE)

View file

@ -1160,6 +1160,10 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
{ {
m_SongFlags.set(SONG_ISAMIGA); m_SongFlags.set(SONG_ISAMIGA);
} }
if(isGenericMultiChannel || isMdKd)
{
m_playBehaviour.set(kFT2MODTremoloRampWaveform);
}
if(isInconexia) if(isInconexia)
{ {
m_playBehaviour.set(kMODIgnorePanning); m_playBehaviour.set(kMODIgnorePanning);

View file

@ -84,6 +84,7 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 &param, bool toIT, bool co
case CMD_TONEPORTAVOL: command = 'L'; break; case CMD_TONEPORTAVOL: command = 'L'; break;
case CMD_CHANNELVOLUME: command = 'M'; break; case CMD_CHANNELVOLUME: command = 'M'; break;
case CMD_CHANNELVOLSLIDE: command = 'N'; break; case CMD_CHANNELVOLSLIDE: command = 'N'; break;
case CMD_OFFSETPERCENTAGE:
case CMD_OFFSET: command = 'O'; break; case CMD_OFFSET: command = 'O'; break;
case CMD_PANNINGSLIDE: command = 'P'; break; case CMD_PANNINGSLIDE: command = 'P'; break;
case CMD_RETRIG: command = 'Q'; break; case CMD_RETRIG: command = 'Q'; break;
@ -465,7 +466,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
continue; continue;
} }
sampleHeader.ConvertToMPT(Samples[smp + 1]); sampleHeader.ConvertToMPT(Samples[smp + 1], isST3);
m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
@ -527,7 +528,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
} }
CHANNELINDEX channel = (info & s3mChannelMask); CHANNELINDEX channel = (info & s3mChannelMask);
ModCommand dummy = ModCommand::Empty(); ModCommand dummy;
ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy; ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy;
if(info & s3mNotePresent) if(info & s3mNotePresent)
@ -577,12 +578,9 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
} else } else
{ {
if(m.param < 0x08) if(m.param < 0x08)
{
zxxCountLeft++; zxxCountLeft++;
} else if(m.param > 0x08) else if(m.param > 0x08)
{
zxxCountRight++; zxxCountRight++;
}
} }
} }
} }

View file

@ -64,11 +64,13 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags)
WAVReader wavFile(file); WAVReader wavFile(file);
if(!wavFile.IsValid() if(!wavFile.IsValid()
|| wavFile.GetNumChannels() == 0 || wavFile.GetNumChannels() == 0
|| wavFile.GetNumChannels() > MAX_BASECHANNELS || wavFile.GetNumChannels() > MAX_BASECHANNELS
|| wavFile.GetBitsPerSample() == 0 || wavFile.GetNumChannels() >= MAX_SAMPLES
|| wavFile.GetBitsPerSample() > 32 || wavFile.GetBitsPerSample() == 0
|| (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat)) || wavFile.GetBitsPerSample() > 64
|| (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
|| (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat))
{ {
return false; return false;
} else if(loadFlags == onlyVerifyHeader) } else if(loadFlags == onlyVerifyHeader)
@ -173,22 +175,22 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags)
if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat) if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
{ {
CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32> > >(sample, sampleChunk, channel, wavFile.GetNumChannels()); if(wavFile.GetBitsPerSample() <= 32)
CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
else
CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float64>, SC::DecodeFloat64<littleEndian64>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
} else } else
{ {
if(wavFile.GetBitsPerSample() <= 8) if(wavFile.GetBitsPerSample() <= 8)
{
CopyWavChannel<SC::DecodeUint8>(sample, sampleChunk, channel, wavFile.GetNumChannels()); CopyWavChannel<SC::DecodeUint8>(sample, sampleChunk, channel, wavFile.GetNumChannels());
} else if(wavFile.GetBitsPerSample() <= 16) else if(wavFile.GetBitsPerSample() <= 16)
{ CopyWavChannel<SC::DecodeInt16<0, littleEndian16>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
CopyWavChannel<SC::DecodeInt16<0, littleEndian16> >(sample, sampleChunk, channel, wavFile.GetNumChannels()); else if(wavFile.GetBitsPerSample() <= 24)
} else if(wavFile.GetBitsPerSample() <= 24) CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
{ else if(wavFile.GetBitsPerSample() <= 32)
CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24> > >(sample, sampleChunk, channel, wavFile.GetNumChannels()); CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
} else if(wavFile.GetBitsPerSample() <= 32) else if(wavFile.GetBitsPerSample() <= 64)
{ CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int64>, SC::DecodeInt64<0, littleEndian64>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32> > >(sample, sampleChunk, channel, wavFile.GetNumChannels());
}
} }
sample.PrecomputeLoops(*this, false); sample.PrecomputeLoops(*this, false);

View file

@ -114,12 +114,12 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_
template <typename SampleConversion, typename Tbyte> template <typename SampleConversion, typename Tbyte>
size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, typename SampleConversion::peak_t *srcPeak = nullptr, SampleConversion conv = SampleConversion()) size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourceSize, typename SampleConversion::peak_t *srcPeak = nullptr, SampleConversion conv = SampleConversion())
{ {
const size_t inSize = sizeof(typename SampleConversion::input_t); const size_t sampleSize = SampleConversion::input_inc;
MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t));
size_t numSamples = sample.nLength * sample.GetNumChannels(); size_t numSamples = sample.nLength * sample.GetNumChannels();
LimitMax(numSamples, sourceSize / inSize); LimitMax(numSamples, sourceSize / sampleSize);
const std::byte * inBuf = mpt::byte_cast<const std::byte*>(sourceBuffer); const std::byte * inBuf = mpt::byte_cast<const std::byte*>(sourceBuffer);
// Finding max value // Finding max value
@ -150,7 +150,7 @@ size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size
*srcPeak = sampleConv.GetSrcPeak(); *srcPeak = sampleConv.GetSrcPeak();
} }
return numSamples * inSize; return numSamples * sampleSize;
} }

View file

@ -180,18 +180,19 @@ void ModSequence::assign(ORDERINDEX newSize, PATTERNINDEX pat)
ORDERINDEX ModSequence::insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill) ORDERINDEX ModSequence::insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill)
{ {
if (pos >= m_sndFile.GetModSpecifications().ordersMax || count == 0) const auto ordersMax = m_sndFile.GetModSpecifications().ordersMax;
if(pos >= ordersMax || GetLengthTailTrimmed() >= ordersMax || count == 0)
return 0; return 0;
// Limit number of orders to be inserted so that we don't exceed the format limit. // Limit number of orders to be inserted so that we don't exceed the format limit.
LimitMax(count, ORDERINDEX(m_sndFile.GetModSpecifications().ordersMax - pos)); LimitMax(count, static_cast<ORDERINDEX>(ordersMax - pos));
reserve(pos + count); reserve(std::max(pos, GetLength()) + count);
// Inserting past the end of the container? // Inserting past the end of the container?
if(pos > size()) if(pos > size())
resize(pos); resize(pos);
std::vector<PATTERNINDEX>::insert(begin() + pos, count, fill); std::vector<PATTERNINDEX>::insert(begin() + pos, count, fill);
// Did we overgrow? Remove patterns at end. // Did we overgrow? Remove patterns at end.
if(size() > m_sndFile.GetModSpecifications().ordersMax) if(size() > ordersMax)
resize(m_sndFile.GetModSpecifications().ordersMax); resize(ordersMax);
return count; return count;
} }

View file

@ -17,7 +17,7 @@
OPENMPT_NAMESPACE_BEGIN OPENMPT_NAMESPACE_BEGIN
// Convert an S3M sample header to OpenMPT's internal sample header. // Convert an S3M sample header to OpenMPT's internal sample header.
void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp, bool isST3) const
{ {
mptSmp.Initialize(MOD_TYPE_S3M); mptSmp.Initialize(MOD_TYPE_S3M);
mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
@ -48,17 +48,18 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const
} }
// Volume / Panning // Volume / Panning
mptSmp.nVolume = std::min(static_cast<uint8>(defaultVolume), uint8(64)) * 4; mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4;
// C-5 frequency // C-5 frequency
mptSmp.nC5Speed = c5speed; mptSmp.nC5Speed = c5speed;
// ST3 ignores the high 16 bits
if(isST3)
mptSmp.nC5Speed &= 0xFFFF;
if(mptSmp.nC5Speed == 0) if(mptSmp.nC5Speed == 0)
{
mptSmp.nC5Speed = 8363; mptSmp.nC5Speed = 8363;
} else if(mptSmp.nC5Speed < 1024) else if(mptSmp.nC5Speed < 1024)
{
mptSmp.nC5Speed = 1024; mptSmp.nC5Speed = 1024;
}
} }

View file

@ -134,7 +134,7 @@ struct S3MSampleHeader
char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments)
// Convert an S3M sample header to OpenMPT's internal sample header. // Convert an S3M sample header to OpenMPT's internal sample header.
void ConvertToMPT(ModSample &mptSmp) const; void ConvertToMPT(ModSample &mptSmp, bool isST3 = false) const;
// Convert OpenMPT's internal sample header to an S3M sample header. // Convert OpenMPT's internal sample header to an S3M sample header.
SmpLength ConvertToS3M(const ModSample &mptSmp); SmpLength ConvertToS3M(const ModSample &mptSmp);
// Retrieve the internal sample format flags for this sample. // Retrieve the internal sample format flags for this sample.

View file

@ -20,6 +20,7 @@
#include "../common/mptFileIO.h" #include "../common/mptFileIO.h"
#endif // !MODPLUG_NO_FILESAVE #endif // !MODPLUG_NO_FILESAVE
#include "../common/misc_util.h" #include "../common/misc_util.h"
#include "../common/Endianness.h"
#include "Tagging.h" #include "Tagging.h"
#include "ITTools.h" #include "ITTools.h"
#include "XMTools.h" #include "XMTools.h"
@ -421,11 +422,12 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo
WAVReader wavFile(file); WAVReader wavFile(file);
if(!wavFile.IsValid() if(!wavFile.IsValid()
|| wavFile.GetNumChannels() == 0 || wavFile.GetNumChannels() == 0
|| wavFile.GetNumChannels() > 2 || wavFile.GetNumChannels() > 2
|| (wavFile.GetBitsPerSample() == 0 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3) || (wavFile.GetBitsPerSample() == 0 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3)
|| (wavFile.GetBitsPerSample() > 64) || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
|| (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat && wavFile.GetSampleFormat() != WAVFormatChunk::fmtIMA_ADPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtALaw && wavFile.GetSampleFormat() != WAVFormatChunk::fmtULaw)) || (wavFile.GetBitsPerSample() > 64)
|| (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat && wavFile.GetSampleFormat() != WAVFormatChunk::fmtIMA_ADPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtALaw && wavFile.GetSampleFormat() != WAVFormatChunk::fmtULaw))
{ {
return false; return false;
} }
@ -2486,6 +2488,7 @@ struct IFFChunk
idVHDR = MagicBE("VHDR"), idVHDR = MagicBE("VHDR"),
idBODY = MagicBE("BODY"), idBODY = MagicBE("BODY"),
idNAME = MagicBE("NAME"), idNAME = MagicBE("NAME"),
idCHAN = MagicBE("CHAN"),
}; };
uint32be id; // See ChunkIdentifiers uint32be id; // See ChunkIdentifiers
@ -2538,6 +2541,7 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file)
FileReader vhdrChunk = chunks.GetChunk(IFFChunk::idVHDR); FileReader vhdrChunk = chunks.GetChunk(IFFChunk::idVHDR);
FileReader bodyChunk = chunks.GetChunk(IFFChunk::idBODY); FileReader bodyChunk = chunks.GetChunk(IFFChunk::idBODY);
FileReader chanChunk = chunks.GetChunk(IFFChunk::idCHAN);
IFFSampleHeader sampleHeader; IFFSampleHeader sampleHeader;
if(!bodyChunk.IsValid() if(!bodyChunk.IsValid()
|| !vhdrChunk.IsValid() || !vhdrChunk.IsValid()
@ -2549,6 +2553,7 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file)
DestroySampleThreadsafe(nSample); DestroySampleThreadsafe(nSample);
// Default values // Default values
const uint8 bytesPerSample = memcmp(fileHeader.magic, "8SVX", 4) ? 2 : 1; const uint8 bytesPerSample = memcmp(fileHeader.magic, "8SVX", 4) ? 2 : 1;
const uint8 channels = chanChunk.ReadUint32BE() == 6 ? 2 : 1;
ModSample &sample = Samples[nSample]; ModSample &sample = Samples[nSample];
sample.Initialize(); sample.Initialize();
sample.nLoopStart = sampleHeader.oneShotHiSamples / bytesPerSample; sample.nLoopStart = sampleHeader.oneShotHiSamples / bytesPerSample;
@ -2569,13 +2574,13 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file)
m_szNames[nSample] = ""; m_szNames[nSample] = "";
} }
sample.nLength = mpt::saturate_cast<SmpLength>(bodyChunk.GetLength() / bytesPerSample); sample.nLength = mpt::saturate_cast<SmpLength>(bodyChunk.GetLength() / (bytesPerSample * channels));
if((sample.nLoopStart + 4 < sample.nLoopEnd) && (sample.nLoopEnd <= sample.nLength)) sample.uFlags.set(CHN_LOOP); if((sample.nLoopStart + 4 < sample.nLoopEnd) && (sample.nLoopEnd <= sample.nLength)) sample.uFlags.set(CHN_LOOP);
// While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data. // While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data.
SampleIO( SampleIO(
(bytesPerSample == 2) ? SampleIO::_16bit : SampleIO::_8bit, (bytesPerSample == 2) ? SampleIO::_16bit : SampleIO::_8bit,
SampleIO::mono, (channels == 2) ? SampleIO::stereoSplit : SampleIO::mono,
SampleIO::littleEndian, SampleIO::littleEndian,
SampleIO::signedPCM) SampleIO::signedPCM)
.ReadSample(sample, bodyChunk); .ReadSample(sample, bodyChunk);

View file

@ -327,7 +327,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
const auto &lut = GetEncoding() == uLaw ? uLawTable : aLawTable; const auto &lut = GetEncoding() == uLaw ? uLawTable : aLawTable;
SmpLength readLength = sample.nLength * GetNumChannels(); SmpLength readLength = sample.nLength * GetNumChannels();
LimitMax(readLength, mpt::saturate_cast<SmpLength>(file.BytesLeft())); LimitMax(readLength, mpt::saturate_cast<SmpLength>(fileSize));
bytesRead = readLength; bytesRead = readLength;
const uint8 *inBuf = mpt::byte_cast<const uint8*>(sourceBuf); const uint8 *inBuf = mpt::byte_cast<const uint8*>(sourceBuf);

View file

@ -512,7 +512,7 @@ enum PlayBehaviour
kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off
kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings
kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row
kFT2TremoloRampWaveform, // FT2-compatible tremolo ramp down / triangle waveform kFT2MODTremoloRampWaveform, // FT2-/ProTracker-compatible tremolo ramp down / triangle waveform
kFT2PortaUpDownMemory, // Portamento up and down have separate memory kFT2PortaUpDownMemory, // Portamento up and down have separate memory
kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays

View file

@ -154,6 +154,9 @@ public:
int vol = 0; int vol = 0;
sndFile.ProcessInstrumentFade(chn, vol); sndFile.ProcessInstrumentFade(chn, vol);
if(chn.dwFlags[CHN_ADLIB])
continue;
if(updateInc || chnSettings[channel].incChanged) if(updateInc || chnSettings[channel].incChanged)
{ {
if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick) if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick)
@ -568,11 +571,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
{ {
if(Samples[smp].uFlags[CHN_PANNING]) if(Samples[smp].uFlags[CHN_PANNING])
chn.SetInstrumentPan(Samples[smp].nPan, *this); chn.SetInstrumentPan(Samples[smp].nPan, *this);
if(Samples[smp].uFlags[CHN_ADLIB])
{
memory.state->Chn[nChn].Stop();
memory.chnSettings[nChn].ticksToRender = 0;
}
} }
} }
@ -1301,18 +1299,25 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
m_PlayState.m_nFrameDelay = m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nFrameDelay = m_PlayState.m_nPatternDelay = 0;
m_PlayState.m_nTickCount = Util::MaxValueOfType(m_PlayState.m_nTickCount) - 1; m_PlayState.m_nTickCount = Util::MaxValueOfType(m_PlayState.m_nTickCount) - 1;
m_PlayState.m_bPositionChanged = true; m_PlayState.m_bPositionChanged = true;
if(m_opl != nullptr)
m_opl->Reset();
for(CHANNELINDEX n = 0; n < GetNumChannels(); n++) for(CHANNELINDEX n = 0; n < GetNumChannels(); n++)
{ {
if(m_PlayState.Chn[n].nLastNote != NOTE_NONE) auto &chn = m_PlayState.Chn[n];
if(chn.nLastNote != NOTE_NONE)
{ {
m_PlayState.Chn[n].nNewNote = m_PlayState.Chn[n].nLastNote; chn.nNewNote = chn.nLastNote;
} }
if(memory.chnSettings[n].vol != 0xFF && !adjustSamplePos) if(memory.chnSettings[n].vol != 0xFF && !adjustSamplePos)
{ {
m_PlayState.Chn[n].nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4; chn.nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4;
}
if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl)
{
m_opl->Patch(n, chn.pModSample->adlib);
m_opl->NoteCut(n);
} }
} }
if(m_opl != nullptr) m_opl->Reset();
#ifndef NO_PLUGINS #ifndef NO_PLUGINS
// If there were any PC events, update plugin parameters to their latest value. // If there were any PC events, update plugin parameters to their latest value.
@ -1694,7 +1699,7 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo
chn.nFineTune = pSmp->nFineTune; chn.nFineTune = pSmp->nFineTune;
} }
chn.nTranspose = pSmp->RelativeTone; chn.nTranspose = UseFinetuneAndTranspose() ? pSmp->RelativeTone : 0;
// FT2 compatibility: Don't reset portamento target with new instrument numbers. // FT2 compatibility: Don't reset portamento target with new instrument numbers.
// Test case: Porta-Pickup.xm // Test case: Porta-Pickup.xm
@ -4984,6 +4989,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *
// MIDI channel // MIDI channel
isNibble = true; isNibble = true;
data = 0xFF; data = 0xFF;
#ifndef NO_PLUGINS
const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
if(plug > 0 && plug <= MAX_MIXPLUGINS) if(plug > 0 && plug <= MAX_MIXPLUGINS)
{ {
@ -4991,6 +4997,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *
if(midiPlug) if(midiPlug)
data = midiPlug->GetMidiChannel(nChn); data = midiPlug->GetMidiChannel(nChn);
} }
#endif // NO_PLUGINS
if(data == 0xFF) if(data == 0xFF)
{ {
// Fallback if no plugin was found // Fallback if no plugin was found

View file

@ -537,9 +537,14 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
m_nInstruments = maxInstr; m_nInstruments = maxInstr;
// Set default play state values // Set default play state values
if (!m_nDefaultTempo.GetInt()) m_nDefaultTempo.Set(125); if(!m_nDefaultTempo.GetInt())
if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; m_nDefaultTempo.Set(125);
if (m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; else
LimitMax(m_nDefaultTempo, TEMPO(uint16_max, 0));
if(!m_nDefaultSpeed)
m_nDefaultSpeed = 6;
if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat)
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat;
m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nMusicTempo = m_nDefaultTempo;
m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
@ -1085,7 +1090,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kFT2FinetunePrecision); playBehaviour.set(kFT2FinetunePrecision);
playBehaviour.set(kFT2NoteOffFlags); playBehaviour.set(kFT2NoteOffFlags);
playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kRowDelayWithNoteDelay);
playBehaviour.set(kFT2TremoloRampWaveform); playBehaviour.set(kFT2MODTremoloRampWaveform);
playBehaviour.set(kFT2PortaUpDownMemory); playBehaviour.set(kFT2PortaUpDownMemory);
playBehaviour.set(kFT2PanSustainRelease); playBehaviour.set(kFT2PanSustainRelease);
playBehaviour.set(kFT2NoteDelayWithoutInstr); playBehaviour.set(kFT2NoteDelayWithoutInstr);
@ -1116,6 +1121,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kMODOutOfRangeNoteDelay); playBehaviour.set(kMODOutOfRangeNoteDelay);
playBehaviour.set(kMODTempoOnSecondTick); playBehaviour.set(kMODTempoOnSecondTick);
playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kRowDelayWithNoteDelay);
playBehaviour.set(kFT2MODTremoloRampWaveform);
break; break;
default: default:

View file

@ -889,7 +889,7 @@ void CSoundFile::ProcessTremolo(ModChannel &chn, int &vol) const
const uint8 attenuation = ((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) || m_playBehaviour[kITVibratoTremoloPanbrello]) ? 5 : 6; const uint8 attenuation = ((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) || m_playBehaviour[kITVibratoTremoloPanbrello]) ? 5 : 6;
int delta = GetVibratoDelta(chn.nTremoloType, chn.nTremoloPos); int delta = GetVibratoDelta(chn.nTremoloType, chn.nTremoloPos);
if((chn.nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2TremoloRampWaveform]) if((chn.nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2MODTremoloRampWaveform])
{ {
// FT2 compatibility: Tremolo ramp down / triangle implementation is weird and affected by vibrato position (copypaste bug) // FT2 compatibility: Tremolo ramp down / triangle implementation is weird and affected by vibrato position (copypaste bug)
// Test case: TremoloWaveforms.xm, TremoloVibrato.xm // Test case: TremoloWaveforms.xm, TremoloVibrato.xm
@ -2295,7 +2295,10 @@ bool CSoundFile::ReadNote()
// In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440.
// Hence, we have to translate our "sample rate" into pitch. // Hence, we have to translate our "sample rate" into pitch.
const auto freq = hasTuning ? chn.nPeriod : GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); const auto freq = hasTuning ? chn.nPeriod : GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac);
const auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS);
#ifndef MODPLUG_TRACKER
milliHertz = Util::muldivr_unsigned(milliHertz, m_nFreqFactor, 65536);
#endif // !MODPLUG_TRACKER
const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0); const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0);
m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]);
} }

View file

@ -565,7 +565,7 @@ void CSoundFile::UpgradeModule()
{ {
{ kFT2NoteOffFlags, MPT_V("1.27.00.27") }, { kFT2NoteOffFlags, MPT_V("1.27.00.27") },
{ kRowDelayWithNoteDelay, MPT_V("1.27.00.37") }, { kRowDelayWithNoteDelay, MPT_V("1.27.00.37") },
{ kFT2TremoloRampWaveform, MPT_V("1.27.00.37") }, { kFT2MODTremoloRampWaveform, MPT_V("1.27.00.37") },
{ kFT2PortaUpDownMemory, MPT_V("1.27.00.37") }, { kFT2PortaUpDownMemory, MPT_V("1.27.00.37") },
{ kFT2PanSustainRelease, MPT_V("1.28.00.09") }, { kFT2PanSustainRelease, MPT_V("1.28.00.09") },
{ kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") }, { kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") },

View file

@ -187,10 +187,9 @@ static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length)
template <class T> template <class T>
static void ConvertStereoToMonoOneChannelImpl(T *pDest, const SmpLength length) static void ConvertStereoToMonoOneChannelImpl(T *pDest, const T *pSource, const SmpLength length)
{ {
const T *pEnd = pDest + length; for(const T *pEnd = pDest + length; pDest != pEnd; pDest++, pSource += 2)
for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2)
{ {
*pDest = *pSource; *pDest = *pSource;
} }
@ -218,9 +217,9 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers
conversionMode = onlyLeft; conversionMode = onlyLeft;
} }
if(smp.GetElementarySampleSize() == 2) if(smp.GetElementarySampleSize() == 2)
ConvertStereoToMonoOneChannelImpl(smp.sample16() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); ConvertStereoToMonoOneChannelImpl(smp.sample16(), smp.sample16() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
else if(smp.GetElementarySampleSize() == 1) else if(smp.GetElementarySampleSize() == 1)
ConvertStereoToMonoOneChannelImpl(smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); ConvertStereoToMonoOneChannelImpl(smp.sample8(), smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
else else
return false; return false;
} }
@ -241,7 +240,70 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers
template <class T> template <class T>
static void ConvertMonoToStereoImpl(const T * MPT_RESTRICT src, T * MPT_RESTRICT dst, SmpLength length) static void SplitStereoImpl(void *destL, void *destR, const T *source, SmpLength length)
{
T *l = static_cast<T *>(destL), *r = static_cast<T*>(destR);
while(length--)
{
*(l++) = source[0];
*(r++) = source[1];
source += 2;
}
}
// Converts a stereo sample into two mono samples. Source sample will not be deleted.
bool SplitStereo(const ModSample &source, ModSample &left, ModSample &right, CSoundFile &sndFile)
{
if(!source.HasSampleData() || source.GetNumChannels() != 2 || &left == &right)
return false;
const bool sourceIsLeft = &left == &source, sourceIsRight = &right == &source;
if(left.HasSampleData() && !sourceIsLeft)
return false;
if(right.HasSampleData() && !sourceIsRight)
return false;
void *leftData = sourceIsLeft ? left.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize());
void *rightData = sourceIsRight ? right.samplev() : ModSample::AllocateSample(source.nLength, source.GetElementarySampleSize());
if(!leftData || !rightData)
{
if(!sourceIsLeft)
ModSample::FreeSample(leftData);
if(!sourceIsRight)
ModSample::FreeSample(rightData);
return false;
}
if(source.GetElementarySampleSize() == 2)
SplitStereoImpl(leftData, rightData, source.sample16(), source.nLength);
else if(source.GetElementarySampleSize() == 1)
SplitStereoImpl(leftData, rightData, source.sample8(), source.nLength);
else
MPT_ASSERT_NOTREACHED();
CriticalSection cs;
left = source;
left.uFlags.reset(CHN_STEREO);
left.pData.pSample = leftData;
right = source;
right.uFlags.reset(CHN_STEREO);
right.pData.pSample = rightData;
for(auto &chn : sndFile.m_PlayState.Chn)
{
if(chn.pModSample == &left || chn.pModSample == &right)
chn.dwFlags.reset(CHN_STEREO);
}
left.PrecomputeLoops(sndFile, false);
right.PrecomputeLoops(sndFile, false);
return true;
}
template <class T>
static void ConvertMonoToStereoImpl(const T *MPT_RESTRICT src, T *MPT_RESTRICT dst, SmpLength length)
{ {
while(length--) while(length--)
{ {

View file

@ -46,6 +46,10 @@ enum StereoToMonoMode
// Convert a sample with any number of channels to mono // Convert a sample with any number of channels to mono
bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode conversionMode); bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode conversionMode);
// Converts a stereo sample into two mono samples. Source sample will not be deleted.
// Either of the two target samples may be identical to the source sample.
bool SplitStereo(const ModSample &source, ModSample &left, ModSample &right, CSoundFile &sndFile);
// Convert a mono sample to stereo // Convert a mono sample to stereo
bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile); bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile);

View file

@ -276,7 +276,7 @@ void LFOPlugin::HardAllNotesOff()
} }
bool LFOPlugin::IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) bool LFOPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn)
{ {
if(IMixPlugin *plugin = GetOutputPlugin()) if(IMixPlugin *plugin = GetOutputPlugin())
return plugin->IsNotePlaying(note, trackerChn); return plugin->IsNotePlaying(note, trackerChn);

View file

@ -91,7 +91,7 @@ public:
void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) override; void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) override;
void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override;
void HardAllNotesOff() override; void HardAllNotesOff() override;
bool IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) override; bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override;
int32 GetNumPrograms() const override { return 0; } int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; } int32 GetCurrentProgram() override { return 0; }

View file

@ -947,8 +947,11 @@ void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vo
} }
bool IMidiPlugin::IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) bool IMidiPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn)
{ {
if(!ModCommand::IsNote(note) || trackerChn >= std::size(m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note]))
return false;
note -= NOTE_MIN; note -= NOTE_MIN;
return (m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note][trackerChn] != 0); return (m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note][trackerChn] != 0);
} }

View file

@ -147,7 +147,7 @@ public:
virtual void MidiVibrato(int32 /*depth*/, int8 /*pwd*/, CHANNELINDEX /*trackerChn*/) { } virtual void MidiVibrato(int32 /*depth*/, int8 /*pwd*/, CHANNELINDEX /*trackerChn*/) { }
virtual void MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { } virtual void MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { }
virtual void HardAllNotesOff() { } virtual void HardAllNotesOff() { }
virtual bool IsNotePlaying(uint32 /*note*/, CHANNELINDEX /*trackerChn*/) { return false; } virtual bool IsNotePlaying(uint8 /*note*/, CHANNELINDEX /*trackerChn*/) { return false; }
// Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically. // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically.
virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff); virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff);
@ -264,7 +264,7 @@ public:
void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) override; void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) override;
void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) override; void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) override;
void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override;
bool IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) override; bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override;
// Get the MIDI channel currently associated with a given tracker channel // Get the MIDI channel currently associated with a given tracker channel
virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const; virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const;

View file

@ -234,12 +234,13 @@ mpt::ustring CTuning::GetNoteName(const NOTEINDEXTYPE &x, bool addOctave) const
void CTuning::SetNoteName(const NOTEINDEXTYPE &n, const mpt::ustring &str) void CTuning::SetNoteName(const NOTEINDEXTYPE &n, const mpt::ustring &str)
{ {
const NOTEINDEXTYPE pos = (GetGroupSize() < 1) ? n : static_cast<NOTEINDEXTYPE>(mpt::wrapping_modulo(n, m_GroupSize));
if(!str.empty()) if(!str.empty())
{ {
m_NoteNameMap[n] = str; m_NoteNameMap[pos] = str;
} else } else
{ {
const auto iter = m_NoteNameMap.find(n); const auto iter = m_NoteNameMap.find(pos);
if(iter != m_NoteNameMap.end()) if(iter != m_NoteNameMap.end())
{ {
m_NoteNameMap.erase(iter); m_NoteNameMap.erase(iter);