aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h')
-rw-r--r--Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h398
1 files changed, 398 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h b/Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h
new file mode 100644
index 00000000..e1376219
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/soundlib/IntMixer.h
@@ -0,0 +1,398 @@
+/*
+ * IntMixer.h
+ * ----------
+ * Purpose: Fixed point mixer classes
+ * Notes : (currently none)
+ * Authors: Olivier Lapicque
+ * OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "Resampler.h"
+#include "MixerInterface.h"
+#include "Paula.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+template<int channelsOut, int channelsIn, typename out, typename in, size_t mixPrecision>
+struct IntToIntTraits : public MixerTraits<channelsOut, channelsIn, out, in>
+{
+ typedef MixerTraits<channelsOut, channelsIn, out, in> base_t;
+ typedef typename base_t::input_t input_t;
+ typedef typename base_t::output_t output_t;
+
+ static MPT_CONSTEXPRINLINE output_t Convert(const input_t x)
+ {
+ static_assert(std::numeric_limits<input_t>::is_integer, "Input must be integer");
+ static_assert(std::numeric_limits<output_t>::is_integer, "Output must be integer");
+ static_assert(sizeof(out) * 8 >= mixPrecision, "Mix precision is higher than output type can handle");
+ static_assert(sizeof(in) * 8 <= mixPrecision, "Mix precision is lower than input type");
+ return static_cast<output_t>(x) * (1<<(mixPrecision - sizeof(in) * 8));
+ }
+};
+
+typedef IntToIntTraits<2, 1, mixsample_t, int8, 16> Int8MToIntS;
+typedef IntToIntTraits<2, 1, mixsample_t, int16, 16> Int16MToIntS;
+typedef IntToIntTraits<2, 2, mixsample_t, int8, 16> Int8SToIntS;
+typedef IntToIntTraits<2, 2, mixsample_t, int16, 16> Int16SToIntS;
+
+
+//////////////////////////////////////////////////////////////////////////
+// Interpolation templates
+
+
+template<class Traits>
+struct AmigaBlepInterpolation
+{
+ SamplePosition subIncrement;
+ Paula::State &paula;
+ const Paula::BlepArray &WinSincIntegral;
+ const int numSteps;
+ unsigned int remainingSamples = 0;
+
+ 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}
+ {
+ 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 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--)
+ {
+ 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(Paula::MINIMUM_INTERVAL);
+ pos += subIncrement;
+ }
+ paula.remainder += paula.stepRemainder;
+
+ // Now, process any remaining integer clock amount < MINIMUM_INTERVAL
+ 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();
+ }
+
+ auto out = paula.OutputSample(WinSincIntegral);
+ for(int i = 0; i < Traits::numChannelsOut; i++)
+ outSample[i] = out;
+ }
+};
+
+
+template<class Traits>
+struct LinearInterpolation
+{
+ 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)
+ {
+ static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
+ const typename Traits::output_t fract = posLo >> 18u;
+
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ typename Traits::output_t srcVol = Traits::Convert(inBuffer[i]);
+ typename Traits::output_t destVol = Traits::Convert(inBuffer[i + Traits::numChannelsIn]);
+
+ outSample[i] = srcVol + ((fract * (destVol - srcVol)) / 16384);
+ }
+ }
+};
+
+
+template<class Traits>
+struct FastSincInterpolation
+{
+ 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)
+ {
+ static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
+ const int16 *lut = CResampler::FastSincTable + ((posLo >> 22) & 0x3FC);
+
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ outSample[i] =
+ (lut[0] * Traits::Convert(inBuffer[i - Traits::numChannelsIn])
+ + lut[1] * Traits::Convert(inBuffer[i])
+ + lut[2] * Traits::Convert(inBuffer[i + Traits::numChannelsIn])
+ + lut[3] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])) / 16384;
+ }
+ }
+};
+
+
+template<class Traits>
+struct PolyphaseInterpolation
+{
+ const SINC_TYPE *sinc;
+
+ MPT_FORCEINLINE PolyphaseInterpolation(const ModChannel &chn, const CResampler &resampler, unsigned int)
+ {
+ #ifdef MODPLUG_TRACKER
+ // Otherwise causes "warning C4100: 'resampler' : unreferenced formal parameter"
+ // because all 3 tables are static members.
+ // #pragma warning fails with this templated case for unknown reasons.
+ MPT_UNREFERENCED_PARAMETER(resampler);
+ #endif // MODPLUG_TRACKER
+ 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 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");
+ const SINC_TYPE *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH;
+
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ outSample[i] =
+ (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn])
+ + lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn])
+ + lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn])
+ + lut[3] * Traits::Convert(inBuffer[i])
+ + lut[4] * Traits::Convert(inBuffer[i + Traits::numChannelsIn])
+ + lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])
+ + lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn])
+ + lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn])) / (1 << SINC_QUANTSHIFT);
+ }
+ }
+};
+
+
+template<class Traits>
+struct FIRFilterInterpolation
+{
+ const int16 *WFIRlut;
+
+ MPT_FORCEINLINE FIRFilterInterpolation(const ModChannel &, const CResampler &resampler, unsigned int)
+ {
+ WFIRlut = resampler.m_WindowedFIR.lut;
+ }
+
+ 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");
+ const int16 * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK);
+
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ typename Traits::output_t vol1 =
+ (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn]))
+ + (lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn]))
+ + (lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]))
+ + (lut[3] * Traits::Convert(inBuffer[i]));
+ typename Traits::output_t vol2 =
+ (lut[4] * Traits::Convert(inBuffer[i + 1 * Traits::numChannelsIn]))
+ + (lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]))
+ + (lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn]))
+ + (lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn]));
+ outSample[i] = ((vol1 / 2) + (vol2 / 2)) / (1 << (WFIR_16BITSHIFT - 1));
+ }
+ }
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+// Mixing templates (add sample to stereo mix)
+
+template<class Traits>
+struct NoRamp
+{
+ typename Traits::output_t lVol, rVol;
+
+ MPT_FORCEINLINE NoRamp(const ModChannel &chn)
+ {
+ lVol = chn.leftVol;
+ rVol = chn.rightVol;
+ }
+};
+
+
+struct Ramp
+{
+ ModChannel &channel;
+ int32 lRamp, rRamp;
+
+ MPT_FORCEINLINE Ramp(ModChannel &chn)
+ : channel{chn}
+ {
+ lRamp = chn.rampLeftVol;
+ rRamp = chn.rampRightVol;
+ }
+
+ MPT_FORCEINLINE ~Ramp()
+ {
+ channel.rampLeftVol = lRamp; channel.leftVol = lRamp >> VOLUMERAMPPRECISION;
+ channel.rampRightVol = rRamp; channel.rightVol = rRamp >> VOLUMERAMPPRECISION;
+ }
+};
+
+
+// Legacy optimization: If chn.nLeftVol == chn.nRightVol, save one multiplication instruction
+template<class Traits>
+struct MixMonoFastNoRamp : public NoRamp<Traits>
+{
+ typedef NoRamp<Traits> base_t;
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer)
+ {
+ typename Traits::output_t vol = outSample[0] * base_t::lVol;
+ for(int i = 0; i < Traits::numChannelsOut; i++)
+ {
+ outBuffer[i] += vol;
+ }
+ }
+};
+
+
+template<class Traits>
+struct MixMonoNoRamp : public NoRamp<Traits>
+{
+ typedef NoRamp<Traits> base_t;
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer)
+ {
+ outBuffer[0] += outSample[0] * base_t::lVol;
+ outBuffer[1] += outSample[0] * base_t::rVol;
+ }
+};
+
+
+template<class Traits>
+struct MixMonoRamp : public Ramp
+{
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer)
+ {
+ lRamp += chn.leftRamp;
+ rRamp += chn.rightRamp;
+ outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION);
+ outBuffer[1] += outSample[0] * (rRamp >> VOLUMERAMPPRECISION);
+ }
+};
+
+
+template<class Traits>
+struct MixStereoNoRamp : public NoRamp<Traits>
+{
+ typedef NoRamp<Traits> base_t;
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer)
+ {
+ outBuffer[0] += outSample[0] * base_t::lVol;
+ outBuffer[1] += outSample[1] * base_t::rVol;
+ }
+};
+
+
+template<class Traits>
+struct MixStereoRamp : public Ramp
+{
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer)
+ {
+ lRamp += chn.leftRamp;
+ rRamp += chn.rightRamp;
+ outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION);
+ outBuffer[1] += outSample[1] * (rRamp >> VOLUMERAMPPRECISION);
+ }
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+// Filter templates
+
+
+template<class Traits>
+struct NoFilter
+{
+ MPT_FORCEINLINE NoFilter(const ModChannel &) { }
+
+ MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { }
+};
+
+
+// Resonant filter
+template<class Traits>
+struct ResonantFilter
+{
+ ModChannel &channel;
+ // Filter history
+ typename Traits::output_t fy[Traits::numChannelsIn][2];
+
+ MPT_FORCEINLINE ResonantFilter(ModChannel &chn)
+ : channel{chn}
+ {
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ fy[i][0] = chn.nFilter_Y[i][0];
+ fy[i][1] = chn.nFilter_Y[i][1];
+ }
+ }
+
+ MPT_FORCEINLINE ~ResonantFilter()
+ {
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ channel.nFilter_Y[i][0] = fy[i][0];
+ channel.nFilter_Y[i][1] = fy[i][1];
+ }
+ }
+
+ // To avoid a precision loss in the state variables especially with quiet samples at low cutoff and high mix rate, we pre-amplify the sample.
+#define MIXING_FILTER_PREAMP 256
+ // Filter values are clipped to double the input range
+#define ClipFilter(x) Clamp<typename Traits::output_t, typename Traits::output_t>(x, int16_min * 2 * MIXING_FILTER_PREAMP, int16_max * 2 * MIXING_FILTER_PREAMP)
+
+ MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn)
+ {
+ static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels");
+
+ for(int i = 0; i < Traits::numChannelsIn; i++)
+ {
+ const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP;
+ typename Traits::output_t val = static_cast<typename Traits::output_t>(mpt::rshift_signed(
+ Util::mul32to64(inputAmp, chn.nFilter_A0) +
+ Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) +
+ Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) +
+ (1 << (MIXING_FILTER_PRECISION - 1)), MIXING_FILTER_PRECISION));
+ fy[i][1] = fy[i][0];
+ fy[i][0] = val - (inputAmp & chn.nFilter_HP);
+ outSample[i] = val / MIXING_FILTER_PREAMP;
+ }
+ }
+
+#undef ClipFilter
+};
+
+
+OPENMPT_NAMESPACE_END