libOpenMPT Legacy: Updated to version 0.5.17

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-03-14 04:35:36 -07:00
parent aa28b2beb1
commit 3815ba0baf
15 changed files with 150 additions and 109 deletions

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=16768
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.16
MPT_SVNDATE=2022-01-30T16:50:10.915999Z
MPT_SVNVERSION=17112
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.17
MPT_SVNDATE=2022-03-13T14:51:00.976461Z

View File

@ -1,10 +1,10 @@
#pragma once
#define OPENMPT_VERSION_SVNVERSION "16768"
#define OPENMPT_VERSION_REVISION 16768
#define OPENMPT_VERSION_SVNVERSION "17112"
#define OPENMPT_VERSION_REVISION 17112
#define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.16"
#define OPENMPT_VERSION_DATE "2022-01-30T16:50:10.915999Z"
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.17"
#define OPENMPT_VERSION_DATE "2022-03-13T14:51:00.976461Z"
#define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -18,6 +18,6 @@ OPENMPT_NAMESPACE_BEGIN
#define VER_MAJORMAJOR 1
#define VER_MAJOR 29
#define VER_MINOR 15
#define VER_MINORMINOR 04
#define VER_MINORMINOR 06
OPENMPT_NAMESPACE_END

View File

@ -5,6 +5,21 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
### libopenmpt 0.5.17 (2022-03-13)
* [**Sec**] Possible out-of-bounds write in malformed IT / XM / MPTM files
using the internal LFO plugin. (r17081)
* [**Sec**] Possible out-of-bounds read when using Amiga BLEP interpolation
with extremely high-pitched notes. (r17082)
* MO3: Pattern indices 254 / 255 were not treated as playable patterns even if
the original file was a MOD / XM.
* Correctly apply ST3-style effect memory when seeking in S3M files.
* Command S (S3M / IT style) effect memory was not applied when seeking.
* FLAC: Update to v1.3.4 (2022-02-21).
* pugixml: Update to v1.12.1 (2022-02-16).
### libopenmpt 0.5.16 (2022-01-30)
* [**Bug**] Possible hang with malformed DMF, DSM, MED and OKT files

View File

@ -127,12 +127,12 @@
/*! \defgroup libopenmpt_cpp libopenmpt C++ */
namespace openmpt {
/*! \addtogroup libopenmpt_cpp
@{
*/
namespace openmpt {
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4275)
@ -1163,10 +1163,10 @@ public:
}; // class module
} // namespace openmpt
/*!
@}
*/
} // namespace openmpt
#endif // LIBOPENMPT_HPP

View File

@ -22,6 +22,14 @@
#define LIBOPENMPT_CXX_API
#undef LIBOPENMPT_CXX_API
/*!
@}
*/
/*! \addtogroup libopenmpt_c
@{
*/
/*! \brief Defined if libopenmpt/libopenmpt_stream_callbacks_buffer.h exists. */
#define LIBOPENMPT_STREAM_CALLBACKS_BUFFER
@ -51,6 +59,14 @@
*/
#define LIBOPENMPT_STREAM_CALLBACKS_FILE
/*!
@}
*/
/*! \addtogroup libopenmpt
@{
*/
#if defined(__DOXYGEN__)
#define LIBOPENMPT_API_HELPER_EXPORT

View File

@ -26,12 +26,12 @@
/*! \defgroup libopenmpt_ext_cpp libopenmpt_ext C++ */
namespace openmpt {
/*! \addtogroup libopenmpt_ext_cpp
@{
*/
namespace openmpt {
class module_ext_impl;
class LIBOPENMPT_CXX_API module_ext : public module {
@ -77,8 +77,16 @@ public:
}; // class module_ext
/*!
@}
*/
namespace ext {
/*! \addtogroup libopenmpt_ext_cpp
@{
*/
#define LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE(name) \
static const char name ## _id [] = # name ; \
class name; \
@ -300,12 +308,12 @@ class interactive {
#undef LIBOPENMPT_DECLARE_EXT_CXX_INTERFACE
#undef LIBOPENMPT_EXT_CXX_INTERFACE
} // namespace ext
} // namespace openmpt
/*!
@}
*/
} // namespace ext
} // namespace openmpt
#endif // LIBOPENMPT_EXT_HPP

View File

@ -34,12 +34,20 @@
#include "soundlib/mod_specifications.h"
#include "soundlib/AudioReadTarget.h"
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#include <windows.h>
#endif // MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
OPENMPT_NAMESPACE_BEGIN
#if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS)
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if defined(_WIN32_WINNT)
#if defined(NTDDI_VERSION)
#if (NTDDI_VERSION < 0x06020000)
MPT_WARNING("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define NTDDI_VERSION 0x0602000.")
#endif
#elif defined(_WIN32_WINNT)
#if (_WIN32_WINNT < 0x0602)
MPT_WARNING("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.")
#endif // _WIN32_WINNT

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

