Distortion.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Distortion.cpp
  3. * --------------
  4. * Purpose: Implementation of the DMO Distortion DSP (for non-Windows platforms)
  5. * Notes : The original plugin's integer and floating point code paths only
  6. * behave identically when feeding floating point numbers in range
  7. * [-32768, +32768] rather than the usual [-1, +1] into the plugin.
  8. * Authors: OpenMPT Devs
  9. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  10. */
  11. #include "stdafx.h"
  12. #ifndef NO_PLUGINS
  13. #include "../../Sndfile.h"
  14. #include "Distortion.h"
  15. #include "DMOUtils.h"
  16. #include "mpt/base/numbers.hpp"
  17. #endif // !NO_PLUGINS
  18. OPENMPT_NAMESPACE_BEGIN
  19. #ifndef NO_PLUGINS
  20. namespace DMO
  21. {
  22. IMixPlugin* Distortion::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  23. {
  24. return new (std::nothrow) Distortion(factory, sndFile, mixStruct);
  25. }
  26. Distortion::Distortion(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  27. : IMixPlugin(factory, sndFile, mixStruct)
  28. {
  29. m_param[kDistGain] = 0.7f;
  30. m_param[kDistEdge] = 0.15f;
  31. m_param[kDistPreLowpassCutoff] = 1.0f;
  32. m_param[kDistPostEQCenterFrequency] = 0.291f;
  33. m_param[kDistPostEQBandwidth] = 0.291f;
  34. m_mixBuffer.Initialize(2, 2);
  35. InsertIntoFactoryList();
  36. }
  37. void Distortion::Process(float *pOutL, float *pOutR, uint32 numFrames)
  38. {
  39. if(!m_mixBuffer.Ok())
  40. return;
  41. const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) };
  42. float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) };
  43. for(uint32 i = numFrames; i != 0; i--)
  44. {
  45. for(uint8 channel = 0; channel < 2; channel++)
  46. {
  47. float x = *(in[channel])++;
  48. // Pre EQ
  49. float z = x * m_preEQa0 + m_preEQz1[channel] * m_preEQb1;
  50. m_preEQz1[channel] = z;
  51. z *= 1073741824.0f; // 32768^2
  52. // The actual distortion
  53. z = logGain(z, m_edge, m_shift);
  54. // Post EQ / Gain
  55. z = (z * m_postEQa0) - m_postEQz1[channel] * m_postEQb1 - m_postEQz2[channel] * m_postEQb0;
  56. m_postEQz1[channel] = z * m_postEQb0 + m_postEQz2[channel];
  57. m_postEQz2[channel] = z;
  58. z *= (1.0f / 1073741824.0f); // 32768^2
  59. *(out[channel])++ = z;
  60. }
  61. }
  62. ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
  63. }
  64. PlugParamValue Distortion::GetParameter(PlugParamIndex index)
  65. {
  66. if(index < kDistNumParameters)
  67. {
  68. return m_param[index];
  69. }
  70. return 0;
  71. }
  72. void Distortion::SetParameter(PlugParamIndex index, PlugParamValue value)
  73. {
  74. if(index < kDistNumParameters)
  75. {
  76. value = mpt::safe_clamp(value, 0.0f, 1.0f);
  77. m_param[index] = value;
  78. RecalculateDistortionParams();
  79. }
  80. }
  81. void Distortion::Resume()
  82. {
  83. m_isResumed = true;
  84. RecalculateDistortionParams();
  85. PositionChanged();
  86. }
  87. void Distortion::PositionChanged()
  88. {
  89. // Reset filter state
  90. m_preEQz1[0] = m_preEQz1[1] = 0;
  91. m_postEQz1[0] = m_postEQz2[0] = 0;
  92. m_postEQz1[1] = m_postEQz2[1] = 0;
  93. }
  94. #ifdef MODPLUG_TRACKER
  95. CString Distortion::GetParamName(PlugParamIndex param)
  96. {
  97. switch(param)
  98. {
  99. case kDistGain: return _T("Gain");
  100. case kDistEdge: return _T("Edge");
  101. case kDistPreLowpassCutoff: return _T("PreLowpassCutoff");
  102. case kDistPostEQCenterFrequency: return _T("PostEQCenterFrequency");
  103. case kDistPostEQBandwidth: return _T("PostEQBandwidth");
  104. }
  105. return CString();
  106. }
  107. CString Distortion::GetParamLabel(PlugParamIndex param)
  108. {
  109. switch(param)
  110. {
  111. case kDistGain:
  112. return _T("dB");
  113. case kDistPreLowpassCutoff:
  114. case kDistPostEQCenterFrequency:
  115. case kDistPostEQBandwidth:
  116. return _T("Hz");
  117. }
  118. return CString();
  119. }
  120. CString Distortion::GetParamDisplay(PlugParamIndex param)
  121. {
  122. float value = m_param[param];
  123. switch(param)
  124. {
  125. case kDistGain:
  126. value = GainInDecibel();
  127. break;
  128. case kDistEdge:
  129. value *= 100.0f;
  130. break;
  131. case kDistPreLowpassCutoff:
  132. case kDistPostEQCenterFrequency:
  133. case kDistPostEQBandwidth:
  134. value = FreqInHertz(value);
  135. break;
  136. }
  137. CString s;
  138. s.Format(_T("%.2f"), value);
  139. return s;
  140. }
  141. #endif // MODPLUG_TRACKER
  142. void Distortion::RecalculateDistortionParams()
  143. {
  144. // Pre-EQ
  145. m_preEQb1 = std::sqrt((2.0f * std::cos(2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPreLowpassCutoff]) / m_SndFile.GetSampleRate(), 0.5f)) + 3.0f) / 5.0f);
  146. m_preEQa0 = std::sqrt(1.0f - m_preEQb1 * m_preEQb1);
  147. // Distortion
  148. float edge = 2.0f + m_param[kDistEdge] * 29.0f;
  149. m_edge = static_cast<uint8>(edge); // 2...31 shifted bits
  150. m_shift = mpt::bit_width(m_edge);
  151. static constexpr float LogNorm[32] =
  152. {
  153. 1.00f, 1.00f, 1.50f, 1.00f, 1.75f, 1.40f, 1.17f, 1.00f,
  154. 1.88f, 1.76f, 1.50f, 1.36f, 1.25f, 1.15f, 1.07f, 1.00f,
  155. 1.94f, 1.82f, 1.72f, 1.63f, 1.55f, 1.48f, 1.41f, 1.35f,
  156. 1.29f, 1.24f, 1.19f, 1.15f, 1.11f, 1.07f, 1.03f, 1.00f,
  157. };
  158. // Post-EQ
  159. const float gain = std::pow(10.0f, GainInDecibel() / 20.0f);
  160. const float postFreq = 2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPostEQCenterFrequency]) / m_SndFile.GetSampleRate(), 0.5f);
  161. const float postBw = 2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPostEQBandwidth]) / m_SndFile.GetSampleRate(), 0.5f);
  162. const float t = std::tan(5.0e-1f * postBw);
  163. m_postEQb1 = ((1.0f - t) / (1.0f + t));
  164. m_postEQb0 = -std::cos(postFreq);
  165. m_postEQa0 = gain * std::sqrt(1.0f - m_postEQb0 * m_postEQb0) * std::sqrt(1.0f - m_postEQb1 * m_postEQb1) * LogNorm[m_edge];
  166. }
  167. } // namespace DMO
  168. #else
  169. MPT_MSVC_WORKAROUND_LNK4221(Distortion)
  170. #endif // !NO_PLUGINS
  171. OPENMPT_NAMESPACE_END