Update libopenmpt to version 0.5.6

CQTexperiment
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
All rights reserved.

View File

@ -170,36 +170,24 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`.
- 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:
# 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
or
# generates asm.js with a fixed size 128MB heap
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
# generates JavaScript with compatibility for older VMs
make CONFIG=emscripten EMSCRIPTEN_TARGET=js
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
might or might not work. Depending on how your distribution calls the
`node.js` binary, you might have to edit
`build/make/config-emscripten.mk`.
node.js. Depending on how your distribution calls the `node.js` binary,
you might have to edit `build/make/config-emscripten.mk`.
- DJGPP / DOS
@ -229,7 +217,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`.
- 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

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=13932
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.4
MPT_SVNDATE=2020-11-29T15:01:39.790705Z
MPT_SVNVERSION=14311
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6
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
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)
# emits native wasm.
CPPFLAGS += -DMPT_BUILD_WASM

View File

@ -1,10 +1,10 @@
#pragma once
#define OPENMPT_VERSION_SVNVERSION "13932"
#define OPENMPT_VERSION_REVISION 13932
#define OPENMPT_VERSION_SVNVERSION "14311"
#define OPENMPT_VERSION_REVISION 14311
#define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.4"
#define OPENMPT_VERSION_DATE "2020-11-29T15:01:39.790705Z"
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6"
#define OPENMPT_VERSION_DATE "2021-03-14T15:27:41.476009Z"
#define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -309,6 +309,12 @@
#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
#define MPT_COMPILER_QUIRK_NO_WCHAR
#endif

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ Modifications:
fails to compile.
* Macro redefinition of alloca with mingw-w64 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.
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
f->comment_list_length = get32_packet(f);
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);
#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) {
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 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 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.
\retval 0 The end of song has been reached.
\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 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 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.
\retval 0 The end of song has been reached.
\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
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)
* 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 ) {
std::ostringstream about;
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 << 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 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 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.
* \retval 0 The end of song has been reached.
* \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 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 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.
* \retval 0 The end of song has been reached.
* \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 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.
\retval 0 The end of song has been reached.
\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 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.
\retval 0 The end of song has been reached.
\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.
\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
\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 );
//! 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 );
std::memset( interface, 0, interface_size );
int result = 0;
if ( !strcmp( interface_id, "" ) ) {
if ( !std::strcmp( interface_id, "" ) ) {
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 );
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;
@ -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 );
i->set_current_speed = &set_current_speed;
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.")
#endif // MINGW
#if MPT_CLANG_AT_LEAST(5,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>.")
#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..10 on powerpc (32bit) when using -O3. See <https://bugs.llvm.org/show_bug.cgi?id=46683>.")
#endif
#endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS
@ -1106,9 +1106,9 @@ double module_impl::set_position_seconds( double seconds ) {
} else {
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();
m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
m_sndFile->SetCurrentOrder( t.lastOrder );
m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder;
m_sndFile->m_PlayState.m_nNextRow = t.lastRow;
m_currentPositionSeconds = base_seconds + t.duration;
return m_currentPositionSeconds;

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 5
/*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 4
#define OPENMPT_API_VERSION_PATCH 6
/*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */

View File

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=5
LIBOPENMPT_VERSION_PATCH=4
LIBOPENMPT_VERSION_PATCH=6
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=4
LIBOPENMPT_LTVER_REVISION=6
LIBOPENMPT_LTVER_AGE=2

View File

@ -465,7 +465,7 @@ static void clear_current_timeinfo() {
static void WINAPI openmpt_About( HWND win ) {
std::ostringstream about;
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
about << " Copyright (c) 2013-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 << std::endl;
about << openmpt::string::get( "contact" ) << std::endl;

View File

@ -8,7 +8,7 @@
*/
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"
"All rights reserved." "\n"
"" "\n"
@ -74,6 +74,7 @@ static const char * const license =
#include <conio.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.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 ) {
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 ) {
log << std::endl;
return;
@ -536,7 +537,7 @@ static void show_info( std::ostream & log, bool verbose ) {
static void show_man_version( textout & log ) {
log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl;
log << std::endl;
log << "Copyright (c) 2013-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 ) {
@ -2288,8 +2289,13 @@ static int main( int argc, char * argv [] ) {
#endif
textout_dummy dummy_log;
#if defined(WIN32)
#if defined(UNICODE)
textout_ostream_console std_out( std::wcout, STD_OUTPUT_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
textout_ostream std_out( std::cout );
textout_ostream std_err( std::clog );

View File

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

View File

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

View File

@ -903,12 +903,9 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
// Text
// Read composer name
uint8 composerLength = file.ReadUint8();
if(composerLength)
if(std::string composer; file.ReadSizedString<uint8le, mpt::String::spacePadded>(composer))
{
std::string str;
file.ReadString<mpt::String::spacePadded>(str, composerLength);
m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, str);
m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, composer);
}
// Channel names
@ -926,11 +923,13 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
}
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;
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;
textOut.reserve(descriptionHeader.unpackedLen);
textOut.reserve(std::min(maxLength, descriptionHeader.unpackedLen.get()));
size_t readLen = 0;
while(readLen < textLength)

View File

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

View File

@ -1160,6 +1160,10 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
{
m_SongFlags.set(SONG_ISAMIGA);
}
if(isGenericMultiChannel || isMdKd)
{
m_playBehaviour.set(kFT2MODTremoloRampWaveform);
}
if(isInconexia)
{
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_CHANNELVOLUME: command = 'M'; break;
case CMD_CHANNELVOLSLIDE: command = 'N'; break;
case CMD_OFFSETPERCENTAGE:
case CMD_OFFSET: command = 'O'; break;
case CMD_PANNINGSLIDE: command = 'P'; break;
case CMD_RETRIG: command = 'Q'; break;
@ -465,7 +466,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
continue;
}
sampleHeader.ConvertToMPT(Samples[smp + 1]);
sampleHeader.ConvertToMPT(Samples[smp + 1], isST3);
m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
@ -527,7 +528,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
}
CHANNELINDEX channel = (info & s3mChannelMask);
ModCommand dummy = ModCommand::Empty();
ModCommand dummy;
ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy;
if(info & s3mNotePresent)
@ -577,12 +578,9 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
} else
{
if(m.param < 0x08)
{
zxxCountLeft++;
} else if(m.param > 0x08)
{
else if(m.param > 0x08)
zxxCountRight++;
}
}
}
}

