SymMODEcho.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /*
  2. * SymMODEcho.cpp
  3. * --------------
  4. * Purpose: Implementation of the SymMOD Echo DSP
  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 "SymMODEcho.h"
  13. OPENMPT_NAMESPACE_BEGIN
  14. IMixPlugin *SymMODEcho::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  15. {
  16. return new (std::nothrow) SymMODEcho(factory, sndFile, mixStruct);
  17. }
  18. SymMODEcho::SymMODEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  19. : IMixPlugin(factory, sndFile, mixStruct)
  20. , m_chunk(PluginChunk::Default())
  21. {
  22. m_mixBuffer.Initialize(2, 2);
  23. InsertIntoFactoryList();
  24. RecalculateEchoParams();
  25. }
  26. void SymMODEcho::Process(float* pOutL, float* pOutR, uint32 numFrames)
  27. {
  28. const float *srcL = m_mixBuffer.GetInputBuffer(0), *srcR = m_mixBuffer.GetInputBuffer(1);
  29. float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1);
  30. const uint32 delayTime = m_SndFile.m_PlayState.m_nSamplesPerTick * m_chunk.param[kEchoDelay];
  31. // SymMODs don't have a variable tempo so the tick duration should never change... but if someone loads a module into an MPTM file we have to account for this.
  32. if(m_delayLine.size() < delayTime * 2)
  33. m_delayLine.resize(delayTime * 2);
  34. const auto dspType = GetDSPType();
  35. if(dspType == DSPType::Off)
  36. {
  37. // Toggling the echo while it's running keeps its delay line untouched
  38. std::copy(srcL, srcL + numFrames, outL);
  39. std::copy(srcR, srcR + numFrames, outR);
  40. } else
  41. {
  42. for(uint32 i = 0; i < numFrames; i++)
  43. {
  44. if(m_writePos >= delayTime)
  45. m_writePos = 0;
  46. int readPos = m_writePos - delayTime;
  47. if(readPos < 0)
  48. readPos += delayTime;
  49. const float lDry = *srcL++, rDry = *srcR++;
  50. const float lDelay = m_delayLine[readPos * 2], rDelay = m_delayLine[readPos * 2 + 1];
  51. // Output samples
  52. *outL++ = (lDry + lDelay);
  53. *outR++ = (rDry + rDelay);
  54. // Compute new delay line values
  55. float lOut = 0.0f, rOut = 0.0f;
  56. switch(dspType)
  57. {
  58. case DSPType::Off:
  59. break;
  60. case DSPType::Normal: // Normal
  61. lOut = (lDelay + lDry) * m_feedback;
  62. rOut = (rDelay + rDry) * m_feedback;
  63. break;
  64. case DSPType::Cross:
  65. case DSPType::Cross2:
  66. lOut = (rDelay + rDry) * m_feedback;
  67. rOut = (lDelay + lDry) * m_feedback;
  68. break;
  69. case DSPType::Center:
  70. lOut = (lDelay + (lDry + rDry) * 0.5f) * m_feedback;
  71. rOut = lOut;
  72. break;
  73. case DSPType::NumTypes:
  74. break;
  75. }
  76. // Prevent denormals
  77. if(std::abs(lOut) < 1e-24f)
  78. lOut = 0.0f;
  79. if(std::abs(rOut) < 1e-24f)
  80. rOut = 0.0f;
  81. m_delayLine[m_writePos * 2 + 0] = lOut;
  82. m_delayLine[m_writePos * 2 + 1] = rOut;
  83. m_writePos++;
  84. }
  85. }
  86. ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
  87. }
  88. void SymMODEcho::SaveAllParameters()
  89. {
  90. m_pMixStruct->defaultProgram = -1;
  91. try
  92. {
  93. const auto pluginData = mpt::as_raw_memory(m_chunk);
  94. m_pMixStruct->pluginData.assign(pluginData.begin(), pluginData.end());
  95. } catch(mpt::out_of_memory e)
  96. {
  97. mpt::delete_out_of_memory(e);
  98. m_pMixStruct->pluginData.clear();
  99. }
  100. }
  101. void SymMODEcho::RestoreAllParameters(int32 program)
  102. {
  103. if(m_pMixStruct->pluginData.size() == sizeof(m_chunk) && !memcmp(m_pMixStruct->pluginData.data(), "Echo", 4))
  104. {
  105. std::copy(m_pMixStruct->pluginData.begin(), m_pMixStruct->pluginData.end(), mpt::as_raw_memory(m_chunk).begin());
  106. } else
  107. {
  108. IMixPlugin::RestoreAllParameters(program);
  109. }
  110. RecalculateEchoParams();
  111. }
  112. PlugParamValue SymMODEcho::GetParameter(PlugParamIndex index)
  113. {
  114. if(index < kEchoNumParameters)
  115. {
  116. return m_chunk.param[index] / 127.0f;
  117. }
  118. return 0;
  119. }
  120. void SymMODEcho::SetParameter(PlugParamIndex index, PlugParamValue value)
  121. {
  122. if(index < kEchoNumParameters)
  123. {
  124. m_chunk.param[index] = mpt::saturate_round<uint8>(mpt::safe_clamp(value, 0.0f, 1.0f) * 127.0f);
  125. RecalculateEchoParams();
  126. }
  127. }
  128. void SymMODEcho::Resume()
  129. {
  130. m_isResumed = true;
  131. PositionChanged();
  132. }
  133. void SymMODEcho::PositionChanged()
  134. {
  135. try
  136. {
  137. m_delayLine.assign(127 * 2 * m_SndFile.m_PlayState.m_nSamplesPerTick, 0.0f);
  138. } catch(mpt::out_of_memory e)
  139. {
  140. mpt::delete_out_of_memory(e);
  141. }
  142. m_writePos = 0;
  143. }
  144. #ifdef MODPLUG_TRACKER
  145. std::pair<PlugParamValue, PlugParamValue> SymMODEcho::GetParamUIRange(PlugParamIndex param)
  146. {
  147. if(param == kEchoType)
  148. return {0.0f, (static_cast<uint8>(DSPType::NumTypes) - 1) / 127.0f};
  149. else
  150. return {0.0f, 1.0f};
  151. }
  152. CString SymMODEcho::GetParamName(PlugParamIndex param)
  153. {
  154. switch (param)
  155. {
  156. case kEchoType: return _T("Type");
  157. case kEchoDelay: return _T("Delay");
  158. case kEchoFeedback: return _T("Feedback");
  159. case kEchoNumParameters: break;
  160. }
  161. return {};
  162. }
  163. CString SymMODEcho::GetParamLabel(PlugParamIndex param)
  164. {
  165. if(param == kEchoDelay)
  166. return _T("Ticks");
  167. if(param == kEchoFeedback)
  168. return _T("%");
  169. return {};
  170. }
  171. CString SymMODEcho::GetParamDisplay(PlugParamIndex param)
  172. {
  173. switch(static_cast<Parameters>(param))
  174. {
  175. case kEchoType:
  176. switch(GetDSPType())
  177. {
  178. case DSPType::Off: return _T("Off");
  179. case DSPType::Normal: return _T("Normal");
  180. case DSPType::Cross: return _T("Cross");
  181. case DSPType::Cross2: return _T("Cross 2");
  182. case DSPType::Center: return _T("Center");
  183. case DSPType::NumTypes: break;
  184. }
  185. break;
  186. case kEchoDelay:
  187. return mpt::cfmt::val(m_chunk.param[kEchoDelay]);
  188. case kEchoFeedback:
  189. return mpt::cfmt::flt(m_feedback * 100.0f, 4);
  190. case kEchoNumParameters:
  191. break;
  192. }
  193. return {};
  194. }
  195. #endif // MODPLUG_TRACKER
  196. IMixPlugin::ChunkData SymMODEcho::GetChunk(bool)
  197. {
  198. auto data = reinterpret_cast<const std::byte *>(&m_chunk);
  199. return ChunkData(data, sizeof(m_chunk));
  200. }
  201. void SymMODEcho::SetChunk(const ChunkData& chunk, bool)
  202. {
  203. auto data = chunk.data();
  204. if(chunk.size() == sizeof(chunk) && !memcmp(data, "Echo", 4))
  205. {
  206. memcpy(&m_chunk, data, chunk.size());
  207. RecalculateEchoParams();
  208. }
  209. }
  210. void SymMODEcho::RecalculateEchoParams()
  211. {
  212. if(m_chunk.param[kEchoType] >= static_cast<uint8>(DSPType::NumTypes))
  213. m_chunk.param[kEchoType] = 0;
  214. if(m_chunk.param[kEchoDelay] > 127)
  215. m_chunk.param[kEchoDelay] = 127;
  216. if(m_chunk.param[kEchoFeedback] > 127)
  217. m_chunk.param[kEchoFeedback] = 127;
  218. if(GetDSPType() == DSPType::Cross2)
  219. m_feedback = 1.0f - std::pow(2.0f, -static_cast<float>(m_chunk.param[kEchoFeedback] + 1));
  220. else
  221. m_feedback = std::pow(2.0f, -static_cast<float>(m_chunk.param[kEchoFeedback]));
  222. }
  223. OPENMPT_NAMESPACE_END
  224. #endif // NO_PLUGINS