View File

@ -41,9 +41,7 @@ typedef IntToFloatTraits<2, 2, mixsample_t, int16, -int16_min> Int16SToFloatS;
template<class Traits>
struct LinearInterpolation
{
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE LinearInterpolation(const ModChannel &, const CResampler &, unsigned int) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo)
{
@ -64,8 +62,7 @@ struct LinearInterpolation
template<class Traits>
struct FastSincInterpolation
{
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE FastSincInterpolation(const ModChannel &, const CResampler &, unsigned int) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo)
{
@ -89,14 +86,12 @@ struct PolyphaseInterpolation
{
const typename Traits::output_t *sinc;
MPT_FORCEINLINE void Start(const ModChannel &chn, const CResampler &resampler)
MPT_FORCEINLINE PolyphaseInterpolation(const ModChannel &chn, const CResampler &resampler, unsigned int)
{
sinc = (((chn.increment > SamplePosition(0x130000000ll)) || (chn.increment < -SamplePosition(-0x130000000ll))) ?
(((chn.increment > SamplePosition(0x180000000ll)) || (chn.increment < SamplePosition(-0x180000000ll))) ? resampler.gDownsample2x : resampler.gDownsample13x) : resampler.gKaiserSinc);
}
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo)
{
static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
@ -123,13 +118,11 @@ struct FIRFilterInterpolation
{
const typename Traits::output_t *WFIRlut;
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &resampler)
MPT_FORCEINLINE FIRFilterInterpolation(const ModChannel &, const CResampler &resampler, unsigned int)
{
WFIRlut = resampler.m_WindowedFIR.lut;
}
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo)
{
static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
@ -159,30 +152,30 @@ struct NoRamp
{
typename Traits::output_t lVol, rVol;
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE NoRamp(const ModChannel &chn)
{
lVol = static_cast<Traits::output_t>(chn.leftVol) * (1.0f / 4096.0f);
rVol = static_cast<Traits::output_t>(chn.rightVol) * (1.0f / 4096.0f);
}
MPT_FORCEINLINE void End(const ModChannel &) { }
};
struct Ramp
{
ModChannel &channel;
int32 lRamp, rRamp;
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE Ramp(ModChannel &chn)
: channel{chn}
{
lRamp = chn.rampLeftVol;
rRamp = chn.rampRightVol;
}
MPT_FORCEINLINE void End(ModChannel &chn)
MPT_FORCEINLINE ~Ramp()
{
chn.rampLeftVol = lRamp; chn.leftVol = lRamp >> VOLUMERAMPPRECISION;
chn.rampRightVol = rRamp; chn.rightVol = rRamp >> VOLUMERAMPPRECISION;
channel.rampLeftVol = lRamp; channel.leftVol = lRamp >> VOLUMERAMPPRECISION;
channel.rampRightVol = rRamp; channel.rightVol = rRamp >> VOLUMERAMPPRECISION;
}
};
@ -259,8 +252,7 @@ struct MixStereoRamp : public Ramp
template<class Traits>
struct NoFilter
{
MPT_FORCEINLINE void Start(const ModChannel &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE NoFilter(const ModChannel &) { }
MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { }
};
@ -270,10 +262,12 @@ struct NoFilter
template<class Traits>
struct ResonantFilter
{
ModChannel &channel;
// Filter history
typename Traits::output_t fy[Traits::numChannelsIn][2];
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE ResonantFilter(ModChannel &chn)
: channel{chn}
{
for(int i = 0; i < Traits::numChannelsIn; i++)
{
@ -282,12 +276,12 @@ struct ResonantFilter
}
}
MPT_FORCEINLINE void End(ModChannel &chn)
MPT_FORCEINLINE ~ResonantFilter(ModChannel &chn)
{
for(int i = 0; i < Traits::numChannelsIn; i++)
{
chn.nFilter_Y[i][0] = fy[i][0];
chn.nFilter_Y[i][1] = fy[i][1];
channel.nFilter_Y[i][0] = fy[i][0];
channel.nFilter_Y[i][1] = fy[i][1];
}
}

View File

@ -50,23 +50,34 @@ template<class Traits>
struct AmigaBlepInterpolation
{
SamplePosition subIncrement;
Paula::State *paula;
const Paula::BlepArray *WinSincIntegral;
int numSteps;
Paula::State &paula;
const Paula::BlepArray &WinSincIntegral;
const int numSteps;
unsigned int remainingSamples = 0;
MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &resampler)
MPT_FORCEINLINE AmigaBlepInterpolation(ModChannel &chn, const CResampler &resampler, unsigned int numSamples)
: paula{chn.paulaState}
, WinSincIntegral{resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER])}
, numSteps{chn.paulaState.numSteps}
{
paula = &chn.paulaState;
numSteps = paula->numSteps;
WinSincIntegral = &resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER]);
if(numSteps)
{
subIncrement = chn.increment / numSteps;
// May we read past the start or end of sample if we do partial sample increments?
// If that's the case, don't apply any sub increments on the source sample if we reached the last output sample
// Note that this should only happen with notes well outside the Amiga note range, e.g. in software-mixed formats like MED
const int32 targetPos = (chn.position + chn.increment * numSamples).GetInt();
if(static_cast<SmpLength>(targetPos) > chn.nLength)
remainingSamples = numSamples;
}
}
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo)
{
if(--remainingSamples == 0)
subIncrement = {};
SamplePosition pos(0, posLo);
// First, process steps of full length (one Amiga clock interval)
for(int step = numSteps; step > 0; step--)
@ -75,26 +86,26 @@ struct AmigaBlepInterpolation
int32 posInt = pos.GetInt() * Traits::numChannelsIn;
for(int32 i = 0; i < Traits::numChannelsIn; i++)
inSample += Traits::Convert(inBuffer[posInt + i]);
paula->InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn)));
paula->Clock(Paula::MINIMUM_INTERVAL);
paula.InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn)));
paula.Clock(Paula::MINIMUM_INTERVAL);
pos += subIncrement;
}
paula->remainder += paula->stepRemainder;
paula.remainder += paula.stepRemainder;
// Now, process any remaining integer clock amount < MINIMUM_INTERVAL
uint32 remainClocks = paula->remainder.GetInt();
uint32 remainClocks = paula.remainder.GetInt();
if(remainClocks)
{
typename Traits::output_t inSample = 0;
int32 posInt = pos.GetInt() * Traits::numChannelsIn;
for(int32 i = 0; i < Traits::numChannelsIn; i++)
inSample += Traits::Convert(inBuffer[posInt + i]);
paula->InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn)));
paula->Clock(remainClocks);
paula->remainder.RemoveInt();
paula.InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn)));
paula.Clock(remainClocks);
paula.remainder.RemoveInt();
}
auto out = paula->OutputSample(*WinSincIntegral);
auto out = paula.OutputSample(WinSincIntegral);
for(int i = 0; i < Traits::numChannelsOut; i++)
outSample[i] = out;
}
@ -104,9 +115,7 @@ struct AmigaBlepInterpolation
template<class Traits>
struct LinearInterpolation
{
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE LinearInterpolation(const ModChannel &, const CResampler &, unsigned int) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo)
{
@ -127,8 +136,7 @@ struct LinearInterpolation
template<class Traits>
struct FastSincInterpolation
{
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE FastSincInterpolation(const ModChannel &, const CResampler &, unsigned int) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo)
{
@ -152,7 +160,7 @@ struct PolyphaseInterpolation
{
const SINC_TYPE *sinc;
MPT_FORCEINLINE void Start(const ModChannel &chn, const CResampler &resampler)
MPT_FORCEINLINE PolyphaseInterpolation(const ModChannel &chn, const CResampler &resampler, unsigned int)
{
#ifdef MODPLUG_TRACKER
// Otherwise causes "warning C4100: 'resampler' : unreferenced formal parameter"
@ -164,8 +172,6 @@ struct PolyphaseInterpolation
(((chn.increment > SamplePosition(0x180000000ll)) || (chn.increment < SamplePosition(-0x180000000ll))) ? resampler.gDownsample2x : resampler.gDownsample13x) : resampler.gKaiserSinc);
}
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo)
{
static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
@ -192,13 +198,11 @@ struct FIRFilterInterpolation
{
const int16 *WFIRlut;
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &resampler)
MPT_FORCEINLINE FIRFilterInterpolation(const ModChannel &, const CResampler &resampler, unsigned int)
{
WFIRlut = resampler.m_WindowedFIR.lut;
}
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo)
{
static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
@ -230,30 +234,30 @@ struct NoRamp
{
typename Traits::output_t lVol, rVol;
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE NoRamp(const ModChannel &chn)
{
lVol = chn.leftVol;
rVol = chn.rightVol;
}
MPT_FORCEINLINE void End(const ModChannel &) { }
};
struct Ramp
{
ModChannel &channel;
int32 lRamp, rRamp;
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE Ramp(ModChannel &chn)
: channel{chn}
{
lRamp = chn.rampLeftVol;
rRamp = chn.rampRightVol;
}
MPT_FORCEINLINE void End(ModChannel &chn)
MPT_FORCEINLINE ~Ramp()
{
chn.rampLeftVol = lRamp; chn.leftVol = lRamp >> VOLUMERAMPPRECISION;
chn.rampRightVol = rRamp; chn.rightVol = rRamp >> VOLUMERAMPPRECISION;
channel.rampLeftVol = lRamp; channel.leftVol = lRamp >> VOLUMERAMPPRECISION;
channel.rampRightVol = rRamp; channel.rightVol = rRamp >> VOLUMERAMPPRECISION;
}
};
@ -331,8 +335,7 @@ struct MixStereoRamp : public Ramp
template<class Traits>
struct NoFilter
{
MPT_FORCEINLINE void Start(const ModChannel &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE NoFilter(const ModChannel &) { }
MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { }
};
@ -342,10 +345,12 @@ struct NoFilter
template<class Traits>
struct ResonantFilter
{
ModChannel &channel;
// Filter history
typename Traits::output_t fy[Traits::numChannelsIn][2];
MPT_FORCEINLINE void Start(const ModChannel &chn)
MPT_FORCEINLINE ResonantFilter(ModChannel &chn)
: channel{chn}
{
for(int i = 0; i < Traits::numChannelsIn; i++)
{
@ -354,12 +359,12 @@ struct ResonantFilter
}
}
MPT_FORCEINLINE void End(ModChannel &chn)
MPT_FORCEINLINE ~ResonantFilter()
{
for(int i = 0; i < Traits::numChannelsIn; i++)
{
chn.nFilter_Y[i][0] = fy[i][0];
chn.nFilter_Y[i][1] = fy[i][1];
channel.nFilter_Y[i][0] = fy[i][0];
channel.nFilter_Y[i][1] = fy[i][1];
}
}

View File

@ -41,8 +41,7 @@ struct MixerTraits
template<class Traits>
struct NoInterpolation
{
MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { }
MPT_FORCEINLINE void End(const ModChannel &) { }
MPT_FORCEINLINE NoInterpolation(const ModChannel &, const CResampler &, unsigned int) { }
MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const int32)
{
@ -72,14 +71,9 @@ static void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Tr
ModChannel &c = chn;
const typename Traits::input_t * MPT_RESTRICT inSample = static_cast<const typename Traits::input_t *>(c.pCurrentSample);
InterpolationFunc interpolate;
FilterFunc filter;
MixFunc mix;
// Do initialisation if necessary
interpolate.Start(c, resampler);
filter.Start(c);
mix.Start(c);
InterpolationFunc interpolate{c, resampler, numSamples};
FilterFunc filter{c};
MixFunc mix{c};
unsigned int samples = numSamples;
SamplePosition smpPos = c.position; // Fixed-point sample position
@ -96,10 +90,6 @@ static void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Tr
smpPos += increment;
}
mix.End(c);
filter.End(c);
interpolate.End(c);
c.position = smpPos;
}

View File

@ -500,6 +500,10 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
break;
case CMD_S3MCMDEX:
if(!chn.rowCommand.param && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
chn.rowCommand.param = chn.nOldCmdEx;
else
chn.nOldCmdEx = static_cast<ModCommand::PARAM>(chn.rowCommand.param);
if((p->param & 0xF0) == 0x60)
{
// Fine Pattern Delay
@ -958,6 +962,11 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
}
break;
}
if(m_playBehaviour[kST3EffectMemory] && param != 0)
{
UpdateS3MEffectMemory(chn, param);
}
}
// Interpret F00 effect in XM files as "stop song"
@ -3439,10 +3448,6 @@ bool CSoundFile::ProcessEffects()
// S3M/IT Sxx Extended Commands
case CMD_S3MCMDEX:
if(m_playBehaviour[kST3EffectMemory] && param == 0)
{
param = chn.nArpeggio; // S00 uses the last non-zero effect parameter as memory, like other effects including Arpeggio, so we "borrow" our memory there.
}
ExtendedS3MCommands(nChn, static_cast<ModCommand::PARAM>(param));
break;
@ -3702,9 +3707,9 @@ void CSoundFile::UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param)
chn.nTremorParam = param; // Ixy
chn.nArpeggio = param; // Jxy
chn.nRetrigParam = param; // Qxy
chn.nTremoloDepth = (param & 0x0F) << 2; // Rxy
chn.nTremoloSpeed = (param >> 4) & 0x0F; // Rxy
// Sxy is not handled here.
chn.nTremoloDepth = (param & 0x0F) << 2; // Rxy
chn.nTremoloSpeed = (param >> 4) & 0x0F; // Rxy
chn.nOldCmdEx = param; // Sxy
}

View File

@ -117,7 +117,7 @@ void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames)
if(m_outputToCC)
{
plugin->MidiSend(MIDIEvents::CC(static_cast<MIDIEvents::MidiCC>(m_outputParam & 0x7F), static_cast<uint8>((m_outputParam >> 8) & 0x0F), mpt::saturate_round<uint8>(value * 127.0f)));
} else
} else if(m_outputParam >= 0)
{
plugin->SetParameter(m_outputParam, static_cast<PlugParamValue>(value));
}