View File

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

View File

@ -114,12 +114,12 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_
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())
{
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));
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);
// Finding max value
@ -150,7 +150,7 @@ size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size
*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)
{
if (pos >= m_sndFile.GetModSpecifications().ordersMax || count == 0)
const auto ordersMax = m_sndFile.GetModSpecifications().ordersMax;
if(pos >= ordersMax || GetLengthTailTrimmed() >= ordersMax || count == 0)
return 0;
// Limit number of orders to be inserted so that we don't exceed the format limit.
LimitMax(count, ORDERINDEX(m_sndFile.GetModSpecifications().ordersMax - pos));
reserve(pos + count);
LimitMax(count, static_cast<ORDERINDEX>(ordersMax - pos));
reserve(std::max(pos, GetLength()) + count);
// Inserting past the end of the container?
if(pos > size())
resize(pos);
std::vector<PATTERNINDEX>::insert(begin() + pos, count, fill);
// Did we overgrow? Remove patterns at end.
if(size() > m_sndFile.GetModSpecifications().ordersMax)
resize(m_sndFile.GetModSpecifications().ordersMax);
if(size() > ordersMax)
resize(ordersMax);
return count;
}

View File

@ -17,7 +17,7 @@
OPENMPT_NAMESPACE_BEGIN
// 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.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
@ -48,17 +48,18 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const
}
// 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
mptSmp.nC5Speed = c5speed;
// ST3 ignores the high 16 bits
if(isST3)
mptSmp.nC5Speed &= 0xFFFF;
if(mptSmp.nC5Speed == 0)
{
mptSmp.nC5Speed = 8363;
} else if(mptSmp.nC5Speed < 1024)
{
else if(mptSmp.nC5Speed < 1024)
mptSmp.nC5Speed = 1024;
}
}

View File

@ -134,7 +134,7 @@ struct S3MSampleHeader
char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments)
// 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.
SmpLength ConvertToS3M(const ModSample &mptSmp);
// Retrieve the internal sample format flags for this sample.

View File

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

