From e1750e23effc1fe9bf50bdd9317054055c37a867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Heusipp?= Date: Tue, 17 Dec 2024 09:49:25 +0000 Subject: [PATCH] [Ref] mpt/base/saturate_round.hpp: Rename floating point variants of saturate_cast to saturate_trunc in order to make mpt::saturate_cast as broken as C++26 std::saturate_cast. [Ref] mpt/base/saturate_cast.hpp: Use C++26 saturate_cast if available. git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@22561 56274372-70c3-4bfc-bfc3-4c3a0b034d27 --- misc/mptColor.cpp | 2 +- mptrack/CImageListEx.cpp | 6 ++-- mptrack/Mainfrm.h | 2 +- mptrack/ResizableDialog.cpp | 8 ++--- mptrack/View_smp.cpp | 2 +- mptrack/plugins/MidiInOut.cpp | 4 +-- soundlib/Load_symmod.cpp | 8 ++--- soundlib/SampleFormatSFZ.cpp | 2 +- src/mpt/base/saturate_cast.hpp | 32 +++++++------------ src/mpt/base/saturate_round.hpp | 31 +++++++++++++++--- .../base/tests/tests_base_saturate_cast.hpp | 1 - .../base/tests/tests_base_saturate_round.hpp | 2 ++ src/mpt/base/utility.hpp | 22 ++++++++++++- src/openmpt/soundbase/SampleConvert.hpp | 5 +-- .../soundbase/SampleConvertFixedPoint.hpp | 4 +-- 15 files changed, 83 insertions(+), 48 deletions(-) diff --git a/misc/mptColor.cpp b/misc/mptColor.cpp index 8b0b91ac152..5d594dc20fd 100644 --- a/misc/mptColor.cpp +++ b/misc/mptColor.cpp @@ -20,7 +20,7 @@ namespace mpt::Color uint8 GetLuma(uint8 r, uint8 g, uint8 b) noexcept { - return mpt::saturate_cast(r * 0.299f + g * 0.587f + b * 0.114f); + return mpt::saturate_trunc(r * 0.299f + g * 0.587f + b * 0.114f); } diff --git a/mptrack/CImageListEx.cpp b/mptrack/CImageListEx.cpp index cfb030b12ef..f0bc4ce1863 100644 --- a/mptrack/CImageListEx.cpp +++ b/mptrack/CImageListEx.cpp @@ -46,9 +46,9 @@ bool CImageListEx::Create(UINT resourceID, int cx, int cy, int nInitial, int nGr auto hsv = mpt::Color::RGB{pixel->r / 255.0f, pixel->g / 255.0f, pixel->b / 255.0f}.ToHSV(); hsv.v = (1.0f - hsv.v) * (1.0f - hsv.s) + (hsv.v) * hsv.s; const auto rgb = hsv.ToRGB(); - pixel->r = mpt::saturate_cast(rgb.r * 255.0f); - pixel->g = mpt::saturate_cast(rgb.g * 255.0f); - pixel->b = mpt::saturate_cast(rgb.b * 255.0f); + pixel->r = mpt::saturate_trunc(rgb.r * 255.0f); + pixel->g = mpt::saturate_trunc(rgb.g * 255.0f); + pixel->b = mpt::saturate_trunc(rgb.b * 255.0f); } } } diff --git a/mptrack/Mainfrm.h b/mptrack/Mainfrm.h index d378ab1c4cc..b4b7aa46904 100644 --- a/mptrack/Mainfrm.h +++ b/mptrack/Mainfrm.h @@ -283,7 +283,7 @@ class CMainFrame void UpdateTree(CModDoc *pModDoc, UpdateHint hint, CObject *pHint = nullptr); void RefreshDlsBanks(); static CInputHandler *GetInputHandler(); - void SetElapsedTime(double t) { m_dwTimeSec = mpt::saturate_cast(t * 10.0); } + void SetElapsedTime(double t) { m_dwTimeSec = mpt::saturate_trunc(t * 10.0); } #if defined(MPT_ENABLE_UPDATE) bool ShowUpdateIndicator(const UpdateCheckResult &result, const CString &releaseVersion, const CString &infoURL, bool showHighlight); diff --git a/mptrack/ResizableDialog.cpp b/mptrack/ResizableDialog.cpp index 4760c80ae25..48b1c478cc6 100644 --- a/mptrack/ResizableDialog.cpp +++ b/mptrack/ResizableDialog.cpp @@ -119,15 +119,15 @@ CRect ResizableDialog::AdjustItemRect(const DynamicItem &item, const CSize windo CPoint move; if(item.moveSettings.IsHorizontal()) - move.x = mpt::saturate_cast(ratioX * item.moveSettings.m_nXRatio); + move.x = mpt::saturate_trunc(ratioX * item.moveSettings.m_nXRatio); if(item.moveSettings.IsVertical()) - move.y = mpt::saturate_cast(ratioY * item.moveSettings.m_nYRatio); + move.y = mpt::saturate_trunc(ratioY * item.moveSettings.m_nYRatio); CSize size; if(item.sizeSettings.IsHorizontal()) - size.cx = mpt::saturate_cast(ratioX * item.sizeSettings.m_nXRatio); + size.cx = mpt::saturate_trunc(ratioX * item.sizeSettings.m_nXRatio); if(item.sizeSettings.IsVertical()) - size.cy = mpt::saturate_cast(ratioY * item.sizeSettings.m_nYRatio); + size.cy = mpt::saturate_trunc(ratioY * item.sizeSettings.m_nYRatio); auto itemPoint = item.initialPoint + move; auto itemSize = item.initialSize + size; diff --git a/mptrack/View_smp.cpp b/mptrack/View_smp.cpp index d1bc9c6b659..6168c3c44a4 100644 --- a/mptrack/View_smp.cpp +++ b/mptrack/View_smp.cpp @@ -296,7 +296,7 @@ void CViewSample::UpdateScrollSize(int newZoom, bool forceRefresh, SmpLength cen m_timelineUnit = mpt::saturate_round(std::log(static_cast(timelineInterval)) / std::log(power)); if(m_timelineUnit < 1) m_timelineUnit = 0; - m_timelineUnit = mpt::saturate_cast(std::pow(power, m_timelineUnit)); + m_timelineUnit = mpt::saturate_trunc(std::pow(power, m_timelineUnit)); timelineInterval = std::max(1.0, std::round(timelineInterval / m_timelineUnit)) * m_timelineUnit; if(format == TimelineFormat::Seconds) timelineInterval *= sampleRate / 1000.0; diff --git a/mptrack/plugins/MidiInOut.cpp b/mptrack/plugins/MidiInOut.cpp index b6c96028df7..4b75891fac2 100644 --- a/mptrack/plugins/MidiInOut.cpp +++ b/mptrack/plugins/MidiInOut.cpp @@ -444,7 +444,7 @@ void MidiInOut::Process(float *, float *, uint32 numFrames) if(m_sendTimingInfo && !m_positionChanged && m_SndFile.m_PlayState.m_ppqPosFract == 0.0) { // Send Song Position on every pattern change or start of new measure - uint16 ppq = mpt::saturate_cast((m_SndFile.m_PlayState.m_ppqPosBeat + m_SndFile.m_PlayState.m_ppqPosFract) * 4.0); + uint16 ppq = mpt::saturate_trunc((m_SndFile.m_PlayState.m_ppqPosBeat + m_SndFile.m_PlayState.m_ppqPosFract) * 4.0); if(ppq < 16384) { uint32 midiCode = MIDIEvents::SongPosition(ppq); @@ -574,7 +574,7 @@ void MidiInOut::PositionChanged() if(m_sendTimingInfo && !m_SndFile.IsPaused()) { MidiSend(0xFC); // Stop - uint16 ppq = mpt::saturate_cast((m_SndFile.m_PlayState.m_ppqPosBeat + m_SndFile.m_PlayState.m_ppqPosFract) * 4.0); + uint16 ppq = mpt::saturate_trunc((m_SndFile.m_PlayState.m_ppqPosBeat + m_SndFile.m_PlayState.m_ppqPosFract) * 4.0); if(ppq < 16384) MidiSend(MIDIEvents::SongPosition(ppq)); MidiSend(0xFA); // Start diff --git a/soundlib/Load_symmod.cpp b/soundlib/Load_symmod.cpp index ece62df1c05..99752b6efa7 100644 --- a/soundlib/Load_symmod.cpp +++ b/soundlib/Load_symmod.cpp @@ -282,8 +282,8 @@ struct SymTranswaveInst std::pair ConvertLoop(const ModSample &mptSmp) const { const double loopScale = static_cast(mptSmp.nLength) / (100 << 16); - const SmpLength start = mpt::saturate_cast(loopScale * std::min(uint32(100 << 16), loopStart.get())); - const SmpLength length = mpt::saturate_cast(loopScale * std::min(uint32(100 << 16), loopLen.get())); + const SmpLength start = mpt::saturate_trunc(loopScale * std::min(uint32(100 << 16), loopStart.get())); + const SmpLength length = mpt::saturate_trunc(loopScale * std::min(uint32(100 << 16), loopLen.get())); return {start, std::min(mptSmp.nLength - start, length)}; } }; @@ -670,8 +670,8 @@ struct SymInstrument loopLen = (loopLen << 16) + loopLenFine; const double loopScale = static_cast(mptSmp.nLength) / (100 << 16); - loopStart = std::min(mptSmp.nLength, mpt::saturate_cast(loopStart * loopScale)); - loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_cast(loopLen * loopScale)); + loopStart = std::min(mptSmp.nLength, mpt::saturate_trunc(loopStart * loopScale)); + loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_trunc(loopLen * loopScale)); } else if(mptSmp.HasSampleData()) { // The order of operations here may seem weird as it reduces precision, but it's taken directly from the original assembly source (UpdateRecalcLoop) diff --git a/soundlib/SampleFormatSFZ.cpp b/soundlib/SampleFormatSFZ.cpp index 6e7b392b6b2..6a075313575 100644 --- a/soundlib/SampleFormatSFZ.cpp +++ b/soundlib/SampleFormatSFZ.cpp @@ -901,7 +901,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) if(region.ampEnv.release > 0) { const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(GetSampleRate()); - pIns->nFadeOut = std::min(mpt::saturate_cast(32768.0 * tickDuration / region.ampEnv.release), uint32(32767)); + pIns->nFadeOut = std::min(mpt::saturate_trunc(32768.0 * tickDuration / region.ampEnv.release), uint32(32767)); if(GetType() == MOD_TYPE_IT) pIns->nFadeOut = std::min((pIns->nFadeOut + 16u) & ~31u, uint32(8192)); } diff --git a/src/mpt/base/saturate_cast.hpp b/src/mpt/base/saturate_cast.hpp index bdcaec9b17a..02c31927ece 100644 --- a/src/mpt/base/saturate_cast.hpp +++ b/src/mpt/base/saturate_cast.hpp @@ -5,10 +5,14 @@ +#include "mpt/base/detect.hpp" #include "mpt/base/namespace.hpp" #include #include +#if MPT_CXX_AT_LEAST(26) +#include +#endif @@ -16,6 +20,12 @@ namespace mpt { inline namespace MPT_INLINE_NS { +#if MPT_CXX_AT_LEAST(26) + +using std::saturate_cast; + +#else + // Saturate the value of src to the domain of Tdst template constexpr Tdst saturate_cast(Tsrc src) noexcept { @@ -51,27 +61,7 @@ constexpr Tdst saturate_cast(Tsrc src) noexcept { } } -template -constexpr Tdst saturate_cast(double src) { - if (src >= static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (src <= static_cast(std::numeric_limits::min())) { - return std::numeric_limits::min(); - } - return static_cast(src); -} - -template -constexpr Tdst saturate_cast(float src) { - if (src >= static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (src <= static_cast(std::numeric_limits::min())) { - return std::numeric_limits::min(); - } - return static_cast(src); -} +#endif } // namespace MPT_INLINE_NS diff --git a/src/mpt/base/saturate_round.hpp b/src/mpt/base/saturate_round.hpp index 167a4607ebd..295dbd8fea4 100644 --- a/src/mpt/base/saturate_round.hpp +++ b/src/mpt/base/saturate_round.hpp @@ -8,7 +8,6 @@ #include "mpt/base/namespace.hpp" #include "mpt/base/math.hpp" -#include "mpt/base/saturate_cast.hpp" #include @@ -18,25 +17,49 @@ namespace mpt { inline namespace MPT_INLINE_NS { + +template +constexpr Tdst saturate_trunc(double src) { + if (src >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (src <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + return static_cast(src); +} + +template +constexpr Tdst saturate_trunc(float src) { + if (src >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (src <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + return static_cast(src); +} + + // Rounds given double value to nearest integer value of type T. // Out-of-range values are saturated to the specified integer type's limits. template inline T saturate_round(float val) { static_assert(std::numeric_limits::is_integer); - return mpt::saturate_cast(mpt::round(val)); + return mpt::saturate_trunc(mpt::round(val)); } template inline T saturate_round(double val) { static_assert(std::numeric_limits::is_integer); - return mpt::saturate_cast(mpt::round(val)); + return mpt::saturate_trunc(mpt::round(val)); } template inline T saturate_round(long double val) { static_assert(std::numeric_limits::is_integer); - return mpt::saturate_cast(mpt::round(val)); + return mpt::saturate_trunc(mpt::round(val)); } diff --git a/src/mpt/base/tests/tests_base_saturate_cast.hpp b/src/mpt/base/tests/tests_base_saturate_cast.hpp index e2d69ad9dea..94ee83a72ae 100644 --- a/src/mpt/base/tests/tests_base_saturate_cast.hpp +++ b/src/mpt/base/tests/tests_base_saturate_cast.hpp @@ -95,7 +95,6 @@ MPT_TEST_GROUP_INLINE("mpt/base/saturate_cast") MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(std::numeric_limits::max() - 1), std::numeric_limits::max()); - MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast(static_cast(std::numeric_limits::max())), std::numeric_limits::max()); } } // namespace saturate_cast diff --git a/src/mpt/base/tests/tests_base_saturate_round.hpp b/src/mpt/base/tests/tests_base_saturate_round.hpp index 27f753e76ba..de7237035e8 100644 --- a/src/mpt/base/tests/tests_base_saturate_round.hpp +++ b/src/mpt/base/tests/tests_base_saturate_round.hpp @@ -34,6 +34,8 @@ MPT_TEST_GROUP_INLINE("mpt/base/saturate_round") #pragma clang diagnostic pop #endif { + MPT_TEST_EXPECT_EQUAL(mpt::saturate_trunc(static_cast(std::numeric_limits::max())), std::numeric_limits::max()); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::max() + 0.1), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::max() - 0.4), std::numeric_limits::max()); MPT_TEST_EXPECT_EQUAL(mpt::saturate_round(std::numeric_limits::min() + 0.1), std::numeric_limits::min()); diff --git a/src/mpt/base/utility.hpp b/src/mpt/base/utility.hpp index 7d3d4e9f18c..629afef087d 100644 --- a/src/mpt/base/utility.hpp +++ b/src/mpt/base/utility.hpp @@ -11,6 +11,7 @@ #if MPT_CXX_BEFORE(20) #include "mpt/base/saturate_cast.hpp" +#include "mpt/base/saturate_round.hpp" #endif #if MPT_CXX_BEFORE(23) && !MPT_COMPILER_MSVC && !MPT_COMPILER_GCC && !MPT_COMPILER_CLANG @@ -42,11 +43,30 @@ using std::in_range; #else +namespace detail { + +template +constexpr Tdst saturate_cast(Tsrc src) noexcept { + return mpt::saturate_cast(src); +} + +template +constexpr Tdst saturate_cast(double src) { + return mpt::saturate_trunc(src); +} + +template +constexpr Tdst saturate_cast(float src) { + return mpt::saturate_trunc(src); +} + +} + // Returns true iff Tdst can represent the value val. // Use as if(mpt::in_range(-1)). template constexpr bool in_range(Tsrc val) { - return (static_cast(mpt::saturate_cast(val)) == val); + return (static_cast(mpt::detail::saturate_cast(val)) == val); } #endif diff --git a/src/openmpt/soundbase/SampleConvert.hpp b/src/openmpt/soundbase/SampleConvert.hpp index 8ebb477374e..3e68b0ece5d 100644 --- a/src/openmpt/soundbase/SampleConvert.hpp +++ b/src/openmpt/soundbase/SampleConvert.hpp @@ -10,6 +10,7 @@ #include "mpt/base/macros.hpp" #include "mpt/base/math.hpp" #include "mpt/base/saturate_cast.hpp" +#include "mpt/base/saturate_round.hpp" #include "openmpt/base/Int24.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/SampleConvert.hpp" @@ -566,7 +567,7 @@ struct Convert { val = mpt::safe_clamp(val, -1.0f, 1.0f); val *= static_cast(uint64(1) << 63); - return mpt::saturate_cast(SC::fastround(val)); + return mpt::saturate_trunc(SC::fastround(val)); } }; @@ -579,7 +580,7 @@ struct Convert { val = std::clamp(val, -1.0, 1.0); val *= static_cast(uint64(1) << 63); - return mpt::saturate_cast(SC::fastround(val)); + return mpt::saturate_trunc(SC::fastround(val)); } }; diff --git a/src/openmpt/soundbase/SampleConvertFixedPoint.hpp b/src/openmpt/soundbase/SampleConvertFixedPoint.hpp index 04627e2f21c..461dc69c3a5 100644 --- a/src/openmpt/soundbase/SampleConvertFixedPoint.hpp +++ b/src/openmpt/soundbase/SampleConvertFixedPoint.hpp @@ -232,7 +232,7 @@ struct ConvertToFixedPoint { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); val = mpt::sanitize_nan(val); - return mpt::saturate_cast(SC::fastround(val * factor)); + return mpt::saturate_trunc(SC::fastround(val * factor)); } }; @@ -251,7 +251,7 @@ struct ConvertToFixedPoint { static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t) * 8 - 1); val = mpt::sanitize_nan(val); - return mpt::saturate_cast(SC::fastround(val * factor)); + return mpt::saturate_trunc(SC::fastround(val * factor)); } };