208 lines
4.6 KiB
C++
208 lines
4.6 KiB
C++
|
/*
|
||
|
* Echo.cpp
|
||
|
* --------
|
||
|
* Purpose: Implementation of the DMO Echo DSP (for non-Windows platforms)
|
||
|
* 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 "Echo.h"
|
||
|
#endif // !NO_PLUGINS
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
#ifndef NO_PLUGINS
|
||
|
|
||
|
namespace DMO
|
||
|
{
|
||
|
|
||
|
IMixPlugin* Echo::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
|
||
|
{
|
||
|
return new (std::nothrow) Echo(factory, sndFile, mixStruct);
|
||
|
}
|
||
|
|
||
|
|
||
|
Echo::Echo(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
|
||
|
: IMixPlugin(factory, sndFile, mixStruct)
|
||
|
, m_bufferSize(0)
|
||
|
, m_writePos(0)
|
||
|
, m_sampleRate(sndFile.GetSampleRate())
|
||
|
, m_initialFeedback(0.0f)
|
||
|
{
|
||
|
m_param[kEchoWetDry] = 0.5f;
|
||
|
m_param[kEchoFeedback] = 0.5f;
|
||
|
m_param[kEchoLeftDelay] = (500.0f - 1.0f) / 1999.0f;
|
||
|
m_param[kEchoRightDelay] = (500.0f - 1.0f) / 1999.0f;
|
||
|
m_param[kEchoPanDelay] = 0.0f;
|
||
|
|
||
|
m_mixBuffer.Initialize(2, 2);
|
||
|
InsertIntoFactoryList();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Echo::Process(float *pOutL, float *pOutR, uint32 numFrames)
|
||
|
{
|
||
|
if(!m_bufferSize || !m_mixBuffer.Ok())
|
||
|
return;
|
||
|
const float wetMix = m_param[kEchoWetDry], dryMix = 1 - wetMix;
|
||
|
const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) };
|
||
|
float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) };
|
||
|
|
||
|
for(uint32 i = numFrames; i != 0; i--)
|
||
|
{
|
||
|
for(uint8 channel = 0; channel < 2; channel++)
|
||
|
{
|
||
|
const uint8 readChannel = (m_crossEcho ? (1 - channel) : channel);
|
||
|
int readPos = m_writePos - m_delayTime[readChannel];
|
||
|
if(readPos < 0)
|
||
|
readPos += m_bufferSize;
|
||
|
|
||
|
float chnInput = *(in[channel])++;
|
||
|
float chnDelay = m_delayLine[readPos * 2 + readChannel];
|
||
|
|
||
|
// Calculate the delay
|
||
|
float chnOutput = chnInput * m_initialFeedback;
|
||
|
chnOutput += chnDelay * m_param[kEchoFeedback];
|
||
|
|
||
|
// Prevent denormals
|
||
|
if(std::abs(chnOutput) < 1e-24f)
|
||
|
chnOutput = 0.0f;
|
||
|
|
||
|
m_delayLine[m_writePos * 2 + channel] = chnOutput;
|
||
|
// Output samples now
|
||
|
*(out[channel])++ = (chnInput * dryMix + chnDelay * wetMix);
|
||
|
}
|
||
|
m_writePos++;
|
||
|
if(m_writePos == m_bufferSize)
|
||
|
m_writePos = 0;
|
||
|
}
|
||
|
|
||
|
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
|
||
|
}
|
||
|
|
||
|
|
||
|
PlugParamValue Echo::GetParameter(PlugParamIndex index)
|
||
|
{
|
||
|
if(index < kEchoNumParameters)
|
||
|
{
|
||
|
return m_param[index];
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Echo::SetParameter(PlugParamIndex index, PlugParamValue value)
|
||
|
{
|
||
|
if(index < kEchoNumParameters)
|
||
|
{
|
||
|
value = mpt::safe_clamp(value, 0.0f, 1.0f);
|
||
|
if(index == kEchoPanDelay)
|
||
|
value = mpt::round(value);
|
||
|
m_param[index] = value;
|
||
|
RecalculateEchoParams();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Echo::Resume()
|
||
|
{
|
||
|
m_isResumed = true;
|
||
|
m_sampleRate = m_SndFile.GetSampleRate();
|
||
|
RecalculateEchoParams();
|
||
|
PositionChanged();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Echo::PositionChanged()
|
||
|
{
|
||
|
m_bufferSize = m_sampleRate * 2u;
|
||
|
try
|
||
|
{
|
||
|
m_delayLine.assign(m_bufferSize * 2, 0);
|
||
|
} catch(mpt::out_of_memory e)
|
||
|
{
|
||
|
mpt::delete_out_of_memory(e);
|
||
|
m_bufferSize = 0;
|
||
|
}
|
||
|
m_writePos = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef MODPLUG_TRACKER
|
||
|
|
||
|
CString Echo::GetParamName(PlugParamIndex param)
|
||
|
{
|
||
|
switch(param)
|
||
|
{
|
||
|
case kEchoWetDry: return _T("WetDryMix");
|
||
|
case kEchoFeedback: return _T("Feedback");
|
||
|
case kEchoLeftDelay: return _T("LeftDelay");
|
||
|
case kEchoRightDelay: return _T("RightDelay");
|
||
|
case kEchoPanDelay: return _T("PanDelay");
|
||
|
}
|
||
|
return CString();
|
||
|
}
|
||
|
|
||
|
|
||
|
CString Echo::GetParamLabel(PlugParamIndex param)
|
||
|
{
|
||
|
switch(param)
|
||
|
{
|
||
|
case kEchoFeedback:
|
||
|
return _T("%");
|
||
|
case kEchoLeftDelay:
|
||
|
case kEchoRightDelay:
|
||
|
return _T("ms");
|
||
|
default:
|
||
|
return CString{};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
CString Echo::GetParamDisplay(PlugParamIndex param)
|
||
|
{
|
||
|
CString s;
|
||
|
switch(param)
|
||
|
{
|
||
|
case kEchoWetDry:
|
||
|
s.Format(_T("%.1f : %.1f"), m_param[param] * 100.0f, 100.0f - m_param[param] * 100.0f);
|
||
|
break;
|
||
|
case kEchoFeedback:
|
||
|
s.Format(_T("%.2f"), m_param[param] * 100.0f);
|
||
|
break;
|
||
|
case kEchoLeftDelay:
|
||
|
case kEchoRightDelay:
|
||
|
s.Format(_T("%.2f"), 1.0f + m_param[param] * 1999.0f);
|
||
|
break;
|
||
|
case kEchoPanDelay:
|
||
|
s = (m_param[param] <= 0.5) ? _T("No") : _T("Yes");
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
#endif // MODPLUG_TRACKER
|
||
|
|
||
|
|
||
|
void Echo::RecalculateEchoParams()
|
||
|
{
|
||
|
m_initialFeedback = std::sqrt(1.0f - (m_param[kEchoFeedback] * m_param[kEchoFeedback]));
|
||
|
m_delayTime[0] = static_cast<uint32>((1.0f + m_param[kEchoLeftDelay] * 1999.0f) / 1000.0f * m_sampleRate);
|
||
|
m_delayTime[1] = static_cast<uint32>((1.0f + m_param[kEchoRightDelay] * 1999.0f) / 1000.0f * m_sampleRate);
|
||
|
m_crossEcho = (m_param[kEchoPanDelay]) > 0.5f;
|
||
|
}
|
||
|
|
||
|
} // namespace DMO
|
||
|
|
||
|
#else
|
||
|
MPT_MSVC_WORKAROUND_LNK4221(Echo)
|
||
|
|
||
|
#endif // !NO_PLUGINS
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|