winamp/Src/external_dependencies/openmpt-trunk/soundlib/plugins/SymMODEcho.cpp

272 lines
6.4 KiB
C++
Raw Normal View History

2024-09-24 12:54:57 +00:00
/*
* 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