123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /*
- * SymMODEcho.cpp
- * --------------
- * Purpose: Implementation of the SymMOD Echo DSP
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #ifndef NO_PLUGINS
- #include "../Sndfile.h"
- #include "SymMODEcho.h"
- OPENMPT_NAMESPACE_BEGIN
- IMixPlugin *SymMODEcho::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
- {
- return new (std::nothrow) SymMODEcho(factory, sndFile, mixStruct);
- }
- SymMODEcho::SymMODEcho(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
- : IMixPlugin(factory, sndFile, mixStruct)
- , m_chunk(PluginChunk::Default())
- {
- m_mixBuffer.Initialize(2, 2);
- InsertIntoFactoryList();
- RecalculateEchoParams();
- }
- void SymMODEcho::Process(float* pOutL, float* pOutR, uint32 numFrames)
- {
- const float *srcL = m_mixBuffer.GetInputBuffer(0), *srcR = m_mixBuffer.GetInputBuffer(1);
- float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1);
- const uint32 delayTime = m_SndFile.m_PlayState.m_nSamplesPerTick * m_chunk.param[kEchoDelay];
- // 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.
- if(m_delayLine.size() < delayTime * 2)
- m_delayLine.resize(delayTime * 2);
- const auto dspType = GetDSPType();
- if(dspType == DSPType::Off)
- {
- // Toggling the echo while it's running keeps its delay line untouched
- std::copy(srcL, srcL + numFrames, outL);
- std::copy(srcR, srcR + numFrames, outR);
- } else
- {
- for(uint32 i = 0; i < numFrames; i++)
- {
- if(m_writePos >= delayTime)
- m_writePos = 0;
- int readPos = m_writePos - delayTime;
- if(readPos < 0)
- readPos += delayTime;
- const float lDry = *srcL++, rDry = *srcR++;
- const float lDelay = m_delayLine[readPos * 2], rDelay = m_delayLine[readPos * 2 + 1];
- // Output samples
- *outL++ = (lDry + lDelay);
- *outR++ = (rDry + rDelay);
- // Compute new delay line values
- float lOut = 0.0f, rOut = 0.0f;
- switch(dspType)
- {
- case DSPType::Off:
- break;
- case DSPType::Normal: // Normal
- lOut = (lDelay + lDry) * m_feedback;
- rOut = (rDelay + rDry) * m_feedback;
- break;
- case DSPType::Cross:
- case DSPType::Cross2:
- lOut = (rDelay + rDry) * m_feedback;
- rOut = (lDelay + lDry) * m_feedback;
- break;
- case DSPType::Center:
- lOut = (lDelay + (lDry + rDry) * 0.5f) * m_feedback;
- rOut = lOut;
- break;
- case DSPType::NumTypes:
- break;
- }
- // Prevent denormals
- if(std::abs(lOut) < 1e-24f)
- lOut = 0.0f;
- if(std::abs(rOut) < 1e-24f)
- rOut = 0.0f;
- m_delayLine[m_writePos * 2 + 0] = lOut;
- m_delayLine[m_writePos * 2 + 1] = rOut;
- m_writePos++;
- }
- }
- ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
- }
- void SymMODEcho::SaveAllParameters()
- {
- m_pMixStruct->defaultProgram = -1;
- try
- {
- const auto pluginData = mpt::as_raw_memory(m_chunk);
- m_pMixStruct->pluginData.assign(pluginData.begin(), pluginData.end());
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- m_pMixStruct->pluginData.clear();
- }
- }
- void SymMODEcho::RestoreAllParameters(int32 program)
- {
- if(m_pMixStruct->pluginData.size() == sizeof(m_chunk) && !memcmp(m_pMixStruct->pluginData.data(), "Echo", 4))
- {
- std::copy(m_pMixStruct->pluginData.begin(), m_pMixStruct->pluginData.end(), mpt::as_raw_memory(m_chunk).begin());
- } else
- {
- IMixPlugin::RestoreAllParameters(program);
- }
- RecalculateEchoParams();
- }
- PlugParamValue SymMODEcho::GetParameter(PlugParamIndex index)
- {
- if(index < kEchoNumParameters)
- {
- return m_chunk.param[index] / 127.0f;
- }
- return 0;
- }
- void SymMODEcho::SetParameter(PlugParamIndex index, PlugParamValue value)
- {
- if(index < kEchoNumParameters)
- {
- m_chunk.param[index] = mpt::saturate_round<uint8>(mpt::safe_clamp(value, 0.0f, 1.0f) * 127.0f);
- RecalculateEchoParams();
- }
- }
- void SymMODEcho::Resume()
- {
- m_isResumed = true;
- PositionChanged();
- }
- void SymMODEcho::PositionChanged()
- {
- try
- {
- m_delayLine.assign(127 * 2 * m_SndFile.m_PlayState.m_nSamplesPerTick, 0.0f);
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- }
- m_writePos = 0;
- }
- #ifdef MODPLUG_TRACKER
- std::pair<PlugParamValue, PlugParamValue> SymMODEcho::GetParamUIRange(PlugParamIndex param)
- {
- if(param == kEchoType)
- return {0.0f, (static_cast<uint8>(DSPType::NumTypes) - 1) / 127.0f};
- else
- return {0.0f, 1.0f};
- }
- CString SymMODEcho::GetParamName(PlugParamIndex param)
- {
- switch (param)
- {
- case kEchoType: return _T("Type");
- case kEchoDelay: return _T("Delay");
- case kEchoFeedback: return _T("Feedback");
- case kEchoNumParameters: break;
- }
- return {};
- }
- CString SymMODEcho::GetParamLabel(PlugParamIndex param)
- {
- if(param == kEchoDelay)
- return _T("Ticks");
- if(param == kEchoFeedback)
- return _T("%");
- return {};
- }
- CString SymMODEcho::GetParamDisplay(PlugParamIndex param)
- {
- switch(static_cast<Parameters>(param))
- {
- case kEchoType:
- switch(GetDSPType())
- {
- case DSPType::Off: return _T("Off");
- case DSPType::Normal: return _T("Normal");
- case DSPType::Cross: return _T("Cross");
- case DSPType::Cross2: return _T("Cross 2");
- case DSPType::Center: return _T("Center");
- case DSPType::NumTypes: break;
- }
- break;
- case kEchoDelay:
- return mpt::cfmt::val(m_chunk.param[kEchoDelay]);
- case kEchoFeedback:
- return mpt::cfmt::flt(m_feedback * 100.0f, 4);
- case kEchoNumParameters:
- break;
- }
- return {};
- }
- #endif // MODPLUG_TRACKER
- IMixPlugin::ChunkData SymMODEcho::GetChunk(bool)
- {
- auto data = reinterpret_cast<const std::byte *>(&m_chunk);
- return ChunkData(data, sizeof(m_chunk));
- }
- void SymMODEcho::SetChunk(const ChunkData& chunk, bool)
- {
- auto data = chunk.data();
- if(chunk.size() == sizeof(chunk) && !memcmp(data, "Echo", 4))
- {
- memcpy(&m_chunk, data, chunk.size());
- RecalculateEchoParams();
- }
- }
- void SymMODEcho::RecalculateEchoParams()
- {
- if(m_chunk.param[kEchoType] >= static_cast<uint8>(DSPType::NumTypes))
- m_chunk.param[kEchoType] = 0;
- if(m_chunk.param[kEchoDelay] > 127)
- m_chunk.param[kEchoDelay] = 127;
- if(m_chunk.param[kEchoFeedback] > 127)
- m_chunk.param[kEchoFeedback] = 127;
- if(GetDSPType() == DSPType::Cross2)
- m_feedback = 1.0f - std::pow(2.0f, -static_cast<float>(m_chunk.param[kEchoFeedback] + 1));
- else
- m_feedback = std::pow(2.0f, -static_cast<float>(m_chunk.param[kEchoFeedback]));
- }
- OPENMPT_NAMESPACE_END
- #endif // NO_PLUGINS
|