1 /* 2 * IntMixer.h 3 * ---------- 4 * Purpose: Fixed point mixer classes 5 * Notes : (currently none) 6 * Authors: Olivier Lapicque 7 * OpenMPT Devs 8 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. 9 */ 10 11 12 #pragma once 13 14 #include "openmpt/all/BuildSettings.hpp" 15 16 #include "Resampler.h" 17 #include "MixerInterface.h" 18 #include "Paula.h" 19 20 OPENMPT_NAMESPACE_BEGIN 21 22 template<int channelsOut, int channelsIn, typename out, typename in, size_t mixPrecision> 23 struct IntToIntTraits : public MixerTraits<channelsOut, channelsIn, out, in> 24 { 25 typedef MixerTraits<channelsOut, channelsIn, out, in> base_t; 26 typedef typename base_t::input_t input_t; 27 typedef typename base_t::output_t output_t; 28 ConvertIntToIntTraits29 static MPT_CONSTEXPRINLINE output_t Convert(const input_t x) 30 { 31 static_assert(std::numeric_limits<input_t>::is_integer, "Input must be integer"); 32 static_assert(std::numeric_limits<output_t>::is_integer, "Output must be integer"); 33 static_assert(sizeof(out) * 8 >= mixPrecision, "Mix precision is higher than output type can handle"); 34 static_assert(sizeof(in) * 8 <= mixPrecision, "Mix precision is lower than input type"); 35 return static_cast<output_t>(x) * (1<<(mixPrecision - sizeof(in) * 8)); 36 } 37 }; 38 39 typedef IntToIntTraits<2, 1, mixsample_t, int8, 16> Int8MToIntS; 40 typedef IntToIntTraits<2, 1, mixsample_t, int16, 16> Int16MToIntS; 41 typedef IntToIntTraits<2, 2, mixsample_t, int8, 16> Int8SToIntS; 42 typedef IntToIntTraits<2, 2, mixsample_t, int16, 16> Int16SToIntS; 43 44 45 ////////////////////////////////////////////////////////////////////////// 46 // Interpolation templates 47 48 49 template<class Traits> 50 struct AmigaBlepInterpolation 51 { 52 SamplePosition subIncrement; 53 Paula::State *paula; 54 const Paula::BlepArray *WinSincIntegral; 55 int numSteps; 56 StartAmigaBlepInterpolation57 MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &resampler) 58 { 59 paula = &chn.paulaState; 60 numSteps = paula->numSteps; 61 WinSincIntegral = &resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER]); 62 if(numSteps) 63 subIncrement = chn.increment / numSteps; 64 } 65 EndAmigaBlepInterpolation66 MPT_FORCEINLINE void End(const ModChannel &) { } 67 operatorAmigaBlepInterpolation68 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) 69 { 70 SamplePosition pos(0, posLo); 71 // First, process steps of full length (one Amiga clock interval) 72 for(int step = numSteps; step > 0; step--) 73 { 74 typename Traits::output_t inSample = 0; 75 int32 posInt = pos.GetInt() * Traits::numChannelsIn; 76 for(int32 i = 0; i < Traits::numChannelsIn; i++) 77 inSample += Traits::Convert(inBuffer[posInt + i]); 78 paula->InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn))); 79 paula->Clock(Paula::MINIMUM_INTERVAL); 80 pos += subIncrement; 81 } 82 paula->remainder += paula->stepRemainder; 83 84 // Now, process any remaining integer clock amount < MINIMUM_INTERVAL 85 uint32 remainClocks = paula->remainder.GetInt(); 86 if(remainClocks) 87 { 88 typename Traits::output_t inSample = 0; 89 int32 posInt = pos.GetInt() * Traits::numChannelsIn; 90 for(int32 i = 0; i < Traits::numChannelsIn; i++) 91 inSample += Traits::Convert(inBuffer[posInt + i]); 92 paula->InputSample(static_cast<int16>(inSample / (4 * Traits::numChannelsIn))); 93 paula->Clock(remainClocks); 94 paula->remainder.RemoveInt(); 95 } 96 97 auto out = paula->OutputSample(*WinSincIntegral); 98 for(int i = 0; i < Traits::numChannelsOut; i++) 99 outSample[i] = out; 100 } 101 }; 102 103 104 template<class Traits> 105 struct LinearInterpolation 106 { StartLinearInterpolation107 MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } 108 EndLinearInterpolation109 MPT_FORCEINLINE void End(const ModChannel &) { } 110 operatorLinearInterpolation111 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) 112 { 113 static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels"); 114 const typename Traits::output_t fract = posLo >> 18u; 115 116 for(int i = 0; i < Traits::numChannelsIn; i++) 117 { 118 typename Traits::output_t srcVol = Traits::Convert(inBuffer[i]); 119 typename Traits::output_t destVol = Traits::Convert(inBuffer[i + Traits::numChannelsIn]); 120 121 outSample[i] = srcVol + ((fract * (destVol - srcVol)) / 16384); 122 } 123 } 124 }; 125 126 127 template<class Traits> 128 struct FastSincInterpolation 129 { StartFastSincInterpolation130 MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &) { } EndFastSincInterpolation131 MPT_FORCEINLINE void End(const ModChannel &) { } 132 operatorFastSincInterpolation133 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) 134 { 135 static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels"); 136 const int16 *lut = CResampler::FastSincTable + ((posLo >> 22) & 0x3FC); 137 138 for(int i = 0; i < Traits::numChannelsIn; i++) 139 { 140 outSample[i] = 141 (lut[0] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) 142 + lut[1] * Traits::Convert(inBuffer[i]) 143 + lut[2] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) 144 + lut[3] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])) / 16384; 145 } 146 } 147 }; 148 149 150 template<class Traits> 151 struct PolyphaseInterpolation 152 { 153 const SINC_TYPE *sinc; 154 StartPolyphaseInterpolation155 MPT_FORCEINLINE void Start(const ModChannel &chn, const CResampler &resampler) 156 { 157 #ifdef MODPLUG_TRACKER 158 // Otherwise causes "warning C4100: 'resampler' : unreferenced formal parameter" 159 // because all 3 tables are static members. 160 // #pragma warning fails with this templated case for unknown reasons. 161 MPT_UNREFERENCED_PARAMETER(resampler); 162 #endif // MODPLUG_TRACKER 163 sinc = (((chn.increment > SamplePosition(0x130000000ll)) || (chn.increment < SamplePosition(-0x130000000ll))) ? 164 (((chn.increment > SamplePosition(0x180000000ll)) || (chn.increment < SamplePosition(-0x180000000ll))) ? resampler.gDownsample2x : resampler.gDownsample13x) : resampler.gKaiserSinc); 165 } 166 EndPolyphaseInterpolation167 MPT_FORCEINLINE void End(const ModChannel &) { } 168 operatorPolyphaseInterpolation169 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) 170 { 171 static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels"); 172 const SINC_TYPE *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH; 173 174 for(int i = 0; i < Traits::numChannelsIn; i++) 175 { 176 outSample[i] = 177 (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn]) 178 + lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn]) 179 + lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn]) 180 + lut[3] * Traits::Convert(inBuffer[i]) 181 + lut[4] * Traits::Convert(inBuffer[i + Traits::numChannelsIn]) 182 + lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn]) 183 + lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn]) 184 + lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn])) / (1 << SINC_QUANTSHIFT); 185 } 186 } 187 }; 188 189 190 template<class Traits> 191 struct FIRFilterInterpolation 192 { 193 const int16 *WFIRlut; 194 StartFIRFilterInterpolation195 MPT_FORCEINLINE void Start(const ModChannel &, const CResampler &resampler) 196 { 197 WFIRlut = resampler.m_WindowedFIR.lut; 198 } 199 EndFIRFilterInterpolation200 MPT_FORCEINLINE void End(const ModChannel &) { } 201 operatorFIRFilterInterpolation202 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) 203 { 204 static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels"); 205 const int16 * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK); 206 207 for(int i = 0; i < Traits::numChannelsIn; i++) 208 { 209 typename Traits::output_t vol1 = 210 (lut[0] * Traits::Convert(inBuffer[i - 3 * Traits::numChannelsIn])) 211 + (lut[1] * Traits::Convert(inBuffer[i - 2 * Traits::numChannelsIn])) 212 + (lut[2] * Traits::Convert(inBuffer[i - Traits::numChannelsIn])) 213 + (lut[3] * Traits::Convert(inBuffer[i])); 214 typename Traits::output_t vol2 = 215 (lut[4] * Traits::Convert(inBuffer[i + 1 * Traits::numChannelsIn])) 216 + (lut[5] * Traits::Convert(inBuffer[i + 2 * Traits::numChannelsIn])) 217 + (lut[6] * Traits::Convert(inBuffer[i + 3 * Traits::numChannelsIn])) 218 + (lut[7] * Traits::Convert(inBuffer[i + 4 * Traits::numChannelsIn])); 219 outSample[i] = ((vol1 / 2) + (vol2 / 2)) / (1 << (WFIR_16BITSHIFT - 1)); 220 } 221 } 222 }; 223 224 225 ////////////////////////////////////////////////////////////////////////// 226 // Mixing templates (add sample to stereo mix) 227 228 template<class Traits> 229 struct NoRamp 230 { 231 typename Traits::output_t lVol, rVol; 232 StartNoRamp233 MPT_FORCEINLINE void Start(const ModChannel &chn) 234 { 235 lVol = chn.leftVol; 236 rVol = chn.rightVol; 237 } 238 EndNoRamp239 MPT_FORCEINLINE void End(const ModChannel &) { } 240 }; 241 242 243 struct Ramp 244 { 245 int32 lRamp, rRamp; 246 StartRamp247 MPT_FORCEINLINE void Start(const ModChannel &chn) 248 { 249 lRamp = chn.rampLeftVol; 250 rRamp = chn.rampRightVol; 251 } 252 EndRamp253 MPT_FORCEINLINE void End(ModChannel &chn) 254 { 255 chn.rampLeftVol = lRamp; chn.leftVol = lRamp >> VOLUMERAMPPRECISION; 256 chn.rampRightVol = rRamp; chn.rightVol = rRamp >> VOLUMERAMPPRECISION; 257 } 258 }; 259 260 261 // Legacy optimization: If chn.nLeftVol == chn.nRightVol, save one multiplication instruction 262 template<class Traits> 263 struct MixMonoFastNoRamp : public NoRamp<Traits> 264 { 265 typedef NoRamp<Traits> base_t; operatorMixMonoFastNoRamp266 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) 267 { 268 typename Traits::output_t vol = outSample[0] * base_t::lVol; 269 for(int i = 0; i < Traits::numChannelsOut; i++) 270 { 271 outBuffer[i] += vol; 272 } 273 } 274 }; 275 276 277 template<class Traits> 278 struct MixMonoNoRamp : public NoRamp<Traits> 279 { 280 typedef NoRamp<Traits> base_t; operatorMixMonoNoRamp281 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) 282 { 283 outBuffer[0] += outSample[0] * base_t::lVol; 284 outBuffer[1] += outSample[0] * base_t::rVol; 285 } 286 }; 287 288 289 template<class Traits> 290 struct MixMonoRamp : public Ramp 291 { operatorMixMonoRamp292 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer) 293 { 294 lRamp += chn.leftRamp; 295 rRamp += chn.rightRamp; 296 outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION); 297 outBuffer[1] += outSample[0] * (rRamp >> VOLUMERAMPPRECISION); 298 } 299 }; 300 301 302 template<class Traits> 303 struct MixStereoNoRamp : public NoRamp<Traits> 304 { 305 typedef NoRamp<Traits> base_t; operatorMixStereoNoRamp306 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &, typename Traits::output_t * const MPT_RESTRICT outBuffer) 307 { 308 outBuffer[0] += outSample[0] * base_t::lVol; 309 outBuffer[1] += outSample[1] * base_t::rVol; 310 } 311 }; 312 313 314 template<class Traits> 315 struct MixStereoRamp : public Ramp 316 { operatorMixStereoRamp317 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &outSample, const ModChannel &chn, typename Traits::output_t * const MPT_RESTRICT outBuffer) 318 { 319 lRamp += chn.leftRamp; 320 rRamp += chn.rightRamp; 321 outBuffer[0] += outSample[0] * (lRamp >> VOLUMERAMPPRECISION); 322 outBuffer[1] += outSample[1] * (rRamp >> VOLUMERAMPPRECISION); 323 } 324 }; 325 326 327 ////////////////////////////////////////////////////////////////////////// 328 // Filter templates 329 330 331 template<class Traits> 332 struct NoFilter 333 { StartNoFilter334 MPT_FORCEINLINE void Start(const ModChannel &) { } EndNoFilter335 MPT_FORCEINLINE void End(const ModChannel &) { } 336 operatorNoFilter337 MPT_FORCEINLINE void operator() (const typename Traits::outbuf_t &, const ModChannel &) { } 338 }; 339 340 341 // Resonant filter 342 template<class Traits> 343 struct ResonantFilter 344 { 345 // Filter history 346 typename Traits::output_t fy[Traits::numChannelsIn][2]; 347 StartResonantFilter348 MPT_FORCEINLINE void Start(const ModChannel &chn) 349 { 350 for(int i = 0; i < Traits::numChannelsIn; i++) 351 { 352 fy[i][0] = chn.nFilter_Y[i][0]; 353 fy[i][1] = chn.nFilter_Y[i][1]; 354 } 355 } 356 EndResonantFilter357 MPT_FORCEINLINE void End(ModChannel &chn) 358 { 359 for(int i = 0; i < Traits::numChannelsIn; i++) 360 { 361 chn.nFilter_Y[i][0] = fy[i][0]; 362 chn.nFilter_Y[i][1] = fy[i][1]; 363 } 364 } 365 366 // 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. 367 #define MIXING_FILTER_PREAMP 256 368 // Filter values are clipped to double the input range 369 #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) 370 operatorResonantFilter371 MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) 372 { 373 static_assert(static_cast<int>(Traits::numChannelsIn) <= static_cast<int>(Traits::numChannelsOut), "Too many input channels"); 374 375 for(int i = 0; i < Traits::numChannelsIn; i++) 376 { 377 const auto inputAmp = outSample[i] * MIXING_FILTER_PREAMP; 378 typename Traits::output_t val = static_cast<typename Traits::output_t>(mpt::rshift_signed( 379 Util::mul32to64(inputAmp, chn.nFilter_A0) + 380 Util::mul32to64(ClipFilter(fy[i][0]), chn.nFilter_B0) + 381 Util::mul32to64(ClipFilter(fy[i][1]), chn.nFilter_B1) + 382 (1 << (MIXING_FILTER_PRECISION - 1)), MIXING_FILTER_PRECISION)); 383 fy[i][1] = fy[i][0]; 384 fy[i][0] = val - (inputAmp & chn.nFilter_HP); 385 outSample[i] = val / MIXING_FILTER_PREAMP; 386 } 387 } 388 389 #undef ClipFilter 390 }; 391 392 393 OPENMPT_NAMESPACE_END 394