/* * 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(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 + _2e26) * (1.0f / _2e31); m_ratio = 1.0f - (1.0f / CompressorRatio()); m_predelay = static_cast((PreDelay() * sampleRate) + 2.0f); } } // namespace DMO #else MPT_MSVC_WORKAROUND_LNK4221(Compressor) #endif // !NO_PLUGINS OPENMPT_NAMESPACE_END