1
0

Snd_flt.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * Snd_flt.cpp
  3. * -----------
  4. * Purpose: Calculation of resonant filter coefficients.
  5. * Notes : Extended filter range was introduced in MPT 1.12 and went up to 8652 Hz.
  6. * MPT 1.16 upped this to the current 10670 Hz.
  7. * We have no way of telling whether a file was made with MPT 1.12 or 1.16 though.
  8. * Authors: Olivier Lapicque
  9. * OpenMPT Devs
  10. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  11. */
  12. #include "stdafx.h"
  13. #include "Sndfile.h"
  14. #include "Tables.h"
  15. #include "../common/misc_util.h"
  16. #include "mpt/base/numbers.hpp"
  17. OPENMPT_NAMESPACE_BEGIN
  18. // AWE32: cutoff = reg[0-255] * 31.25 + 100 -> [100Hz-8060Hz]
  19. // EMU10K1 docs: cutoff = reg[0-127]*62+100
  20. uint8 CSoundFile::FrequencyToCutOff(double frequency) const
  21. {
  22. // IT Cutoff is computed as cutoff = 110 * 2 ^ (0.25 + x/y), where x is the cutoff and y defines the filter range.
  23. // Reversed, this gives us x = (log2(cutoff / 110) - 0.25) * y.
  24. // <==========> Rewrite as x = (log2(cutoff) - log2(110) - 0.25) * y.
  25. // <==========> Rewrite as x = (ln(cutoff) - ln(110) - 0.25*ln(2)) * y/ln(2).
  26. // <4.8737671609324025>
  27. double cutoff = (std::log(frequency) - 4.8737671609324025) * (m_SongFlags[SONG_EXFILTERRANGE] ? (20.0 / mpt::numbers::ln2) : (24.0 / mpt::numbers::ln2));
  28. Limit(cutoff, 0.0, 127.0);
  29. return mpt::saturate_round<uint8>(cutoff);
  30. }
  31. uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int envModifier) const
  32. {
  33. MPT_ASSERT(nCutOff < 128);
  34. float computedCutoff = static_cast<float>(nCutOff * (envModifier + 256)); // 0...127*512
  35. float Fc;
  36. if(GetType() != MOD_TYPE_IMF)
  37. {
  38. Fc = 110.0f * std::pow(2.0f, 0.25f + computedCutoff / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f));
  39. } else
  40. {
  41. // EMU8000: Documentation says the cutoff is in quarter semitones, with 0x00 being 125 Hz and 0xFF being 8 kHz
  42. // The first half of the sentence contradicts the second, though.
  43. Fc = 125.0f * std::pow(2.0f, computedCutoff * 6.0f / (127.0f * 512.0f));
  44. }
  45. int freq = mpt::saturate_round<int>(Fc);
  46. Limit(freq, 120, 20000);
  47. if(freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2;
  48. return static_cast<uint32>(freq);
  49. }
  50. // Simple 2-poles resonant filter. Returns computed cutoff in range [0, 254] or -1 if filter is not applied.
  51. int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier) const
  52. {
  53. int cutoff = static_cast<int>(chn.nCutOff) + chn.nCutSwing;
  54. int resonance = static_cast<int>(chn.nResonance & 0x7F) + chn.nResSwing;
  55. Limit(cutoff, 0, 127);
  56. Limit(resonance, 0, 127);
  57. if(!m_playBehaviour[kMPTOldSwingBehaviour])
  58. {
  59. chn.nCutOff = (uint8)cutoff;
  60. chn.nCutSwing = 0;
  61. chn.nResonance = (uint8)resonance;
  62. chn.nResSwing = 0;
  63. }
  64. // envModifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation.
  65. const int computedCutoff = cutoff * (envModifier + 256) / 256;
  66. // Filtering is only ever done in IT if either cutoff is not full or if resonance is set.
  67. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254)
  68. {
  69. if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn && chn.triggerNote)
  70. {
  71. // Z7F next to a note disables the filter, however in other cases this should not happen.
  72. // Test cases: filter-reset.it, filter-reset-carry.it, filter-reset-envelope.it, filter-nna.it, FilterResetPatDelay.it
  73. chn.dwFlags.reset(CHN_FILTER);
  74. }
  75. return -1;
  76. }
  77. chn.dwFlags.set(CHN_FILTER);
  78. // 2 * damping factor
  79. const float dmpfac = std::pow(10.0f, -resonance * ((24.0f / 128.0f) / 20.0f));
  80. const float fc = CutOffToFrequency(cutoff, envModifier) * (2.0f * mpt::numbers::pi_v<float>);
  81. float d, e;
  82. if(m_playBehaviour[kITFilterBehaviour] && !m_SongFlags[SONG_EXFILTERRANGE])
  83. {
  84. const float r = m_MixerSettings.gdwMixingFreq / fc;
  85. d = dmpfac * r + dmpfac - 1.0f;
  86. e = r * r;
  87. } else
  88. {
  89. const float r = fc / m_MixerSettings.gdwMixingFreq;
  90. d = (1.0f - 2.0f * dmpfac) * r;
  91. LimitMax(d, 2.0f);
  92. d = (2.0f * dmpfac - d) / r;
  93. e = 1.0f / (r * r);
  94. }
  95. float fg = 1.0f / (1.0f + d + e);
  96. float fb0 = (d + e + e) / (1 + d + e);
  97. float fb1 = -e / (1.0f + d + e);
  98. #if defined(MPT_INTMIXER)
  99. #define MPT_FILTER_CONVERT(x) mpt::saturate_round<mixsample_t>((x) * (1 << MIXING_FILTER_PRECISION))
  100. #else
  101. #define MPT_FILTER_CONVERT(x) (x)
  102. #endif
  103. switch(chn.nFilterMode)
  104. {
  105. case FilterMode::HighPass:
  106. chn.nFilter_A0 = MPT_FILTER_CONVERT(1.0f - fg);
  107. chn.nFilter_B0 = MPT_FILTER_CONVERT(fb0);
  108. chn.nFilter_B1 = MPT_FILTER_CONVERT(fb1);
  109. #ifdef MPT_INTMIXER
  110. chn.nFilter_HP = -1;
  111. #else
  112. chn.nFilter_HP = 1.0f;
  113. #endif // MPT_INTMIXER
  114. break;
  115. default:
  116. chn.nFilter_A0 = MPT_FILTER_CONVERT(fg);
  117. chn.nFilter_B0 = MPT_FILTER_CONVERT(fb0);
  118. chn.nFilter_B1 = MPT_FILTER_CONVERT(fb1);
  119. #ifdef MPT_INTMIXER
  120. if(chn.nFilter_A0 == 0)
  121. chn.nFilter_A0 = 1; // Prevent silence at low filter cutoff and very high sampling rate
  122. chn.nFilter_HP = 0;
  123. #else
  124. chn.nFilter_HP = 0;
  125. #endif // MPT_INTMIXER
  126. break;
  127. }
  128. #undef MPT_FILTER_CONVERT
  129. if (bReset)
  130. {
  131. chn.nFilter_Y[0][0] = chn.nFilter_Y[0][1] = 0;
  132. chn.nFilter_Y[1][0] = chn.nFilter_Y[1][1] = 0;
  133. }
  134. return computedCutoff;
  135. }
  136. OPENMPT_NAMESPACE_END