View File

@ -154,6 +154,9 @@ public:
int vol = 0;
sndFile.ProcessInstrumentFade(chn, vol);
if(chn.dwFlags[CHN_ADLIB])
continue;
if(updateInc || chnSettings[channel].incChanged)
{
if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick)
@ -568,11 +571,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
{
if(Samples[smp].uFlags[CHN_PANNING])
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_nTickCount = Util::MaxValueOfType(m_PlayState.m_nTickCount) - 1;
m_PlayState.m_bPositionChanged = true;
if(m_opl != nullptr)
m_opl->Reset();
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)
{
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
// 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.nTranspose = pSmp->RelativeTone;
chn.nTranspose = UseFinetuneAndTranspose() ? pSmp->RelativeTone : 0;
// FT2 compatibility: Don't reset portamento target with new instrument numbers.
// Test case: Porta-Pickup.xm
@ -4984,6 +4989,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *
// MIDI channel
isNibble = true;
data = 0xFF;
#ifndef NO_PLUGINS
const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
if(plug > 0 && plug <= MAX_MIXPLUGINS)
{
@ -4991,6 +4997,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *
if(midiPlug)
data = midiPlug->GetMidiChannel(nChn);
}
#endif // NO_PLUGINS
if(data == 0xFF)
{
// Fallback if no plugin was found

View File

@ -537,9 +537,14 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
m_nInstruments = maxInstr;
// Set default play state values
if (!m_nDefaultTempo.GetInt()) m_nDefaultTempo.Set(125);
if (!m_nDefaultSpeed) m_nDefaultSpeed = 6;
if (m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat;
if(!m_nDefaultTempo.GetInt())
m_nDefaultTempo.Set(125);
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_nMusicTempo = m_nDefaultTempo;
m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
@ -1085,7 +1090,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kFT2FinetunePrecision);
playBehaviour.set(kFT2NoteOffFlags);
playBehaviour.set(kRowDelayWithNoteDelay);
playBehaviour.set(kFT2TremoloRampWaveform);
playBehaviour.set(kFT2MODTremoloRampWaveform);
playBehaviour.set(kFT2PortaUpDownMemory);
playBehaviour.set(kFT2PanSustainRelease);
playBehaviour.set(kFT2NoteDelayWithoutInstr);
@ -1116,6 +1121,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kMODOutOfRangeNoteDelay);
playBehaviour.set(kMODTempoOnSecondTick);
playBehaviour.set(kRowDelayWithNoteDelay);
playBehaviour.set(kFT2MODTremoloRampWaveform);
break;
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;
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)
// 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.
// Hence, we have to translate our "sample rate" into pitch.
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);
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") },
{ 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") },
{ kFT2PanSustainRelease, MPT_V("1.28.00.09") },
{ kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") },

View File

@ -187,10 +187,9 @@ static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length)
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(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2)
for(const T *pEnd = pDest + length; pDest != pEnd; pDest++, pSource += 2)
{
*pDest = *pSource;
}
@ -218,9 +217,9 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers
conversionMode = onlyLeft;
}
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)
ConvertStereoToMonoOneChannelImpl(smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
ConvertStereoToMonoOneChannelImpl(smp.sample8(), smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength);
else
return false;
}
@ -241,7 +240,70 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers
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--)
{

View File

@ -46,6 +46,10 @@ enum StereoToMonoMode
// Convert a sample with any number of channels to mono
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
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())
return plugin->IsNotePlaying(note, trackerChn);

View File

@ -91,7 +91,7 @@ public:
void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) override;
void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) 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 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;
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 MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { }
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.
virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff);
@ -264,7 +264,7 @@ public:
void MidiPitchBend(int32 increment, 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;
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
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)
{
const NOTEINDEXTYPE pos = (GetGroupSize() < 1) ? n : static_cast<NOTEINDEXTYPE>(mpt::wrapping_modulo(n, m_GroupSize));
if(!str.empty())
{
m_NoteNameMap[n] = str;
m_NoteNameMap[pos] = str;
} else
{
const auto iter = m_NoteNameMap.find(n);
const auto iter = m_NoteNameMap.find(pos);
if(iter != m_NoteNameMap.end())
{
m_NoteNameMap.erase(iter);