DigiBoosterEcho.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * DigiBoosterEcho.cpp
  3. * -------------------
  4. * Purpose: Implementation of the DigiBooster Pro Echo DSP
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs, based on original code by Grzegorz Kraszewski (BSD 2-clause)
  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 "DigiBoosterEcho.h"
  13. OPENMPT_NAMESPACE_BEGIN
  14. IMixPlugin* DigiBoosterEcho::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  15. {
  16. return new (std::nothrow) DigiBoosterEcho(factory, sndFile, mixStruct);
  17. }
  18. DigiBoosterEcho::DigiBoosterEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  19. : IMixPlugin(factory, sndFile, mixStruct)
  20. , m_sampleRate(sndFile.GetSampleRate())
  21. , m_chunk(PluginChunk::Default())
  22. {
  23. m_mixBuffer.Initialize(2, 2);
  24. InsertIntoFactoryList();
  25. }
  26. void DigiBoosterEcho::Process(float *pOutL, float *pOutR, uint32 numFrames)
  27. {
  28. if(!m_bufferSize)
  29. return;
  30. const float *srcL = m_mixBuffer.GetInputBuffer(0), *srcR = m_mixBuffer.GetInputBuffer(1);
  31. float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1);
  32. for(uint32 i = numFrames; i != 0; i--)
  33. {
  34. int readPos = m_writePos - m_delayTime;
  35. if(readPos < 0)
  36. readPos += m_bufferSize;
  37. float l = *srcL++, r = *srcR++;
  38. float lDelay = m_delayLine[readPos * 2], rDelay = m_delayLine[readPos * 2 + 1];
  39. // Calculate the delay
  40. float al = l * m_NCrossNBack;
  41. al += r * m_PCrossNBack;
  42. al += lDelay * m_NCrossPBack;
  43. al += rDelay * m_PCrossPBack;
  44. float ar = r * m_NCrossNBack;
  45. ar += l * m_PCrossNBack;
  46. ar += rDelay * m_NCrossPBack;
  47. ar += lDelay * m_PCrossPBack;
  48. // Prevent denormals
  49. if(std::abs(al) < 1e-24f)
  50. al = 0.0f;
  51. if(std::abs(ar) < 1e-24f)
  52. ar = 0.0f;
  53. m_delayLine[m_writePos * 2] = al;
  54. m_delayLine[m_writePos * 2 + 1] = ar;
  55. m_writePos++;
  56. if(m_writePos == m_bufferSize)
  57. m_writePos = 0;
  58. // Output samples now
  59. *outL++ = (l * m_NMix + lDelay * m_PMix);
  60. *outR++ = (r * m_NMix + rDelay * m_PMix);
  61. }
  62. ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
  63. }
  64. void DigiBoosterEcho::SaveAllParameters()
  65. {
  66. m_pMixStruct->defaultProgram = -1;
  67. try
  68. {
  69. m_pMixStruct->pluginData.resize(sizeof(m_chunk));
  70. memcpy(m_pMixStruct->pluginData.data(), &m_chunk, sizeof(m_chunk));
  71. } catch(mpt::out_of_memory e)
  72. {
  73. mpt::delete_out_of_memory(e);
  74. m_pMixStruct->pluginData.clear();
  75. }
  76. }
  77. void DigiBoosterEcho::RestoreAllParameters(int32 program)
  78. {
  79. if(m_pMixStruct->pluginData.size() == sizeof(m_chunk) && !memcmp(m_pMixStruct->pluginData.data(), "Echo", 4))
  80. {
  81. memcpy(&m_chunk, m_pMixStruct->pluginData.data(), sizeof(m_chunk));
  82. } else
  83. {
  84. IMixPlugin::RestoreAllParameters(program);
  85. }
  86. RecalculateEchoParams();
  87. }
  88. PlugParamValue DigiBoosterEcho::GetParameter(PlugParamIndex index)
  89. {
  90. if(index < kEchoNumParameters)
  91. {
  92. return m_chunk.param[index] / 255.0f;
  93. }
  94. return 0;
  95. }
  96. void DigiBoosterEcho::SetParameter(PlugParamIndex index, PlugParamValue value)
  97. {
  98. if(index < kEchoNumParameters)
  99. {
  100. m_chunk.param[index] = mpt::saturate_round<uint8>(mpt::safe_clamp(value, 0.0f, 1.0f) * 255.0f);
  101. RecalculateEchoParams();
  102. }
  103. }
  104. void DigiBoosterEcho::Resume()
  105. {
  106. m_isResumed = true;
  107. m_sampleRate = m_SndFile.GetSampleRate();
  108. RecalculateEchoParams();
  109. PositionChanged();
  110. }
  111. void DigiBoosterEcho::PositionChanged()
  112. {
  113. m_bufferSize = (m_sampleRate >> 1) + (m_sampleRate >> 6);
  114. try
  115. {
  116. m_delayLine.assign(m_bufferSize * 2, 0);
  117. } catch(mpt::out_of_memory e)
  118. {
  119. mpt::delete_out_of_memory(e);
  120. m_bufferSize = 0;
  121. }
  122. m_writePos = 0;
  123. }
  124. #ifdef MODPLUG_TRACKER
  125. CString DigiBoosterEcho::GetParamName(PlugParamIndex param)
  126. {
  127. switch(param)
  128. {
  129. case kEchoDelay: return _T("Delay");
  130. case kEchoFeedback: return _T("Feedback");
  131. case kEchoMix: return _T("Wet / Dry Ratio");
  132. case kEchoCross: return _T("Cross Echo");
  133. }
  134. return CString();
  135. }
  136. CString DigiBoosterEcho::GetParamLabel(PlugParamIndex param)
  137. {
  138. if(param == kEchoDelay)
  139. return _T("ms");
  140. return CString();
  141. }
  142. CString DigiBoosterEcho::GetParamDisplay(PlugParamIndex param)
  143. {
  144. CString s;
  145. if(param == kEchoMix)
  146. {
  147. int wet = (m_chunk.param[kEchoMix] * 100) / 255;
  148. s.Format(_T("%d%% / %d%%"), wet, 100 - wet);
  149. } else if(param < kEchoNumParameters)
  150. {
  151. int val = m_chunk.param[param];
  152. if(param == kEchoDelay)
  153. {
  154. if(val == 0)
  155. val = 167;
  156. val *= 2;
  157. }
  158. s.Format(_T("%d"), val);
  159. }
  160. return s;
  161. }
  162. #endif // MODPLUG_TRACKER
  163. IMixPlugin::ChunkData DigiBoosterEcho::GetChunk(bool)
  164. {
  165. auto data = reinterpret_cast<const std::byte *>(&m_chunk);
  166. return ChunkData(data, sizeof(m_chunk));
  167. }
  168. void DigiBoosterEcho::SetChunk(const ChunkData &chunk, bool)
  169. {
  170. auto data = chunk.data();
  171. if(chunk.size() == sizeof(chunk) && !memcmp(data, "Echo", 4))
  172. {
  173. memcpy(&m_chunk, data, chunk.size());
  174. RecalculateEchoParams();
  175. }
  176. }
  177. void DigiBoosterEcho::RecalculateEchoParams()
  178. {
  179. // The fallback value when the delay is 0 was determined experimentally from DBPro 2.21 output.
  180. // The C implementation of libdigibooster3 has no specific handling of this value and thus produces a delay with maximum length.
  181. m_delayTime = ((m_chunk.param[kEchoDelay] ? m_chunk.param[kEchoDelay] : 167u) * m_sampleRate + 250u) / 500u;
  182. m_PMix = (m_chunk.param[kEchoMix]) * (1.0f / 256.0f);
  183. m_NMix = (256 - m_chunk.param[kEchoMix]) * (1.0f / 256.0f);
  184. m_PCrossPBack = (m_chunk.param[kEchoCross] * m_chunk.param[kEchoFeedback]) * (1.0f / 65536.0f);
  185. m_PCrossNBack = (m_chunk.param[kEchoCross] * (256 - m_chunk.param[kEchoFeedback])) * (1.0f / 65536.0f);
  186. m_NCrossPBack = ((m_chunk.param[kEchoCross] - 256) * m_chunk.param[kEchoFeedback]) * (1.0f / 65536.0f);
  187. m_NCrossNBack = ((m_chunk.param[kEchoCross] - 256) * (m_chunk.param[kEchoFeedback] - 256)) * (1.0f / 65536.0f);
  188. }
  189. OPENMPT_NAMESPACE_END
  190. #endif // NO_PLUGINS