Chorus.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * Chorus.cpp
  3. * ----------
  4. * Purpose: Implementation of the DMO Chorus DSP (for non-Windows platforms)
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #ifndef NO_PLUGINS
  11. #include "../../Sndfile.h"
  12. #include "Chorus.h"
  13. #include "mpt/base/numbers.hpp"
  14. #endif // !NO_PLUGINS
  15. OPENMPT_NAMESPACE_BEGIN
  16. #ifndef NO_PLUGINS
  17. namespace DMO
  18. {
  19. IMixPlugin* Chorus::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  20. {
  21. return new (std::nothrow) Chorus(factory, sndFile, mixStruct);
  22. }
  23. Chorus::Chorus(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, bool isFlanger)
  24. : IMixPlugin(factory, sndFile, mixStruct)
  25. , m_isFlanger(isFlanger)
  26. {
  27. m_param[kChorusWetDryMix] = 0.5f;
  28. m_param[kChorusDepth] = 0.1f;
  29. m_param[kChorusFrequency] = 0.11f;
  30. m_param[kChorusWaveShape] = 1.0f;
  31. m_param[kChorusPhase] = 0.75f;
  32. m_param[kChorusFeedback] = (25.0f + 99.0f) / 198.0f;
  33. m_param[kChorusDelay] = 0.8f;
  34. m_mixBuffer.Initialize(2, 2);
  35. InsertIntoFactoryList();
  36. }
  37. // Integer part of buffer position
  38. int32 Chorus::GetBufferIntOffset(int32 fpOffset) const
  39. {
  40. if(fpOffset < 0)
  41. fpOffset += m_bufSize * 4096;
  42. MPT_ASSERT(fpOffset >= 0);
  43. return (fpOffset / 4096) % m_bufSize;
  44. }
  45. void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames)
  46. {
  47. if(!m_bufSize || !m_mixBuffer.Ok())
  48. return;
  49. const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) };
  50. float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) };
  51. const bool isTriangle = IsTriangle();
  52. const float feedback = Feedback() / 100.0f;
  53. const float wetDryMix = WetDryMix();
  54. const uint32 phase = Phase();
  55. const auto &bufferR = m_isFlanger ? m_bufferR : m_bufferL;
  56. for(uint32 i = numFrames; i != 0; i--)
  57. {
  58. const float leftIn = *(in[0])++;
  59. const float rightIn = *(in[1])++;
  60. const int32 readOffset = GetBufferIntOffset(m_bufPos + m_delayOffset);
  61. const int32 writeOffset = GetBufferIntOffset(m_bufPos);
  62. if(m_isFlanger)
  63. {
  64. m_DryBufferL[m_dryWritePos] = leftIn;
  65. m_DryBufferR[m_dryWritePos] = rightIn;
  66. m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + leftIn;
  67. m_bufferR[writeOffset] = (m_bufferR[readOffset] * feedback) + rightIn;
  68. } else
  69. {
  70. m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + (leftIn + rightIn) * 0.5f;
  71. }
  72. float waveMin;
  73. float waveMax;
  74. if(isTriangle)
  75. {
  76. m_waveShapeMin += m_waveShapeVal;
  77. m_waveShapeMax += m_waveShapeVal;
  78. if(m_waveShapeMin > 1)
  79. m_waveShapeMin -= 2;
  80. if(m_waveShapeMax > 1)
  81. m_waveShapeMax -= 2;
  82. waveMin = std::abs(m_waveShapeMin) * 2 - 1;
  83. waveMax = std::abs(m_waveShapeMax) * 2 - 1;
  84. } else
  85. {
  86. m_waveShapeMin = m_waveShapeMax * m_waveShapeVal + m_waveShapeMin;
  87. m_waveShapeMax = m_waveShapeMax - m_waveShapeMin * m_waveShapeVal;
  88. waveMin = m_waveShapeMin;
  89. waveMax = m_waveShapeMax;
  90. }
  91. const float leftDelayIn = m_isFlanger ? m_DryBufferL[(m_dryWritePos + 2) % 3] : leftIn;
  92. const float rightDelayIn = m_isFlanger ? m_DryBufferR[(m_dryWritePos + 2) % 3] : rightIn;
  93. float left1 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL)];
  94. float left2 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL + 4096)];
  95. float fracPos = (m_delayL & 0xFFF) * (1.0f / 4096.0f);
  96. float leftOut = (left2 - left1) * fracPos + left1;
  97. *(out[0])++ = leftDelayIn + (leftOut - leftDelayIn) * wetDryMix;
  98. float right1 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR)];
  99. float right2 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR + 4096)];
  100. fracPos = (m_delayR & 0xFFF) * (1.0f / 4096.0f);
  101. float rightOut = (right2 - right1) * fracPos + right1;
  102. *(out[1])++ = rightDelayIn + (rightOut - rightDelayIn) * wetDryMix;
  103. // Increment delay positions
  104. if(m_dryWritePos <= 0)
  105. m_dryWritePos += 3;
  106. m_dryWritePos--;
  107. m_delayL = m_delayOffset + (phase < 4 ? 1 : -1) * static_cast<int32>(waveMin * m_depthDelay);
  108. m_delayR = m_delayOffset + (phase < 2 ? -1 : 1) * static_cast<int32>(((phase % 2u) ? waveMax : waveMin) * m_depthDelay);
  109. if(m_bufPos <= 0)
  110. m_bufPos += m_bufSize * 4096;
  111. m_bufPos -= 4096;
  112. }
  113. ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
  114. }
  115. PlugParamValue Chorus::GetParameter(PlugParamIndex index)
  116. {
  117. if(index < kChorusNumParameters)
  118. {
  119. return m_param[index];
  120. }
  121. return 0;
  122. }
  123. void Chorus::SetParameter(PlugParamIndex index, PlugParamValue value)
  124. {
  125. if(index < kChorusNumParameters)
  126. {
  127. value = mpt::safe_clamp(value, 0.0f, 1.0f);
  128. if(index == kChorusWaveShape)
  129. {
  130. value = mpt::round(value);
  131. if(m_param[index] != value)
  132. {
  133. m_waveShapeMin = 0.0f;
  134. m_waveShapeMax = 0.5f + value * 0.5f;
  135. }
  136. } else if(index == kChorusPhase)
  137. {
  138. value = mpt::round(value * 4.0f) / 4.0f;
  139. }
  140. m_param[index] = value;
  141. RecalculateChorusParams();
  142. }
  143. }
  144. void Chorus::Resume()
  145. {
  146. PositionChanged();
  147. RecalculateChorusParams();
  148. m_isResumed = true;
  149. m_waveShapeMin = 0.0f;
  150. m_waveShapeMax = IsTriangle() ? 0.5f : 1.0f;
  151. m_delayL = m_delayR = m_delayOffset;
  152. m_bufPos = 0;
  153. m_dryWritePos = 0;
  154. }
  155. void Chorus::PositionChanged()
  156. {
  157. m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 3840, 1000);
  158. try
  159. {
  160. m_bufferL.assign(m_bufSize, 0.0f);
  161. if(m_isFlanger)
  162. m_bufferR.assign(m_bufSize, 0.0f);
  163. m_DryBufferL.fill(0.0f);
  164. m_DryBufferR.fill(0.0f);
  165. } catch(mpt::out_of_memory e)
  166. {
  167. mpt::delete_out_of_memory(e);
  168. m_bufSize = 0;
  169. }
  170. }
  171. #ifdef MODPLUG_TRACKER
  172. CString Chorus::GetParamName(PlugParamIndex param)
  173. {
  174. switch(param)
  175. {
  176. case kChorusWetDryMix: return _T("WetDryMix");
  177. case kChorusDepth: return _T("Depth");
  178. case kChorusFrequency: return _T("Frequency");
  179. case kChorusWaveShape: return _T("WaveShape");
  180. case kChorusPhase: return _T("Phase");
  181. case kChorusFeedback: return _T("Feedback");
  182. case kChorusDelay: return _T("Delay");
  183. }
  184. return CString();
  185. }
  186. CString Chorus::GetParamLabel(PlugParamIndex param)
  187. {
  188. switch(param)
  189. {
  190. case kChorusWetDryMix:
  191. case kChorusDepth:
  192. case kChorusFeedback:
  193. return _T("%");
  194. case kChorusFrequency:
  195. return _T("Hz");
  196. case kChorusPhase:
  197. return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN
  198. case kChorusDelay:
  199. return _T("ms");
  200. }
  201. return CString();
  202. }
  203. CString Chorus::GetParamDisplay(PlugParamIndex param)
  204. {
  205. CString s;
  206. float value = m_param[param];
  207. switch(param)
  208. {
  209. case kChorusWetDryMix:
  210. case kChorusDepth:
  211. value *= 100.0f;
  212. break;
  213. case kChorusFrequency:
  214. value = FrequencyInHertz();
  215. break;
  216. case kChorusWaveShape:
  217. return (value < 1) ? _T("Triangle") : _T("Sine");
  218. break;
  219. case kChorusPhase:
  220. switch(Phase())
  221. {
  222. case 0: return _T("-180");
  223. case 1: return _T("-90");
  224. case 2: return _T("0");
  225. case 3: return _T("90");
  226. case 4: return _T("180");
  227. }
  228. break;
  229. case kChorusFeedback:
  230. value = Feedback();
  231. break;
  232. case kChorusDelay:
  233. value = Delay();
  234. }
  235. s.Format(_T("%.2f"), value);
  236. return s;
  237. }
  238. #endif // MODPLUG_TRACKER
  239. void Chorus::RecalculateChorusParams()
  240. {
  241. const float sampleRate = static_cast<float>(m_SndFile.GetSampleRate());
  242. float delaySamples = Delay() * sampleRate / 1000.0f;
  243. m_depthDelay = Depth() * delaySamples * 2048.0f;
  244. m_delayOffset = mpt::saturate_round<int32>(4096.0f * (delaySamples + 2.0f));
  245. m_frequency = FrequencyInHertz();
  246. const float frequencySamples = m_frequency / sampleRate;
  247. if(IsTriangle())
  248. m_waveShapeVal = frequencySamples * 2.0f;
  249. else
  250. m_waveShapeVal = std::sin(frequencySamples * mpt::numbers::pi_v<float>) * 2.0f;
  251. }
  252. } // namespace DMO
  253. #else
  254. MPT_MSVC_WORKAROUND_LNK4221(Chorus)
  255. #endif // !NO_PLUGINS
  256. OPENMPT_NAMESPACE_END