winamp/Src/external_dependencies/openmpt-trunk/soundlib/plugins/dmo/Compressor.cpp
2024-09-24 14:54:57 +02:00

238 lines
5.5 KiB
C++

/*
* Compressor.cpp
* ---------------
* Purpose: Implementation of the DMO Compressor DSP (for non-Windows platforms)
* Notes : The original plugin's integer and floating point code paths only
* behave identically when feeding floating point numbers in range
* [-32768, +32768] rather than the usual [-1, +1] into the plugin.
* 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 "Compressor.h"
#include "DMOUtils.h"
#include "mpt/base/numbers.hpp"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* Compressor::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) Compressor(factory, sndFile, mixStruct);
}
Compressor::Compressor(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
{
m_param[kCompGain] = 0.5f;
m_param[kCompAttack] = 0.02f;
m_param[kCompRelease] = 150.0f / 2950.0f;
m_param[kCompThreshold] = 2.0f / 3.0f;
m_param[kCompRatio] = 0.02f;
m_param[kCompPredelay] = 1.0f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void Compressor::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!m_bufSize || !m_mixBuffer.Ok())
return;
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--)
{
float leftIn = *(in[0])++;
float rightIn = *(in[1])++;
m_buffer[m_bufPos * 2] = leftIn;
m_buffer[m_bufPos * 2 + 1] = rightIn;
leftIn = std::abs(leftIn);
rightIn = std::abs(rightIn);
float mono = (leftIn + rightIn) * (0.5f * 32768.0f * 32768.0f);
float monoLog = std::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31));
float newPeak = monoLog + (m_peak - monoLog) * ((m_peak <= monoLog) ? m_attack : m_release);
m_peak = newPeak;
if(newPeak < m_threshold)
newPeak = m_threshold;
float compGain = (m_threshold - newPeak) * m_ratio + 0.9999999f;
// Computes 2 ^ (2 ^ (log2(x) - 26) - 1) (x = 0...2^31)
uint32 compGainInt = static_cast<uint32>(compGain * 2147483648.0f);
uint32 compGainPow = compGainInt << 5;
compGainInt >>= 26;
if(compGainInt) // compGainInt >= 2^26
{
compGainPow |= 0x80000000u;
compGainInt--;
}
compGainPow >>= (31 - compGainInt);
int32 readOffset = m_predelay + m_bufPos * 4096 + m_bufSize - 1;
readOffset /= 4096;
readOffset %= m_bufSize;
float outGain = (compGainPow * (1.0f / 2147483648.0f)) * m_gain;
*(out[0])++ = m_buffer[readOffset * 2] * outGain;
*(out[1])++ = m_buffer[readOffset * 2 + 1] * outGain;
if(m_bufPos-- == 0)
m_bufPos += m_bufSize;
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue Compressor::GetParameter(PlugParamIndex index)
{
if(index < kCompNumParameters)
{
return m_param[index];
}
return 0;
}
void Compressor::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kCompNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
m_param[index] = value;
RecalculateCompressorParams();
}
}
void Compressor::Resume()
{
m_isResumed = true;
PositionChanged();
RecalculateCompressorParams();
}
void Compressor::PositionChanged()
{
m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 200, 1000);
try
{
m_buffer.assign(m_bufSize * 2, 0.0f);
} catch(mpt::out_of_memory e)
{
mpt::delete_out_of_memory(e);
m_bufSize = 0;
}
m_bufPos = 0;
m_peak = 0.0f;
}
#ifdef MODPLUG_TRACKER
CString Compressor::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kCompGain: return _T("Gain");
case kCompAttack: return _T("Attack");
case kCompRelease: return _T("Release");
case kCompThreshold: return _T("Threshold");
case kCompRatio: return _T("Ratio");
case kCompPredelay: return _T("Predelay");
}
return CString();
}
CString Compressor::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kCompGain:
case kCompThreshold:
return _T("dB");
case kCompAttack:
case kCompRelease:
case kCompPredelay:
return _T("ms");
}
return CString();
}
CString Compressor::GetParamDisplay(PlugParamIndex param)
{
float value = m_param[param];
switch(param)
{
case kCompGain:
value = GainInDecibel();
break;
case kCompAttack:
value = AttackTime();
break;
case kCompRelease:
value = ReleaseTime();
break;
case kCompThreshold:
value = ThresholdInDecibel();
break;
case kCompRatio:
value = CompressorRatio();
break;
case kCompPredelay:
value = PreDelay();
break;
}
CString s;
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
void Compressor::RecalculateCompressorParams()
{
const float sampleRate = m_SndFile.GetSampleRate() / 1000.0f;
m_gain = std::pow(10.0f, GainInDecibel() / 20.0f);
m_attack = std::pow(10.0f, -1.0f / (AttackTime() * sampleRate));
m_release = std::pow(10.0f, -1.0f / (ReleaseTime() * sampleRate));
const float _2e31 = float(1u << 31);
const float _2e26 = float(1u << 26);
m_threshold = std::min((_2e31 - 1.0f), (std::log(std::pow(10.0f, ThresholdInDecibel() / 20.0f) * _2e31) * _2e26) / mpt::numbers::ln2_v<float> + _2e26) * (1.0f / _2e31);
m_ratio = 1.0f - (1.0f / CompressorRatio());
m_predelay = static_cast<int32>((PreDelay() * sampleRate) + 2.0f);
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Compressor)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END