Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,306 @@
/*
* Chorus.cpp
* ----------
* Purpose: Implementation of the DMO Chorus 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 "Chorus.h"
#include "mpt/base/numbers.hpp"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* Chorus::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) Chorus(factory, sndFile, mixStruct);
}
Chorus::Chorus(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, bool isFlanger)
: IMixPlugin(factory, sndFile, mixStruct)
, m_isFlanger(isFlanger)
{
m_param[kChorusWetDryMix] = 0.5f;
m_param[kChorusDepth] = 0.1f;
m_param[kChorusFrequency] = 0.11f;
m_param[kChorusWaveShape] = 1.0f;
m_param[kChorusPhase] = 0.75f;
m_param[kChorusFeedback] = (25.0f + 99.0f) / 198.0f;
m_param[kChorusDelay] = 0.8f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
// Integer part of buffer position
int32 Chorus::GetBufferIntOffset(int32 fpOffset) const
{
if(fpOffset < 0)
fpOffset += m_bufSize * 4096;
MPT_ASSERT(fpOffset >= 0);
return (fpOffset / 4096) % m_bufSize;
}
void Chorus::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) };
const bool isTriangle = IsTriangle();
const float feedback = Feedback() / 100.0f;
const float wetDryMix = WetDryMix();
const uint32 phase = Phase();
const auto &bufferR = m_isFlanger ? m_bufferR : m_bufferL;
for(uint32 i = numFrames; i != 0; i--)
{
const float leftIn = *(in[0])++;
const float rightIn = *(in[1])++;
const int32 readOffset = GetBufferIntOffset(m_bufPos + m_delayOffset);
const int32 writeOffset = GetBufferIntOffset(m_bufPos);
if(m_isFlanger)
{
m_DryBufferL[m_dryWritePos] = leftIn;
m_DryBufferR[m_dryWritePos] = rightIn;
m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + leftIn;
m_bufferR[writeOffset] = (m_bufferR[readOffset] * feedback) + rightIn;
} else
{
m_bufferL[writeOffset] = (m_bufferL[readOffset] * feedback) + (leftIn + rightIn) * 0.5f;
}
float waveMin;
float waveMax;
if(isTriangle)
{
m_waveShapeMin += m_waveShapeVal;
m_waveShapeMax += m_waveShapeVal;
if(m_waveShapeMin > 1)
m_waveShapeMin -= 2;
if(m_waveShapeMax > 1)
m_waveShapeMax -= 2;
waveMin = std::abs(m_waveShapeMin) * 2 - 1;
waveMax = std::abs(m_waveShapeMax) * 2 - 1;
} else
{
m_waveShapeMin = m_waveShapeMax * m_waveShapeVal + m_waveShapeMin;
m_waveShapeMax = m_waveShapeMax - m_waveShapeMin * m_waveShapeVal;
waveMin = m_waveShapeMin;
waveMax = m_waveShapeMax;
}
const float leftDelayIn = m_isFlanger ? m_DryBufferL[(m_dryWritePos + 2) % 3] : leftIn;
const float rightDelayIn = m_isFlanger ? m_DryBufferR[(m_dryWritePos + 2) % 3] : rightIn;
float left1 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL)];
float left2 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL + 4096)];
float fracPos = (m_delayL & 0xFFF) * (1.0f / 4096.0f);
float leftOut = (left2 - left1) * fracPos + left1;
*(out[0])++ = leftDelayIn + (leftOut - leftDelayIn) * wetDryMix;
float right1 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR)];
float right2 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR + 4096)];
fracPos = (m_delayR & 0xFFF) * (1.0f / 4096.0f);
float rightOut = (right2 - right1) * fracPos + right1;
*(out[1])++ = rightDelayIn + (rightOut - rightDelayIn) * wetDryMix;
// Increment delay positions
if(m_dryWritePos <= 0)
m_dryWritePos += 3;
m_dryWritePos--;
m_delayL = m_delayOffset + (phase < 4 ? 1 : -1) * static_cast<int32>(waveMin * m_depthDelay);
m_delayR = m_delayOffset + (phase < 2 ? -1 : 1) * static_cast<int32>(((phase % 2u) ? waveMax : waveMin) * m_depthDelay);
if(m_bufPos <= 0)
m_bufPos += m_bufSize * 4096;
m_bufPos -= 4096;
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue Chorus::GetParameter(PlugParamIndex index)
{
if(index < kChorusNumParameters)
{
return m_param[index];
}
return 0;
}
void Chorus::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kChorusNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
if(index == kChorusWaveShape)
{
value = mpt::round(value);
if(m_param[index] != value)
{
m_waveShapeMin = 0.0f;
m_waveShapeMax = 0.5f + value * 0.5f;
}
} else if(index == kChorusPhase)
{
value = mpt::round(value * 4.0f) / 4.0f;
}
m_param[index] = value;
RecalculateChorusParams();
}
}
void Chorus::Resume()
{
PositionChanged();
RecalculateChorusParams();
m_isResumed = true;
m_waveShapeMin = 0.0f;
m_waveShapeMax = IsTriangle() ? 0.5f : 1.0f;
m_delayL = m_delayR = m_delayOffset;
m_bufPos = 0;
m_dryWritePos = 0;
}
void Chorus::PositionChanged()
{
m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 3840, 1000);
try
{
m_bufferL.assign(m_bufSize, 0.0f);
if(m_isFlanger)
m_bufferR.assign(m_bufSize, 0.0f);
m_DryBufferL.fill(0.0f);
m_DryBufferR.fill(0.0f);
} catch(mpt::out_of_memory e)
{
mpt::delete_out_of_memory(e);
m_bufSize = 0;
}
}
#ifdef MODPLUG_TRACKER
CString Chorus::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kChorusWetDryMix: return _T("WetDryMix");
case kChorusDepth: return _T("Depth");
case kChorusFrequency: return _T("Frequency");
case kChorusWaveShape: return _T("WaveShape");
case kChorusPhase: return _T("Phase");
case kChorusFeedback: return _T("Feedback");
case kChorusDelay: return _T("Delay");
}
return CString();
}
CString Chorus::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kChorusWetDryMix:
case kChorusDepth:
case kChorusFeedback:
return _T("%");
case kChorusFrequency:
return _T("Hz");
case kChorusPhase:
return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN
case kChorusDelay:
return _T("ms");
}
return CString();
}
CString Chorus::GetParamDisplay(PlugParamIndex param)
{
CString s;
float value = m_param[param];
switch(param)
{
case kChorusWetDryMix:
case kChorusDepth:
value *= 100.0f;
break;
case kChorusFrequency:
value = FrequencyInHertz();
break;
case kChorusWaveShape:
return (value < 1) ? _T("Triangle") : _T("Sine");
break;
case kChorusPhase:
switch(Phase())
{
case 0: return _T("-180");
case 1: return _T("-90");
case 2: return _T("0");
case 3: return _T("90");
case 4: return _T("180");
}
break;
case kChorusFeedback:
value = Feedback();
break;
case kChorusDelay:
value = Delay();
}
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
void Chorus::RecalculateChorusParams()
{
const float sampleRate = static_cast<float>(m_SndFile.GetSampleRate());
float delaySamples = Delay() * sampleRate / 1000.0f;
m_depthDelay = Depth() * delaySamples * 2048.0f;
m_delayOffset = mpt::saturate_round<int32>(4096.0f * (delaySamples + 2.0f));
m_frequency = FrequencyInHertz();
const float frequencySamples = m_frequency / sampleRate;
if(IsTriangle())
m_waveShapeVal = frequencySamples * 2.0f;
else
m_waveShapeVal = std::sin(frequencySamples * mpt::numbers::pi_v<float>) * 2.0f;
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Chorus)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,122 @@
/*
* Chorus.h
* --------
* Purpose: Implementation of the DMO Chorus 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.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Chorus : public IMixPlugin
{
protected:
enum Parameters
{
kChorusWetDryMix = 0,
kChorusDepth,
kChorusFrequency,
kChorusWaveShape,
kChorusPhase,
kChorusFeedback,
kChorusDelay,
kChorusNumParameters
};
std::array<float, kChorusNumParameters> m_param;
// Calculated parameters
float m_waveShapeMin, m_waveShapeMax, m_waveShapeVal;
float m_depthDelay;
float m_frequency;
int32 m_delayOffset;
const bool m_isFlanger = false;
// State
std::vector<float> m_bufferL, m_bufferR; // Only m_bufferL is used in case of !m_isFlanger
std::array<float, 3> m_DryBufferL, m_DryBufferR;
int32 m_bufPos = 0, m_bufSize = 0;
int32 m_delayL = 0, m_delayR = 0;
int32 m_dryWritePos = 0;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
Chorus(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, bool stereoBuffers = false);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEFE6629C; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kChorusNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Chorus"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
void BeginSetProgram(int32) override { }
void EndSetProgram() override { }
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
int32 GetBufferIntOffset(int32 fpOffset) const;
virtual float WetDryMix() const { return m_param[kChorusWetDryMix]; }
virtual bool IsTriangle() const { return m_param[kChorusWaveShape] < 1; }
virtual float Depth() const { return m_param[kChorusDepth]; }
virtual float Feedback() const { return -99.0f + m_param[kChorusFeedback] * 198.0f; }
virtual float Delay() const { return m_param[kChorusDelay] * 20.0f; }
virtual float FrequencyInHertz() const { return m_param[kChorusFrequency] * 10.0f; }
virtual int Phase() const { return mpt::saturate_round<uint32>(m_param[kChorusPhase] * 4.0f); }
void RecalculateChorusParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,238 @@
/*
* 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

View file

@ -0,0 +1,109 @@
/*
* Compressor.h
* -------------
* Purpose: Implementation of the DMO Compressor 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Compressor final : public IMixPlugin
{
protected:
enum Parameters
{
kCompGain = 0,
kCompAttack,
kCompRelease,
kCompThreshold,
kCompRatio,
kCompPredelay,
kCompNumParameters
};
std::array<float, kCompNumParameters> m_param;
// Calculated parameters and coefficients
float m_gain;
float m_attack;
float m_release;
float m_threshold;
float m_ratio;
int32 m_predelay;
// State
std::vector<float> m_buffer;
int32 m_bufPos, m_bufSize;
float m_peak;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
Compressor(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEF011F79; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kCompNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Compressor"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
float GainInDecibel() const { return -60.0f + m_param[kCompGain] * 120.0f; }
float AttackTime() const { return 0.01f + m_param[kCompAttack] * 499.99f; }
float ReleaseTime() const { return 50.0f + m_param[kCompRelease] * 2950.0f; }
float ThresholdInDecibel() const { return -60.0f + m_param[kCompThreshold] * 60.0f; }
float CompressorRatio() const { return 1.0f + m_param[kCompRatio] * 99.0f; }
float PreDelay() const { return m_param[kCompPredelay] * 4.0f; }
void RecalculateCompressorParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,431 @@
/*
* DMOPlugin.h
* -----------
* Purpose: DirectX Media Object plugin handling / processing.
* Notes : Some default plugins only have the same output characteristics in the floating point code path (compared to integer PCM)
* if we feed them input in the range [-32768, +32768] rather than the more usual [-1, +1].
* Hence, OpenMPT uses this range for both the floating-point and integer path.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mpt/base/aligned_array.hpp"
#if defined(MPT_WITH_DMO)
#include "mpt/uuid/guid.hpp"
#include "../../Sndfile.h"
#include "DMOPlugin.h"
#include "../PluginManager.h"
#include <uuids.h>
#include <medparam.h>
#include <mmsystem.h>
#endif // MPT_WITH_DMO
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_WITH_DMO)
#ifdef MPT_ALL_LOGGING
#define DMO_LOG
#else
#define DMO_LOG
#endif
IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
CLSID clsid;
if(mpt::VerifyStringToCLSID(factory.dllPath.AsNative(), clsid))
{
IMediaObject *pMO = nullptr;
IMediaObjectInPlace *pMOIP = nullptr;
if ((CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IMediaObject, (VOID **)&pMO) == S_OK) && (pMO))
{
if (pMO->QueryInterface(IID_IMediaObjectInPlace, (void **)&pMOIP) != S_OK) pMOIP = nullptr;
} else pMO = nullptr;
if ((pMO) && (pMOIP))
{
DWORD dwInputs = 0, dwOutputs = 0;
pMO->GetStreamCount(&dwInputs, &dwOutputs);
if (dwInputs == 1 && dwOutputs == 1)
{
DMOPlugin *p = new (std::nothrow) DMOPlugin(factory, sndFile, mixStruct, pMO, pMOIP, clsid.Data1);
return p;
}
#ifdef DMO_LOG
MPT_LOG_GLOBAL(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Unable to use this DMO"));
#endif
}
#ifdef DMO_LOG
else MPT_LOG_GLOBAL(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces"));
#endif
if (pMO) pMO->Release();
if (pMOIP) pMOIP->Release();
}
return nullptr;
}
DMOPlugin::DMOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, IMediaObject *pMO, IMediaObjectInPlace *pMOIP, uint32 uid)
: IMixPlugin(factory, sndFile, mixStruct)
, m_pMediaObject(pMO)
, m_pMediaProcess(pMOIP)
, m_pParamInfo(nullptr)
, m_pMediaParams(nullptr)
, m_nSamplesPerSec(sndFile.GetSampleRate())
, m_uid(uid)
{
if(FAILED(m_pMediaObject->QueryInterface(IID_IMediaParamInfo, (void **)&m_pParamInfo)))
m_pParamInfo = nullptr;
if (FAILED(m_pMediaObject->QueryInterface(IID_IMediaParams, (void **)&m_pMediaParams)))
m_pMediaParams = nullptr;
m_alignedBuffer.f32 = mpt::align_bytes<16, MIXBUFFERSIZE * 2>(m_interleavedBuffer.f32);
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
DMOPlugin::~DMOPlugin()
{
if(m_pMediaParams)
{
m_pMediaParams->Release();
m_pMediaParams = nullptr;
}
if(m_pParamInfo)
{
m_pParamInfo->Release();
m_pParamInfo = nullptr;
}
if(m_pMediaProcess)
{
m_pMediaProcess->Release();
m_pMediaProcess = nullptr;
}
if(m_pMediaObject)
{
m_pMediaObject->Release();
m_pMediaObject = nullptr;
}
}
uint32 DMOPlugin::GetLatency() const
{
REFERENCE_TIME time; // Unit 100-nanoseconds
if(m_pMediaProcess->GetLatency(&time) == S_OK)
{
return static_cast<uint32>(time * m_nSamplesPerSec / (10 * 1000 * 1000));
}
return 0;
}
static constexpr float _f2si = 32768.0f;
static constexpr float _si2f = 1.0f / 32768.0f;
static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, float * MPT_RESTRICT output, uint32 numFrames)
{
while(numFrames--)
{
*(output++) = *(inputL++) * _f2si;
*(output++) = *(inputR++) * _f2si;
}
}
static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames)
{
while(numFrames--)
{
*(outputL++) = *(input++) * _si2f;
*(outputR++) = *(input++) * _si2f;
}
}
// Interleave two float streams into one int16 stereo stream.
static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, int16 * MPT_RESTRICT output, uint32 numFrames)
{
while(numFrames--)
{
*(output++) = static_cast<int16>(Clamp(*(inputL++) * _f2si, static_cast<float>(int16_min), static_cast<float>(int16_max)));
*(output++) = static_cast<int16>(Clamp(*(inputR++) * _f2si, static_cast<float>(int16_min), static_cast<float>(int16_max)));
}
}
// Deinterleave an int16 stereo stream into two float streams.
static void DeinterleaveInt16ToFloat(const int16 * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames)
{
while(numFrames--)
{
*outputL++ += _si2f * static_cast<float>(*input++);
*outputR++ += _si2f * static_cast<float>(*input++);
}
}
void DMOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!numFrames || !m_mixBuffer.Ok())
return;
m_mixBuffer.ClearOutputBuffers(numFrames);
REFERENCE_TIME startTime = Util::muldiv(m_SndFile.GetTotalSampleCount(), 10000000, m_nSamplesPerSec);
if(m_useFloat)
{
InterleaveStereo(m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), m_alignedBuffer.f32, numFrames);
m_pMediaProcess->Process(numFrames * 2 * sizeof(float), reinterpret_cast<BYTE *>(m_alignedBuffer.f32), startTime, DMO_INPLACE_NORMAL);
DeinterleaveStereo(m_alignedBuffer.f32, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
} else
{
InterleaveFloatToInt16(m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), m_alignedBuffer.i16, numFrames);
m_pMediaProcess->Process(numFrames * 2 * sizeof(int16), reinterpret_cast<BYTE *>(m_alignedBuffer.i16), startTime, DMO_INPLACE_NORMAL);
DeinterleaveInt16ToFloat(m_alignedBuffer.i16, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamIndex DMOPlugin::GetNumParameters() const
{
DWORD dwParamCount = 0;
m_pParamInfo->GetParamCount(&dwParamCount);
return dwParamCount;
}
PlugParamValue DMOPlugin::GetParameter(PlugParamIndex index)
{
if(index < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr)
{
MP_PARAMINFO mpi;
MP_DATA md;
MemsetZero(mpi);
md = 0;
if (m_pParamInfo->GetParamInfo(index, &mpi) == S_OK
&& m_pMediaParams->GetParam(index, &md) == S_OK)
{
float fValue, fMin, fMax, fDefault;
fValue = md;
fMin = mpi.mpdMinValue;
fMax = mpi.mpdMaxValue;
fDefault = mpi.mpdNeutralValue;
if (mpi.mpType == MPT_BOOL)
{
fMin = 0;
fMax = 1;
}
fValue -= fMin;
if (fMax > fMin) fValue /= (fMax - fMin);
return fValue;
}
}
return 0;
}
void DMOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr)
{
MP_PARAMINFO mpi;
MemsetZero(mpi);
if (m_pParamInfo->GetParamInfo(index, &mpi) == S_OK)
{
float fMin = mpi.mpdMinValue;
float fMax = mpi.mpdMaxValue;
if (mpi.mpType == MPT_BOOL)
{
fMin = 0;
fMax = 1;
value = (value > 0.5f) ? 1.0f : 0.0f;
}
if (fMax > fMin) value *= (fMax - fMin);
value += fMin;
value = mpt::safe_clamp(value, fMin, fMax);
if (mpi.mpType != MPT_FLOAT) value = mpt::round(value);
m_pMediaParams->SetParam(index, value);
}
}
}
void DMOPlugin::Resume()
{
m_nSamplesPerSec = m_SndFile.GetSampleRate();
m_isResumed = true;
DMO_MEDIA_TYPE mt;
WAVEFORMATEX wfx;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.formattype = FORMAT_WaveFormatEx;
mt.pUnk = nullptr;
mt.pbFormat = (LPBYTE)&wfx;
mt.cbFormat = sizeof(WAVEFORMATEX);
mt.lSampleSize = 2 * sizeof(float);
wfx.wFormatTag = 3; // WAVE_FORMAT_IEEE_FLOAT;
wfx.nChannels = 2;
wfx.nSamplesPerSec = m_nSamplesPerSec;
wfx.wBitsPerSample = sizeof(float) * 8;
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
// First try 32-bit float (DirectX 9+)
m_useFloat = true;
if(FAILED(m_pMediaObject->SetInputType(0, &mt, 0))
|| FAILED(m_pMediaObject->SetOutputType(0, &mt, 0)))
{
m_useFloat = false;
// Try again with 16-bit PCM
mt.lSampleSize = 2 * sizeof(int16);
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.wBitsPerSample = sizeof(int16) * 8;
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
if(FAILED(m_pMediaObject->SetInputType(0, &mt, 0))
|| FAILED(m_pMediaObject->SetOutputType(0, &mt, 0)))
{
#ifdef DMO_LOG
MPT_LOG_GLOBAL(LogDebug, "DMO", U_("DMO: Failed to set I/O media type"));
#endif
}
}
}
void DMOPlugin::PositionChanged()
{
m_pMediaObject->Discontinuity(0);
m_pMediaObject->Flush();
}
void DMOPlugin::Suspend()
{
m_isResumed = false;
m_pMediaObject->Flush();
m_pMediaObject->SetInputType(0, nullptr, DMO_SET_TYPEF_CLEAR);
m_pMediaObject->SetOutputType(0, nullptr, DMO_SET_TYPEF_CLEAR);
}
#ifdef MODPLUG_TRACKER
CString DMOPlugin::GetParamName(PlugParamIndex param)
{
if(param < GetNumParameters() && m_pParamInfo != nullptr)
{
MP_PARAMINFO mpi;
mpi.mpType = MPT_INT;
mpi.szUnitText[0] = 0;
mpi.szLabel[0] = 0;
if(m_pParamInfo->GetParamInfo(param, &mpi) == S_OK)
{
return mpt::ToCString(mpi.szLabel);
}
}
return CString();
}
CString DMOPlugin::GetParamLabel(PlugParamIndex param)
{
if(param < GetNumParameters() && m_pParamInfo != nullptr)
{
MP_PARAMINFO mpi;
mpi.mpType = MPT_INT;
mpi.szUnitText[0] = 0;
mpi.szLabel[0] = 0;
if(m_pParamInfo->GetParamInfo(param, &mpi) == S_OK)
{
return mpt::ToCString(mpi.szUnitText);
}
}
return CString();
}
CString DMOPlugin::GetParamDisplay(PlugParamIndex param)
{
if(param < GetNumParameters() && m_pParamInfo != nullptr && m_pMediaParams != nullptr)
{
MP_PARAMINFO mpi;
mpi.mpType = MPT_INT;
mpi.szUnitText[0] = 0;
mpi.szLabel[0] = 0;
if (m_pParamInfo->GetParamInfo(param, &mpi) == S_OK)
{
MP_DATA md;
if(m_pMediaParams->GetParam(param, &md) == S_OK)
{
switch(mpi.mpType)
{
case MPT_FLOAT:
{
CString s;
s.Format(_T("%.2f"), md);
return s;
}
break;
case MPT_BOOL:
return ((int)md) ? _T("Yes") : _T("No");
break;
case MPT_ENUM:
{
WCHAR *text = nullptr;
m_pParamInfo->GetParamText(param, &text);
const int nValue = mpt::saturate_round<int>(md * (mpi.mpdMaxValue - mpi.mpdMinValue));
// Always skip first two strings (param name, unit name)
for(int i = 0; i < nValue + 2; i++)
{
text += wcslen(text) + 1;
}
return mpt::ToCString(text);
}
break;
case MPT_INT:
default:
{
CString s;
s.Format(_T("%d"), mpt::saturate_round<int>(md));
return s;
}
break;
}
}
}
}
return CString();
}
#endif // MODPLUG_TRACKER
#else // !MPT_WITH_DMO
MPT_MSVC_WORKAROUND_LNK4221(DMOPlugin)
#endif // MPT_WITH_DMO
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,100 @@
/*
* DMOPlugin.h
* -----------
* Purpose: DirectX Media Object plugin handling / processing.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#if defined(MPT_WITH_DMO)
#include "../PlugInterface.h"
#include <dmoreg.h>
#include <strmif.h>
typedef interface IMediaObject IMediaObject;
typedef interface IMediaObjectInPlace IMediaObjectInPlace;
typedef interface IMediaParamInfo IMediaParamInfo;
typedef interface IMediaParams IMediaParams;
OPENMPT_NAMESPACE_BEGIN
class DMOPlugin final : public IMixPlugin
{
protected:
IMediaObject *m_pMediaObject;
IMediaObjectInPlace *m_pMediaProcess;
IMediaParamInfo *m_pParamInfo;
IMediaParams *m_pMediaParams;
uint32 m_nSamplesPerSec;
const uint32 m_uid;
union
{
int16 *i16;
float *f32;
} m_alignedBuffer;
union
{
int16 i16[MIXBUFFERSIZE * 2 + 16]; // 16-bit PCM Stereo interleaved
float f32[MIXBUFFERSIZE * 2 + 16]; // 32-bit Float Stereo interleaved
} m_interleavedBuffer;
bool m_useFloat;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
protected:
DMOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, IMediaObject *pMO, IMediaObjectInPlace *pMOIP, uint32 uid);
~DMOPlugin();
public:
void Release() override { delete this; }
int32 GetUID() const override { return m_uid; }
int32 GetVersion() const override { return 2; }
void Idle() override { }
uint32 GetLatency() const override;
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32 /*nIndex*/) override { }
PlugParamIndex GetNumParameters() const override;
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override;
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return CString(); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex param) override;
CString GetParamDisplay(PlugParamIndex param) override;
// TODO we could simply add our own preset mechanism. But is it really useful for these plugins?
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
};
OPENMPT_NAMESPACE_END
#endif // MPT_WITH_DMO

View file

@ -0,0 +1,59 @@
/*
* DMOUtils.cpp
* ------------
* Purpose: Utility functions shared by DMO plugins
* Notes : none
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "DMOUtils.h"
#ifndef NO_PLUGINS
#include "../../Sndfile.h"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
// Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31)
float logGain(float x, int32 shiftL, int32 shiftR)
{
uint32 intSample = static_cast<uint32>(static_cast<int64>(x));
const uint32 sign = intSample & 0x80000000;
if(sign)
intSample = (~intSample) + 1;
// Multiply until overflow (or edge shift factor is reached)
while(shiftL > 0 && intSample < 0x80000000)
{
intSample += intSample;
shiftL--;
}
// Unsign clipped sample
if(intSample >= 0x80000000)
{
intSample &= 0x7FFFFFFF;
shiftL++;
}
intSample = (shiftL << (31 - shiftR)) | (intSample >> shiftR);
if(sign)
intSample = ~intSample | sign;
return static_cast<float>(static_cast<int32>(intSample));
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Distortion)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,26 @@
/*
* DMOUtils.h
* ----------
* Purpose: Utility functions shared by DMO plugins
* Notes : none
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
// Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31)
float logGain(float x, int32 shiftL, int32 shiftR);
}
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,216 @@
/*
* Distortion.cpp
* --------------
* Purpose: Implementation of the DMO Distortion 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 "Distortion.h"
#include "DMOUtils.h"
#include "mpt/base/numbers.hpp"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* Distortion::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) Distortion(factory, sndFile, mixStruct);
}
Distortion::Distortion(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
{
m_param[kDistGain] = 0.7f;
m_param[kDistEdge] = 0.15f;
m_param[kDistPreLowpassCutoff] = 1.0f;
m_param[kDistPostEQCenterFrequency] = 0.291f;
m_param[kDistPostEQBandwidth] = 0.291f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void Distortion::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!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--)
{
for(uint8 channel = 0; channel < 2; channel++)
{
float x = *(in[channel])++;
// Pre EQ
float z = x * m_preEQa0 + m_preEQz1[channel] * m_preEQb1;
m_preEQz1[channel] = z;
z *= 1073741824.0f; // 32768^2
// The actual distortion
z = logGain(z, m_edge, m_shift);
// Post EQ / Gain
z = (z * m_postEQa0) - m_postEQz1[channel] * m_postEQb1 - m_postEQz2[channel] * m_postEQb0;
m_postEQz1[channel] = z * m_postEQb0 + m_postEQz2[channel];
m_postEQz2[channel] = z;
z *= (1.0f / 1073741824.0f); // 32768^2
*(out[channel])++ = z;
}
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue Distortion::GetParameter(PlugParamIndex index)
{
if(index < kDistNumParameters)
{
return m_param[index];
}
return 0;
}
void Distortion::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kDistNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
m_param[index] = value;
RecalculateDistortionParams();
}
}
void Distortion::Resume()
{
m_isResumed = true;
RecalculateDistortionParams();
PositionChanged();
}
void Distortion::PositionChanged()
{
// Reset filter state
m_preEQz1[0] = m_preEQz1[1] = 0;
m_postEQz1[0] = m_postEQz2[0] = 0;
m_postEQz1[1] = m_postEQz2[1] = 0;
}
#ifdef MODPLUG_TRACKER
CString Distortion::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kDistGain: return _T("Gain");
case kDistEdge: return _T("Edge");
case kDistPreLowpassCutoff: return _T("PreLowpassCutoff");
case kDistPostEQCenterFrequency: return _T("PostEQCenterFrequency");
case kDistPostEQBandwidth: return _T("PostEQBandwidth");
}
return CString();
}
CString Distortion::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kDistGain:
return _T("dB");
case kDistPreLowpassCutoff:
case kDistPostEQCenterFrequency:
case kDistPostEQBandwidth:
return _T("Hz");
}
return CString();
}
CString Distortion::GetParamDisplay(PlugParamIndex param)
{
float value = m_param[param];
switch(param)
{
case kDistGain:
value = GainInDecibel();
break;
case kDistEdge:
value *= 100.0f;
break;
case kDistPreLowpassCutoff:
case kDistPostEQCenterFrequency:
case kDistPostEQBandwidth:
value = FreqInHertz(value);
break;
}
CString s;
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
void Distortion::RecalculateDistortionParams()
{
// Pre-EQ
m_preEQb1 = std::sqrt((2.0f * std::cos(2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPreLowpassCutoff]) / m_SndFile.GetSampleRate(), 0.5f)) + 3.0f) / 5.0f);
m_preEQa0 = std::sqrt(1.0f - m_preEQb1 * m_preEQb1);
// Distortion
float edge = 2.0f + m_param[kDistEdge] * 29.0f;
m_edge = static_cast<uint8>(edge); // 2...31 shifted bits
m_shift = mpt::bit_width(m_edge);
static constexpr float LogNorm[32] =
{
1.00f, 1.00f, 1.50f, 1.00f, 1.75f, 1.40f, 1.17f, 1.00f,
1.88f, 1.76f, 1.50f, 1.36f, 1.25f, 1.15f, 1.07f, 1.00f,
1.94f, 1.82f, 1.72f, 1.63f, 1.55f, 1.48f, 1.41f, 1.35f,
1.29f, 1.24f, 1.19f, 1.15f, 1.11f, 1.07f, 1.03f, 1.00f,
};
// Post-EQ
const float gain = std::pow(10.0f, GainInDecibel() / 20.0f);
const float postFreq = 2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPostEQCenterFrequency]) / m_SndFile.GetSampleRate(), 0.5f);
const float postBw = 2.0f * mpt::numbers::pi_v<float> * std::min(FreqInHertz(m_param[kDistPostEQBandwidth]) / m_SndFile.GetSampleRate(), 0.5f);
const float t = std::tan(5.0e-1f * postBw);
m_postEQb1 = ((1.0f - t) / (1.0f + t));
m_postEQb0 = -std::cos(postFreq);
m_postEQa0 = gain * std::sqrt(1.0f - m_postEQb0 * m_postEQb0) * std::sqrt(1.0f - m_postEQb1 * m_postEQb1) * LogNorm[m_edge];
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Distortion)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,97 @@
/*
* Distortion.h
* ------------
* Purpose: Implementation of the DMO Distortion 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Distortion final : public IMixPlugin
{
protected:
enum Parameters
{
kDistGain = 0,
kDistEdge,
kDistPreLowpassCutoff,
kDistPostEQCenterFrequency,
kDistPostEQBandwidth,
kDistNumParameters
};
std::array<float, kDistNumParameters> m_param;
// Pre-EQ coefficients
float m_preEQz1[2], m_preEQb1, m_preEQa0;
// Post-EQ coefficients
float m_postEQz1[2], m_postEQz2[2], m_postEQa0, m_postEQb0, m_postEQb1;
uint8 m_edge, m_shift;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
Distortion(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEF114C90; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kDistNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Distortion"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
static float FreqInHertz(float param) { return 100.0f + param * 7900.0f; }
float GainInDecibel() const { return -60.0f + m_param[kDistGain] * 60.0f; }
void RecalculateDistortionParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,207 @@
/*
* 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

View file

@ -0,0 +1,99 @@
/*
* Echo.h
* ------
* 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Echo final : public IMixPlugin
{
protected:
enum Parameters
{
kEchoWetDry = 0,
kEchoFeedback,
kEchoLeftDelay,
kEchoRightDelay,
kEchoPanDelay,
kEchoNumParameters
};
std::vector<float> m_delayLine; // Echo delay line
float m_param[kEchoNumParameters];
uint32 m_bufferSize; // Delay line length in frames
uint32 m_writePos; // Current write position in the delay line
uint32 m_delayTime[2]; // In frames
uint32 m_sampleRate;
// Echo calculation coefficients
float m_initialFeedback;
bool m_crossEcho;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
Echo(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEF3E932C; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames)override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kEchoNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Echo"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
void RecalculateEchoParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,158 @@
/*
* Flanger.cpp
* -----------
* Purpose: Implementation of the DMO Flanger 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 "Flanger.h"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* Flanger::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) Flanger(factory, sndFile, mixStruct, false);
}
IMixPlugin* Flanger::CreateLegacy(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new(std::nothrow) Flanger(factory, sndFile, mixStruct, true);
}
Flanger::Flanger(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, const bool legacy)
: Chorus(factory, sndFile, mixStruct, !legacy)
{
m_param[kFlangerWetDryMix] = 0.5f;
m_param[kFlangerWaveShape] = 1.0f;
m_param[kFlangerFrequency] = 0.025f;
m_param[kFlangerDepth] = 1.0f;
m_param[kFlangerPhase] = 0.5f;
m_param[kFlangerFeedback] = (-50.0f + 99.0f) / 198.0f;
m_param[kFlangerDelay] = 0.5f;
// Already done in Chorus constructor
//m_mixBuffer.Initialize(2, 2);
//InsertIntoFactoryList();
}
void Flanger::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kFlangerNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
if(index == kFlangerWaveShape)
{
value = mpt::round(value);
if(m_param[index] != value)
{
m_waveShapeMin = 0.0f;
m_waveShapeMax = 0.5f + value * 0.5f;
}
} else if(index == kFlangerPhase)
{
value = mpt::round(value * 4.0f) / 4.0f;
}
m_param[index] = value;
RecalculateChorusParams();
}
}
#ifdef MODPLUG_TRACKER
CString Flanger::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kFlangerWetDryMix: return _T("WetDryMix");
case kFlangerWaveShape: return _T("WaveShape");
case kFlangerFrequency: return _T("Frequency");
case kFlangerDepth: return _T("Depth");
case kFlangerPhase: return _T("Phase");
case kFlangerFeedback: return _T("Feedback");
case kFlangerDelay: return _T("Delay");
}
return CString();
}
CString Flanger::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kFlangerWetDryMix:
case kFlangerDepth:
case kFlangerFeedback:
return _T("%");
case kFlangerFrequency:
return _T("Hz");
case kFlangerPhase:
return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN
case kFlangerDelay:
return _T("ms");
}
return CString();
}
CString Flanger::GetParamDisplay(PlugParamIndex param)
{
CString s;
float value = m_param[param];
switch(param)
{
case kFlangerWetDryMix:
case kFlangerDepth:
value *= 100.0f;
break;
case kFlangerFrequency:
value = FrequencyInHertz();
break;
case kFlangerWaveShape:
return (value < 1) ? _T("Triangle") : _T("Sine");
break;
case kFlangerPhase:
switch(Phase())
{
case 0: return _T("-180");
case 1: return _T("-90");
case 2: return _T("0");
case 3: return _T("90");
case 4: return _T("180");
}
break;
case kFlangerFeedback:
value = Feedback();
break;
case kFlangerDelay:
value = Delay();
}
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Flanger)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,72 @@
/*
* Flanger.h
* ---------
* Purpose: Implementation of the DMO Flanger 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.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#ifndef NO_PLUGINS
#include "Chorus.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Flanger final : public Chorus
{
protected:
enum Parameters
{
kFlangerWetDryMix = 0,
kFlangerWaveShape,
kFlangerFrequency,
kFlangerDepth,
kFlangerPhase,
kFlangerFeedback,
kFlangerDelay,
kFlangerNumParameters
};
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
static IMixPlugin* CreateLegacy(VSTPluginLib& factory, CSoundFile& sndFile, SNDMIXPLUGIN* mixStruct);
Flanger(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct, const bool legacy);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEFCA3D92; }
PlugParamIndex GetNumParameters() const override { return kFlangerNumParameters; }
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Flanger"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
#endif
protected:
float WetDryMix() const override { return m_param[kFlangerWetDryMix]; }
bool IsTriangle() const override { return m_param[kFlangerWaveShape] < 1; }
float Depth() const override { return m_param[kFlangerDepth]; }
float Feedback() const override { return -99.0f + m_param[kFlangerFeedback] * 198.0f; }
float Delay() const override { return m_param[kFlangerDelay] * 4.0f; }
float FrequencyInHertz() const override { return m_param[kFlangerFrequency] * 10.0f; }
int Phase() const override { return mpt::saturate_round<uint32>(m_param[kFlangerPhase] * 4.0f); }
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,202 @@
/*
* Gargle.cpp
* ----------
* Purpose: Implementation of the DMO Gargle 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 "Gargle.h"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* Gargle::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) Gargle(factory, sndFile, mixStruct);
}
Gargle::Gargle(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
{
m_param[kGargleRate] = 0.02f;
m_param[kGargleWaveShape] = 0.0f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void Gargle::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!m_mixBuffer.Ok())
return;
const float *inL = m_mixBuffer.GetInputBuffer(0), *inR = m_mixBuffer.GetInputBuffer(1);
float *outL = m_mixBuffer.GetOutputBuffer(0), *outR = m_mixBuffer.GetOutputBuffer(1);
const bool triangle = m_param[kGargleWaveShape] < 1.0f;
for(uint32 frame = numFrames; frame != 0;)
{
if(m_counter < m_periodHalf)
{
// First half of gargle period
const uint32 remain = std::min(frame, m_periodHalf - m_counter);
if(triangle)
{
const uint32 stop = m_counter + remain;
const float factor = 1.0f / m_periodHalf;
for(uint32 i = m_counter; i < stop; i++)
{
*outL++ = *inL++ * i * factor;
*outR++ = *inR++ * i * factor;
}
} else
{
for(uint32 i = 0; i < remain; i++)
{
*outL++ = *inL++;
*outR++ = *inR++;
}
}
frame -= remain;
m_counter += remain;
} else
{
// Second half of gargle period
const uint32 remain = std::min(frame, m_period - m_counter);
if(triangle)
{
const uint32 stop = m_period - m_counter - remain;
const float factor = 1.0f / m_periodHalf;
for(uint32 i = m_period - m_counter; i > stop; i--)
{
*outL++ = *inL++ * i * factor;
*outR++ = *inR++ * i * factor;
}
} else
{
for(uint32 i = 0; i < remain; i++)
{
*outL++ = 0;
*outR++ = 0;
}
inL += remain;
inR += remain;
}
frame -= remain;
m_counter += remain;
if(m_counter >= m_period) m_counter = 0;
}
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue Gargle::GetParameter(PlugParamIndex index)
{
if(index < kGargleNumParameters)
{
return m_param[index];
}
return 0;
}
void Gargle::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kGargleNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
if(index == kGargleWaveShape)
value = mpt::round(value);
m_param[index] = value;
RecalculateGargleParams();
}
}
void Gargle::Resume()
{
RecalculateGargleParams();
m_counter = 0;
m_isResumed = true;
}
#ifdef MODPLUG_TRACKER
CString Gargle::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kGargleRate: return _T("Rate");
case kGargleWaveShape: return _T("WaveShape");
}
return CString();
}
CString Gargle::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kGargleRate: return _T("Hz");
}
return CString();
}
CString Gargle::GetParamDisplay(PlugParamIndex param)
{
CString s;
switch(param)
{
case kGargleRate:
s.Format(_T("%u"), RateInHertz());
break;
case kGargleWaveShape:
return (m_param[param] < 0.5) ? _T("Triangle") : _T("Square");
}
return s;
}
#endif // MODPLUG_TRACKER
uint32 Gargle::RateInHertz() const
{
return static_cast<uint32>(mpt::round(std::clamp(m_param[kGargleRate], 0.0f, 1.0f) * 999.0f)) + 1;
}
void Gargle::RecalculateGargleParams()
{
m_period = m_SndFile.GetSampleRate() / RateInHertz();
if(m_period < 2) m_period = 2;
m_periodHalf = m_period / 2;
LimitMax(m_counter, m_period);
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(Gargle)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,90 @@
/*
* Gargle.h
* --------
* Purpose: Implementation of the DMO Gargle 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class Gargle final : public IMixPlugin
{
protected:
enum Parameters
{
kGargleRate = 0,
kGargleWaveShape,
kGargleNumParameters
};
std::array<float, kGargleNumParameters> m_param;
uint32 m_period, m_periodHalf, m_counter; // In frames
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
Gargle(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0xDAFD8210; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kGargleNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override { m_counter = 0; }
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("Gargle"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
uint32 RateInHertz() const;
void RecalculateGargleParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,645 @@
/*
* I3DL2Reverb.cpp
* ---------------
* Purpose: Implementation of the DMO I3DL2Reverb 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 "I3DL2Reverb.h"
#ifdef MODPLUG_TRACKER
#include "../../../sounddsp/Reverb.h"
#endif // MODPLUG_TRACKER
#include "mpt/base/numbers.hpp"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
void I3DL2Reverb::DelayLine::Init(int32 ms, int32 padding, uint32 sampleRate, int32 delayTap)
{
m_length = Util::muldiv(sampleRate, ms, 1000) + padding;
m_position = 0;
SetDelayTap(delayTap);
assign(m_length, 0.0f);
}
void I3DL2Reverb::DelayLine::SetDelayTap(int32 delayTap)
{
if(m_length > 0)
m_delayPosition = (delayTap + m_position + m_length) % m_length;
}
void I3DL2Reverb::DelayLine::Advance()
{
if(--m_position < 0)
m_position += m_length;
if(--m_delayPosition < 0)
m_delayPosition += m_length;
}
MPT_FORCEINLINE void I3DL2Reverb::DelayLine::Set(float value)
{
at(m_position) = value;
}
float I3DL2Reverb::DelayLine::Get(int32 offset) const
{
offset = (offset + m_position) % m_length;
if(offset < 0)
offset += m_length;
return at(offset);
}
MPT_FORCEINLINE float I3DL2Reverb::DelayLine::Get() const
{
return at(m_delayPosition);
}
IMixPlugin* I3DL2Reverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) I3DL2Reverb(factory, sndFile, mixStruct);
}
I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
{
m_param[kI3DL2ReverbRoom] = 0.9f;
m_param[kI3DL2ReverbRoomHF] = 0.99f;
m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f;
m_param[kI3DL2ReverbDecayTime] = 0.07f;
m_param[kI3DL2ReverbDecayHFRatio] = 0.3842105f;
m_param[kI3DL2ReverbReflections] = 0.672545433f;
m_param[kI3DL2ReverbReflectionsDelay] = 0.233333333f;
m_param[kI3DL2ReverbReverb] = 0.85f;
m_param[kI3DL2ReverbReverbDelay] = 0.11f;
m_param[kI3DL2ReverbDiffusion] = 1.0f;
m_param[kI3DL2ReverbDensity] = 1.0f;
m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f;
m_param[kI3DL2ReverbQuality] = 2.0f / 3.0f;
SetCurrentProgram(m_program);
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(m_recalcParams)
{
auto sampleRate = m_effectiveSampleRate;
RecalculateI3DL2ReverbParams();
// Resize and clear delay lines if quality has changed
if(sampleRate != m_effectiveSampleRate)
PositionChanged();
}
if(!m_ok || !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) };
uint32 frames = numFrames;
if(!(m_quality & kFullSampleRate) && m_remain && frames > 0)
{
// Remaining frame from previous render call
frames--;
*(out[0]++) = m_prevL;
*(out[1]++) = m_prevR;
in[0]++;
in[1]++;
m_remain = false;
}
while(frames > 0)
{
// Apply room filter and insert into early reflection delay lines
const float inL = *(in[0]++);
const float inRoomL = (m_filterHist[12] - inL) * m_roomFilter + inL;
m_filterHist[12] = inRoomL;
m_delayLines[15].Set(inRoomL);
const float inR = *(in[1]++);
const float inRoomR = (m_filterHist[13] - inR) * m_roomFilter + inR;
m_filterHist[13] = inRoomR;
m_delayLines[16].Set(inRoomR);
// Early reflections (left)
float earlyL = m_delayLines[15].Get(m_earlyTaps[0][1]) * 0.68f
- m_delayLines[15].Get(m_earlyTaps[0][2]) * 0.5f
- m_delayLines[15].Get(m_earlyTaps[0][3]) * 0.62f
- m_delayLines[15].Get(m_earlyTaps[0][4]) * 0.5f
- m_delayLines[15].Get(m_earlyTaps[0][5]) * 0.62f;
if(m_quality & kMoreDelayLines)
{
float earlyL2 = earlyL;
earlyL = m_delayLines[13].Get() + earlyL * 0.618034f;
m_delayLines[13].Set(earlyL2 - earlyL * 0.618034f);
}
const float earlyRefOutL = earlyL * m_ERLevel;
m_filterHist[15] = m_delayLines[15].Get(m_earlyTaps[0][0]) + m_filterHist[15];
m_filterHist[16] = m_delayLines[16].Get(m_earlyTaps[1][0]) + m_filterHist[16];
// Lots of slightly different copy-pasta ahead
float reverbL1, reverbL2, reverbL3, reverbR1, reverbR2, reverbR3;
reverbL1 = -m_filterHist[15] * 0.707f;
reverbL2 = m_filterHist[16] * 0.707f + reverbL1;
reverbR2 = reverbL1 - m_filterHist[16] * 0.707f;
m_filterHist[5] = (m_filterHist[5] - m_delayLines[5].Get()) * m_delayCoeffs[5][1] + m_delayLines[5].Get();
reverbL1 = m_filterHist[5] * m_delayCoeffs[5][0] + reverbL2 * m_diffusion;
m_delayLines[5].Set(reverbL2 - reverbL1 * m_diffusion);
reverbL2 = reverbL1;
reverbL3 = -0.15f * reverbL1;
m_filterHist[4] = (m_filterHist[4] - m_delayLines[4].Get()) * m_delayCoeffs[4][1] + m_delayLines[4].Get();
reverbL1 = m_filterHist[4] * m_delayCoeffs[4][0] + reverbL2 * m_diffusion;
m_delayLines[4].Set(reverbL2 - reverbL1 * m_diffusion);
reverbL2 = reverbL1;
reverbL3 -= reverbL1 * 0.2f;
if(m_quality & kMoreDelayLines)
{
m_filterHist[3] = (m_filterHist[3] - m_delayLines[3].Get()) * m_delayCoeffs[3][1] + m_delayLines[3].Get();
reverbL1 = m_filterHist[3] * m_delayCoeffs[3][0] + reverbL2 * m_diffusion;
m_delayLines[3].Set(reverbL2 - reverbL1 * m_diffusion);
reverbL2 = reverbL1;
reverbL3 += 0.35f * reverbL1;
m_filterHist[2] = (m_filterHist[2] - m_delayLines[2].Get()) * m_delayCoeffs[2][1] + m_delayLines[2].Get();
reverbL1 = m_filterHist[2] * m_delayCoeffs[2][0] + reverbL2 * m_diffusion;
m_delayLines[2].Set(reverbL2 - reverbL1 * m_diffusion);
reverbL2 = reverbL1;
reverbL3 -= reverbL1 * 0.38f;
}
m_delayLines[17].Set(reverbL2);
reverbL1 = m_delayLines[17].Get() * m_delayCoeffs[12][0];
m_filterHist[17] = (m_filterHist[17] - reverbL1) * m_delayCoeffs[12][1] + reverbL1;
m_filterHist[1] = (m_filterHist[1] - m_delayLines[1].Get()) * m_delayCoeffs[1][1] + m_delayLines[1].Get();
reverbL1 = m_filterHist[17] * m_diffusion + m_filterHist[1] * m_delayCoeffs[1][0];
m_delayLines[1].Set(m_filterHist[17] - reverbL1 * m_diffusion);
reverbL2 = reverbL1;
float reverbL4 = reverbL1 * 0.38f;
m_filterHist[0] = (m_filterHist[0] - m_delayLines[0].Get()) * m_delayCoeffs[0][1] + m_delayLines[0].Get();
reverbL1 = m_filterHist[0] * m_delayCoeffs[0][0] + reverbL2 * m_diffusion;
m_delayLines[0].Set(reverbL2 - reverbL1 * m_diffusion);
reverbL3 -= reverbL1 * 0.38f;
m_filterHist[15] = reverbL1;
// Early reflections (right)
float earlyR = m_delayLines[16].Get(m_earlyTaps[1][1]) * 0.707f
- m_delayLines[16].Get(m_earlyTaps[1][2]) * 0.6f
- m_delayLines[16].Get(m_earlyTaps[1][3]) * 0.5f
- m_delayLines[16].Get(m_earlyTaps[1][4]) * 0.6f
- m_delayLines[16].Get(m_earlyTaps[1][5]) * 0.5f;
if(m_quality & kMoreDelayLines)
{
float earlyR2 = earlyR;
earlyR = m_delayLines[14].Get() + earlyR * 0.618034f;
m_delayLines[14].Set(earlyR2 - earlyR * 0.618034f);
}
const float earlyRefOutR = earlyR * m_ERLevel;
m_filterHist[11] = (m_filterHist[11] - m_delayLines[11].Get()) * m_delayCoeffs[11][1] + m_delayLines[11].Get();
reverbR1 = m_filterHist[11] * m_delayCoeffs[11][0] + reverbR2 * m_diffusion;
m_delayLines[11].Set(reverbR2 - reverbR1 * m_diffusion);
reverbR2 = reverbR1;
m_filterHist[10] = (m_filterHist[10] - m_delayLines[10].Get()) * m_delayCoeffs[10][1] + m_delayLines[10].Get();
reverbR1 = m_filterHist[10] * m_delayCoeffs[10][0] + reverbR2 * m_diffusion;
m_delayLines[10].Set(reverbR2 - reverbR1 * m_diffusion);
reverbR3 = reverbL4 - reverbR2 * 0.15f - reverbR1 * 0.2f;
reverbR2 = reverbR1;
if(m_quality & kMoreDelayLines)
{
m_filterHist[9] = (m_filterHist[9] - m_delayLines[9].Get()) * m_delayCoeffs[9][1] + m_delayLines[9].Get();
reverbR1 = m_filterHist[9] * m_delayCoeffs[9][0] + reverbR2 * m_diffusion;
m_delayLines[9].Set(reverbR2 - reverbR1 * m_diffusion);
reverbR2 = reverbR1;
reverbR3 += reverbR1 * 0.35f;
m_filterHist[8] = (m_filterHist[8] - m_delayLines[8].Get()) * m_delayCoeffs[8][1] + m_delayLines[8].Get();
reverbR1 = m_filterHist[8] * m_delayCoeffs[8][0] + reverbR2 * m_diffusion;
m_delayLines[8].Set(reverbR2 - reverbR1 * m_diffusion);
reverbR2 = reverbR1;
reverbR3 -= reverbR1 * 0.38f;
}
m_delayLines[18].Set(reverbR2);
reverbR1 = m_delayLines[18].Get() * m_delayCoeffs[12][0];
m_filterHist[18] = (m_filterHist[18] - reverbR1) * m_delayCoeffs[12][1] + reverbR1;
m_filterHist[7] = (m_filterHist[7] - m_delayLines[7].Get()) * m_delayCoeffs[7][1] + m_delayLines[7].Get();
reverbR1 = m_filterHist[18] * m_diffusion + m_filterHist[7] * m_delayCoeffs[7][0];
m_delayLines[7].Set(m_filterHist[18] - reverbR1 * m_diffusion);
reverbR2 = reverbR1;
float lateRevOutL = (reverbL3 + reverbR1 * 0.38f) * m_ReverbLevelL;
m_filterHist[6] = (m_filterHist[6] - m_delayLines[6].Get()) * m_delayCoeffs[6][1] + m_delayLines[6].Get();
reverbR1 = m_filterHist[6] * m_delayCoeffs[6][0] + reverbR2 * m_diffusion;
m_delayLines[6].Set(reverbR2 - reverbR1 * m_diffusion);
m_filterHist[16] = reverbR1;
float lateRevOutR = (reverbR3 - reverbR1 * 0.38f) * m_ReverbLevelR;
float outL = earlyRefOutL + lateRevOutL;
float outR = earlyRefOutR + lateRevOutR;
for(auto &line : m_delayLines)
line.Advance();
if(!(m_quality & kFullSampleRate))
{
*(out[0]++) = (outL + m_prevL) * 0.5f;
*(out[1]++) = (outR + m_prevR) * 0.5f;
m_prevL = outL;
m_prevR = outR;
in[0]++;
in[1]++;
if(frames-- == 1)
{
m_remain = true;
break;
}
}
*(out[0]++) = outL;
*(out[1]++) = outR;
frames--;
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
int32 I3DL2Reverb::GetNumPrograms() const
{
#ifdef MODPLUG_TRACKER
return NUM_REVERBTYPES;
#else
return 0;
#endif
}
void I3DL2Reverb::SetCurrentProgram(int32 program)
{
#ifdef MODPLUG_TRACKER
if(program < static_cast<int32>(NUM_REVERBTYPES))
{
m_program = program;
const auto &preset = *GetReverbPreset(m_program);
m_param[kI3DL2ReverbRoom] = (preset.lRoom + 10000) / 10000.0f;
m_param[kI3DL2ReverbRoomHF] = (preset.lRoomHF + 10000) / 10000.0f;
m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f;
m_param[kI3DL2ReverbDecayTime] = (preset.flDecayTime - 0.1f) / 19.9f;
m_param[kI3DL2ReverbDecayHFRatio] = (preset.flDecayHFRatio - 0.1f) / 1.9f;
m_param[kI3DL2ReverbReflections] = (preset.lReflections + 10000) / 11000.0f;
m_param[kI3DL2ReverbReflectionsDelay] = preset.flReflectionsDelay / 0.3f;
m_param[kI3DL2ReverbReverb] = (preset.lReverb + 10000) / 12000.0f;
m_param[kI3DL2ReverbReverbDelay] = preset.flReverbDelay / 0.1f;
m_param[kI3DL2ReverbDiffusion] = preset.flDiffusion / 100.0f;
m_param[kI3DL2ReverbDensity] = preset.flDensity / 100.0f;
m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f;
RecalculateI3DL2ReverbParams();
}
#else
MPT_UNUSED_VARIABLE(program);
#endif
}
PlugParamValue I3DL2Reverb::GetParameter(PlugParamIndex index)
{
if(index < kI3DL2ReverbNumParameters)
{
return m_param[index];
}
return 0;
}
void I3DL2Reverb::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kI3DL2ReverbNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
if(index == kI3DL2ReverbQuality)
value = mpt::round(value * 3.0f) / 3.0f;
m_param[index] = value;
m_recalcParams = true;
}
}
void I3DL2Reverb::Resume()
{
RecalculateI3DL2ReverbParams();
PositionChanged();
m_isResumed = true;
}
void I3DL2Reverb::PositionChanged()
{
MemsetZero(m_filterHist);
m_prevL = 0;
m_prevR = 0;
m_remain = false;
try
{
uint32 sampleRate = static_cast<uint32>(m_effectiveSampleRate);
m_delayLines[0].Init(67, 5, sampleRate, m_delayTaps[0]);
m_delayLines[1].Init(62, 5, sampleRate, m_delayTaps[1]);
m_delayLines[2].Init(53, 5, sampleRate, m_delayTaps[2]);
m_delayLines[3].Init(43, 5, sampleRate, m_delayTaps[3]);
m_delayLines[4].Init(32, 5, sampleRate, m_delayTaps[4]);
m_delayLines[5].Init(22, 5, sampleRate, m_delayTaps[5]);
m_delayLines[6].Init(75, 5, sampleRate, m_delayTaps[6]);
m_delayLines[7].Init(69, 5, sampleRate, m_delayTaps[7]);
m_delayLines[8].Init(60, 5, sampleRate, m_delayTaps[8]);
m_delayLines[9].Init(48, 5, sampleRate, m_delayTaps[9]);
m_delayLines[10].Init(36, 5, sampleRate, m_delayTaps[10]);
m_delayLines[11].Init(25, 5, sampleRate, m_delayTaps[11]);
m_delayLines[12].Init(0, 0, 0); // Dummy for array index consistency with both tap and coefficient arrays
m_delayLines[13].Init(3, 0, sampleRate, m_delayTaps[13]);
m_delayLines[14].Init(3, 0, sampleRate, m_delayTaps[14]);
m_delayLines[15].Init(407, 1, sampleRate);
m_delayLines[16].Init(400, 1, sampleRate);
m_delayLines[17].Init(10, 0, sampleRate, -1);
m_delayLines[18].Init(10, 0, sampleRate, -1);
m_ok = true;
} catch(mpt::out_of_memory e)
{
m_ok = false;
mpt::delete_out_of_memory(e);
}
}
#ifdef MODPLUG_TRACKER
CString I3DL2Reverb::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kI3DL2ReverbRoom: return _T("Room");
case kI3DL2ReverbRoomHF: return _T("RoomHF");
case kI3DL2ReverbRoomRolloffFactor: return _T("RoomRolloffFactor");
case kI3DL2ReverbDecayTime: return _T("DecayTime");
case kI3DL2ReverbDecayHFRatio: return _T("DecayHFRatio");
case kI3DL2ReverbReflections: return _T("Reflections");
case kI3DL2ReverbReflectionsDelay: return _T("ReflectionsDelay");
case kI3DL2ReverbReverb: return _T("Reverb");
case kI3DL2ReverbReverbDelay: return _T("ReverbDelay");
case kI3DL2ReverbDiffusion: return _T("Diffusion");
case kI3DL2ReverbDensity: return _T("Density");
case kI3DL2ReverbHFReference: return _T("HFRefrence");
case kI3DL2ReverbQuality: return _T("Quality");
}
return CString();
}
CString I3DL2Reverb::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kI3DL2ReverbRoom:
case kI3DL2ReverbRoomHF:
case kI3DL2ReverbReflections:
case kI3DL2ReverbReverb:
return _T("dB");
case kI3DL2ReverbDecayTime:
case kI3DL2ReverbReflectionsDelay:
case kI3DL2ReverbReverbDelay:
return _T("s");
case kI3DL2ReverbDiffusion:
case kI3DL2ReverbDensity:
return _T("%");
case kI3DL2ReverbHFReference:
return _T("Hz");
}
return CString();
}
CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param)
{
static constexpr const TCHAR * const modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") };
float value = m_param[param];
switch(param)
{
case kI3DL2ReverbRoom: value = Room() * 0.01f; break;
case kI3DL2ReverbRoomHF: value = RoomHF() * 0.01f; break;
case kI3DL2ReverbRoomRolloffFactor: value = RoomRolloffFactor(); break;
case kI3DL2ReverbDecayTime: value = DecayTime(); break;
case kI3DL2ReverbDecayHFRatio: value = DecayHFRatio(); break;
case kI3DL2ReverbReflections: value = Reflections() * 0.01f; break;
case kI3DL2ReverbReflectionsDelay: value = ReflectionsDelay(); break;
case kI3DL2ReverbReverb: value = Reverb() * 0.01f; break;
case kI3DL2ReverbReverbDelay: value = ReverbDelay(); break;
case kI3DL2ReverbDiffusion: value = Diffusion(); break;
case kI3DL2ReverbDensity: value = Density(); break;
case kI3DL2ReverbHFReference: value = HFReference(); break;
case kI3DL2ReverbQuality: return modes[Quality() % 4u];
}
CString s;
s.Format(_T("%.2f"), value);
return s;
}
CString I3DL2Reverb::GetCurrentProgramName()
{
return GetProgramName(m_program);
}
CString I3DL2Reverb::GetProgramName(int32 program)
{
return mpt::ToCString(GetReverbPresetName(program));
}
#endif // MODPLUG_TRACKER
void I3DL2Reverb::RecalculateI3DL2ReverbParams()
{
m_quality = Quality();
m_effectiveSampleRate = static_cast<float>(m_SndFile.GetSampleRate() / ((m_quality & kFullSampleRate) ? 1u : 2u));
// Diffusion
m_diffusion = Diffusion() * (0.618034f / 100.0f);
// Early Reflection Level
m_ERLevel = std::min(std::pow(10.0f, (Room() + Reflections()) / (100.0f * 20.0f)), 1.0f) * 0.761f;
// Room Filter
float roomHF = std::pow(10.0f, RoomHF() / 100.0f / 10.0f);
if(roomHF == 1.0f)
{
m_roomFilter = 0.0f;
} else
{
float freq = std::cos(HFReference() * (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate);
float roomFilter = (freq * (roomHF + roomHF) - 2.0f + std::sqrt(freq * (roomHF * roomHF * freq * 4.0f) + roomHF * 8.0f - roomHF * roomHF * 4.0f - roomHF * freq * 8.0f)) / (roomHF + roomHF - 2.0f);
m_roomFilter = Clamp(roomFilter, 0.0f, 1.0f);
}
SetDelayTaps();
SetDecayCoeffs();
m_recalcParams = false;
}
void I3DL2Reverb::SetDelayTaps()
{
// Early reflections
static constexpr float delays[] =
{
1.0000f, 1.0000f, 0.0000f, 0.1078f, 0.1768f, 0.2727f,
0.3953f, 0.5386f, 0.6899f, 0.8306f, 0.9400f, 0.9800f,
};
const float sampleRate = m_effectiveSampleRate;
const float reflectionsDelay = ReflectionsDelay();
const float reverbDelay = std::max(ReverbDelay(), 5.0f / 1000.0f);
m_earlyTaps[0][0] = static_cast<int32>((reverbDelay + reflectionsDelay + 7.0f / 1000.0f) * sampleRate);
for(uint32 i = 1; i < 12; i++)
{
m_earlyTaps[i % 2u][i / 2u] = static_cast<int32>((reverbDelay * delays[i] + reflectionsDelay) * sampleRate);
}
// Late reflections
float density = std::min((Density() / 100.0f + 0.1f) * 0.9091f, 1.0f);
float delayL = density * 67.0f / 1000.0f * sampleRate;
float delayR = density * 75.0f / 1000.0f * sampleRate;
for(int i = 0, power = 0; i < 6; i++)
{
power += i;
float factor = std::pow(0.93f, static_cast<float>(power));
m_delayTaps[i + 0] = static_cast<int32>(delayL * factor);
m_delayTaps[i + 6] = static_cast<int32>(delayR * factor);
}
m_delayTaps[12] = static_cast<int32>(10.0f / 1000.0f * sampleRate);
// Early reflections (extra delay lines)
m_delayTaps[13] = static_cast<int32>(3.25f / 1000.0f * sampleRate);
m_delayTaps[14] = static_cast<int32>(3.53f / 1000.0f * sampleRate);
for(std::size_t d = 0; d < std::size(m_delayTaps); d++)
m_delayLines[d].SetDelayTap(m_delayTaps[d]);
}
void I3DL2Reverb::SetDecayCoeffs()
{
float levelLtmp = 1.0f, levelRtmp = 1.0f;
float levelL = 0.0f, levelR = 0.0f;
levelLtmp *= CalcDecayCoeffs(5);
levelRtmp *= CalcDecayCoeffs(11);
levelL += levelLtmp * 0.0225f;
levelR += levelRtmp * 0.0225f;
levelLtmp *= CalcDecayCoeffs(4);
levelRtmp *= CalcDecayCoeffs(10);
levelL += levelLtmp * 0.04f;
levelR += levelRtmp * 0.04f;
if(m_quality & kMoreDelayLines)
{
levelLtmp *= CalcDecayCoeffs(3);
levelRtmp *= CalcDecayCoeffs(9);
levelL += levelLtmp * 0.1225f;
levelR += levelRtmp * 0.1225f;
levelLtmp *= CalcDecayCoeffs(2);
levelRtmp *= CalcDecayCoeffs(8);
levelL += levelLtmp * 0.1444f;
levelR += levelRtmp * 0.1444f;
}
CalcDecayCoeffs(12);
levelLtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0];
levelRtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0];
levelLtmp *= CalcDecayCoeffs(1);
levelRtmp *= CalcDecayCoeffs(7);
levelL += levelRtmp * 0.1444f;
levelR += levelLtmp * 0.1444f;
levelLtmp *= CalcDecayCoeffs(0);
levelRtmp *= CalcDecayCoeffs(6);
levelL += levelLtmp * 0.1444f;
levelR += levelRtmp * 0.1444f;
// Final Reverb Level
float level = std::min(std::pow(10.0f, (Room() + Reverb()) / (100.0f * 20.0f)), 1.0f);
float monoInv = 1.0f - ((levelLtmp + levelRtmp) * 0.5f);
m_ReverbLevelL = level * std::sqrt(monoInv / levelL);
m_ReverbLevelR = level * std::sqrt(monoInv / levelR);
}
float I3DL2Reverb::CalcDecayCoeffs(int32 index)
{
float hfRef = (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate * HFReference();
float decayHFRatio = DecayHFRatio();
if(decayHFRatio > 1.0f)
hfRef = mpt::numbers::pi_v<float>;
float c1 = std::pow(10.0f, ((m_delayTaps[index] / m_effectiveSampleRate) * -60.0f / DecayTime()) / 20.0f);
float c2 = 0.0f;
float c21 = (std::pow(c1, 2.0f - 2.0f / decayHFRatio) - 1.0f) / (1.0f - std::cos(hfRef));
if(c21 != 0 && std::isfinite(c21))
{
float c22 = -2.0f * c21 - 2.0f;
float c23sq = c22 * c22 - c21 * c21 * 4.0f;
float c23 = c23sq > 0.0f ? std::sqrt(c23sq) : 0.0f;
c2 = (c23 - c22) / (c21 + c21);
if(std::abs(c2) > 1.0f)
c2 = (-c22 - c23) / (c21 + c21);
c2 = mpt::sanitize_nan(c2);
}
m_delayCoeffs[index][0] = c1;
m_delayCoeffs[index][1] = c2;
c1 *= c1;
float diff2 = m_diffusion * m_diffusion;
return diff2 + c1 / (1.0f - diff2 * c1) * (1.0f - diff2) * (1.0f - diff2);
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(I3DL2Reverb)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,169 @@
/*
* I3DL2Reverb.h
* -------------
* Purpose: Implementation of the DMO I3DL2Reverb 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.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class I3DL2Reverb final : public IMixPlugin
{
protected:
enum Parameters
{
kI3DL2ReverbRoom = 0,
kI3DL2ReverbRoomHF,
kI3DL2ReverbRoomRolloffFactor, // Doesn't actually do anything :)
kI3DL2ReverbDecayTime,
kI3DL2ReverbDecayHFRatio,
kI3DL2ReverbReflections,
kI3DL2ReverbReflectionsDelay,
kI3DL2ReverbReverb,
kI3DL2ReverbReverbDelay,
kI3DL2ReverbDiffusion,
kI3DL2ReverbDensity,
kI3DL2ReverbHFReference,
kI3DL2ReverbQuality,
kI3DL2ReverbNumParameters
};
enum QualityFlags
{
kMoreDelayLines = 0x01,
kFullSampleRate = 0x02,
};
class DelayLine : private std::vector<float>
{
int32 m_length;
int32 m_position;
int32 m_delayPosition;
public:
void Init(int32 ms, int32 padding, uint32 sampleRate, int32 delayTap = 0);
void SetDelayTap(int32 delayTap);
void Advance();
void Set(float value);
float Get(int32 offset) const;
float Get() const;
};
std::array<float, kI3DL2ReverbNumParameters> m_param;
int32 m_program = 0;
// Calculated parameters
uint32 m_quality;
float m_effectiveSampleRate;
float m_diffusion;
float m_roomFilter;
float m_ERLevel;
float m_ReverbLevelL;
float m_ReverbLevelR;
int32 m_delayTaps[15]; // 6*L + 6*R + LR + Early L + Early R
int32 m_earlyTaps[2][6];
float m_delayCoeffs[13][2];
// State
DelayLine m_delayLines[19];
float m_filterHist[19];
// Remaining frame for downsampled reverb
float m_prevL;
float m_prevR;
bool m_remain = false;
bool m_ok = false, m_recalcParams = true;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0xEF985E71; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override;
int32 GetCurrentProgram() override { return m_program; }
// cppcheck-suppress virtualCallInConstructor
void SetCurrentProgram(int32) override;
PlugParamIndex GetNumParameters() const override { return kI3DL2ReverbNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("I3DL2Reverb"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override;
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32 program) override;
bool HasEditor() const override { return false; }
#endif
void BeginSetProgram(int32) override { }
void EndSetProgram() override { }
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
float Room() const { return -10000.0f + m_param[kI3DL2ReverbRoom] * 10000.0f; }
float RoomHF() const { return -10000.0f + m_param[kI3DL2ReverbRoomHF] * 10000.0f; }
float RoomRolloffFactor() const { return m_param[kI3DL2ReverbRoomRolloffFactor] * 10.0f; }
float DecayTime() const { return 0.1f + m_param[kI3DL2ReverbDecayTime] * 19.9f; }
float DecayHFRatio() const { return 0.1f + m_param[kI3DL2ReverbDecayHFRatio] * 1.9f; }
float Reflections() const { return -10000.0f + m_param[kI3DL2ReverbReflections] * 11000.0f; }
float ReflectionsDelay() const { return m_param[kI3DL2ReverbReflectionsDelay] * 0.3f; }
float Reverb() const { return -10000.0f + m_param[kI3DL2ReverbReverb] * 12000.0f; }
float ReverbDelay() const { return m_param[kI3DL2ReverbReverbDelay] * 0.1f; }
float Diffusion() const { return m_param[kI3DL2ReverbDiffusion] * 100.0f; }
float Density() const { return m_param[kI3DL2ReverbDensity] * 100.0f; }
float HFReference() const { return 20.0f + m_param[kI3DL2ReverbHFReference] * 19980.0f; }
uint32 Quality() const { return mpt::saturate_round<uint32>(m_param[kI3DL2ReverbQuality] * 3.0f); }
void RecalculateI3DL2ReverbParams();
void SetDelayTaps();
void SetDecayCoeffs();
float CalcDecayCoeffs(int32 index);
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,201 @@
/*
* ParamEq.cpp
* -----------
* Purpose: Implementation of the DMO Parametric Equalizer 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 "ParamEq.h"
#include "mpt/base/numbers.hpp"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* ParamEq::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) ParamEq(factory, sndFile, mixStruct);
}
ParamEq::ParamEq(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
, m_maxFreqParam(1.0f)
{
m_param[kEqCenter] = (8000.0f - 80.0f) / 15920.0f;
m_param[kEqBandwidth] = 0.314286f;
m_param[kEqGain] = 0.5f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void ParamEq::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!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) };
if(m_param[kEqGain] == 0.5f)
{
memcpy(out[0], in[0], numFrames * sizeof(float));
memcpy(out[1], in[1], numFrames * sizeof(float));
} else
{
for(uint32 i = numFrames; i != 0; i--)
{
for(uint8 channel = 0; channel < 2; channel++)
{
float x = *(in[channel])++;
float y = b0DIVa0 * x + b1DIVa0 * x1[channel] + b2DIVa0 * x2[channel] - a1DIVa0 * y1[channel] - a2DIVa0 * y2[channel];
x2[channel] = x1[channel];
x1[channel] = x;
y2[channel] = y1[channel];
y1[channel] = y;
*(out[channel])++ = y;
}
}
}
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue ParamEq::GetParameter(PlugParamIndex index)
{
if(index < kEqNumParameters)
{
return m_param[index];
}
return 0;
}
void ParamEq::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kEqNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
m_param[index] = value;
RecalculateEqParams();
}
}
void ParamEq::Resume()
{
m_isResumed = true;
// Limit center frequency to a third of the sampling rate.
m_maxFreqParam = Clamp((m_SndFile.GetSampleRate() / 3.0f - 80.0f) / 15920.0f, 0.0f, 1.0f);
RecalculateEqParams();
PositionChanged();
}
void ParamEq::PositionChanged()
{
// Reset filter state
x1[0] = x2[0] = 0;
x1[1] = x2[1] = 0;
y1[0] = y2[0] = 0;
y1[1] = y2[1] = 0;
}
#ifdef MODPLUG_TRACKER
CString ParamEq::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kEqCenter: return _T("Center");
case kEqBandwidth: return _T("Bandwidth");
case kEqGain: return _T("Gain");
}
return CString();
}
CString ParamEq::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kEqCenter: return _T("Hz");
case kEqBandwidth: return _T("Semitones");
case kEqGain: return _T("dB");
}
return CString();
}
CString ParamEq::GetParamDisplay(PlugParamIndex param)
{
float value = 0.0f;
switch(param)
{
case kEqCenter:
value = FreqInHertz();
break;
case kEqBandwidth:
value = BandwidthInSemitones();
break;
case kEqGain:
value = GainInDecibel();
break;
}
CString s;
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
void ParamEq::RecalculateEqParams()
{
LimitMax(m_param[kEqCenter], m_maxFreqParam);
const float freq = FreqInHertz() / m_SndFile.GetSampleRate();
const float a = std::pow(10.0f, GainInDecibel() / 40.0f);
const float w0 = 2.0f * mpt::numbers::pi_v<float> * freq;
const float sinW0 = std::sin(w0);
const float cosW0 = std::cos(w0);
const float alpha = sinW0 * std::sinh((BandwidthInSemitones() * (mpt::numbers::ln2_v<float> / 24.0f)) * w0 / sinW0);
const float b0 = 1.0f + alpha * a;
const float b1 = -2.0f * cosW0;
const float b2 = 1.0f - alpha * a;
const float a0 = 1.0f + alpha / a;
const float a1 = -2.0f * cosW0;
const float a2 = 1.0f - alpha / a;
b0DIVa0 = b0 / a0;
b1DIVa0 = b1 / a0;
b2DIVa0 = b2 / a0;
a1DIVa0 = a1 / a0;
a2DIVa0 = a2 / a0;
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(ParamEq)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,98 @@
/*
* ParamEq.h
* ---------
* Purpose: Implementation of the DMO Parametric Equalizer 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class ParamEq final : public IMixPlugin
{
protected:
enum Parameters
{
kEqCenter = 0,
kEqBandwidth,
kEqGain,
kEqNumParameters
};
std::array<float, kEqNumParameters> m_param;
// Equalizer coefficients
float b0DIVa0, b1DIVa0, b2DIVa0, a1DIVa0, a2DIVa0;
// Equalizer memory
float x1[2], x2[2];
float y1[2], y2[2];
float m_maxFreqParam;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
ParamEq(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0x120CED89; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kEqNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("ParamEq"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
float BandwidthInSemitones() const { return 1.0f + m_param[kEqBandwidth] * 35.0f; }
float FreqInHertz() const { return 80.0f + m_param[kEqCenter] * 15920.0f; }
float GainInDecibel() const { return (m_param[kEqGain] - 0.5f) * 30.0f; }
void RecalculateEqParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS

View file

@ -0,0 +1,261 @@
/*
* WavesReverb.cpp
* ---------------
* Purpose: Implementation of the DMO WavesReverb 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 "WavesReverb.h"
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_BEGIN
#ifndef NO_PLUGINS
namespace DMO
{
IMixPlugin* WavesReverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
{
return new (std::nothrow) WavesReverb(factory, sndFile, mixStruct);
}
WavesReverb::WavesReverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
: IMixPlugin(factory, sndFile, mixStruct)
{
m_param[kRvbInGain] = 1.0f;
m_param[kRvbReverbMix] = 1.0f;
m_param[kRvbReverbTime] = 1.0f / 3.0f;
m_param[kRvbHighFreqRTRatio] = 0.0f;
m_mixBuffer.Initialize(2, 2);
InsertIntoFactoryList();
}
void WavesReverb::Process(float *pOutL, float *pOutR, uint32 numFrames)
{
if(!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) };
uint32 combPos = m_state.combPos, allpassPos = m_state.allpassPos;
uint32 delay0 = (m_delay[0] + combPos + 1) & 0xFFF;
uint32 delay1 = (m_delay[1] + combPos + 1) & 0xFFF;
uint32 delay2 = (m_delay[2] + combPos + 1) & 0xFFF;
uint32 delay3 = (m_delay[3] + combPos + 1) & 0xFFF;
uint32 delay4 = (m_delay[4] + allpassPos) & 0x3FF;
uint32 delay5 = (m_delay[5] + allpassPos) & 0x3FF;
float delay0old = m_state.comb[delay0][0];
float delay1old = m_state.comb[delay1][1];
float delay2old = m_state.comb[delay2][2];
float delay3old = m_state.comb[delay3][3];
for(uint32 i = numFrames; i != 0; i--)
{
const float leftIn = *(in[0])++ + 1e-30f; // Prevent denormals
const float rightIn = *(in[1])++ + 1e-30f; // Prevent denormals
// Advance buffer index for the four comb filters
delay0 = (delay0 - 1) & 0xFFF;
delay1 = (delay1 - 1) & 0xFFF;
delay2 = (delay2 - 1) & 0xFFF;
delay3 = (delay3 - 1) & 0xFFF;
float &delay0new = m_state.comb[delay0][0];
float &delay1new = m_state.comb[delay1][1];
float &delay2new = m_state.comb[delay2][2];
float &delay3new = m_state.comb[delay3][3];
float r1, r2;
r1 = delay1new * 0.61803401f + m_state.allpass1[delay4][0] * m_coeffs[0];
r2 = m_state.allpass1[delay4][1] * m_coeffs[0] - delay0new * 0.61803401f;
m_state.allpass1[allpassPos][0] = r2 * 0.61803401f + delay0new;
m_state.allpass1[allpassPos][1] = delay1new - r1 * 0.61803401f;
delay0new = r1;
delay1new = r2;
r1 = delay3new * 0.61803401f + m_state.allpass2[delay5][0] * m_coeffs[1];
r2 = m_state.allpass2[delay5][1] * m_coeffs[1] - delay2new * 0.61803401f;
m_state.allpass2[allpassPos][0] = r2 * 0.61803401f + delay2new;
m_state.allpass2[allpassPos][1] = delay3new - r1 * 0.61803401f;
delay2new = r1;
delay3new = r2;
*(out[0])++ = (leftIn * m_dryFactor) + delay0new + delay2new;
*(out[1])++ = (rightIn * m_dryFactor) + delay1new + delay3new;
const float leftWet = leftIn * m_wetFactor;
const float rightWet = rightIn * m_wetFactor;
m_state.comb[combPos][0] = (delay0new * m_coeffs[2]) + (delay0old * m_coeffs[3]) + leftWet;
m_state.comb[combPos][1] = (delay1new * m_coeffs[4]) + (delay1old * m_coeffs[5]) + rightWet;
m_state.comb[combPos][2] = (delay2new * m_coeffs[6]) + (delay2old * m_coeffs[7]) - rightWet;
m_state.comb[combPos][3] = (delay3new * m_coeffs[8]) + (delay3old * m_coeffs[9]) + leftWet;
delay0old = delay0new;
delay1old = delay1new;
delay2old = delay2new;
delay3old = delay3new;
// Advance buffer index
combPos = (combPos - 1) & 0xFFF;
allpassPos = (allpassPos - 1) & 0x3FF;
delay4 = (delay4 - 1) & 0x3FF;
delay5 = (delay5 - 1) & 0x3FF;
}
m_state.combPos = combPos;
m_state.allpassPos = allpassPos;
ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
}
PlugParamValue WavesReverb::GetParameter(PlugParamIndex index)
{
if(index < kRvbNumParameters)
{
return m_param[index];
}
return 0;
}
void WavesReverb::SetParameter(PlugParamIndex index, PlugParamValue value)
{
if(index < kRvbNumParameters)
{
value = mpt::safe_clamp(value, 0.0f, 1.0f);
m_param[index] = value;
RecalculateWavesReverbParams();
}
}
void WavesReverb::Resume()
{
m_isResumed = true;
// Recalculate delays
uint32 delay0 = mpt::saturate_round<uint32>(m_SndFile.GetSampleRate() * 0.045f);
uint32 delay1 = mpt::saturate_round<uint32>(delay0 * 1.18920707f); // 2^0.25
uint32 delay2 = mpt::saturate_round<uint32>(delay1 * 1.18920707f);
uint32 delay3 = mpt::saturate_round<uint32>(delay2 * 1.18920707f);
uint32 delay4 = mpt::saturate_round<uint32>((delay0 + delay2) * 0.11546667f);
uint32 delay5 = mpt::saturate_round<uint32>((delay1 + delay3) * 0.11546667f);
// Comb delays
m_delay[0] = delay0 - delay4;
m_delay[1] = delay2 - delay4;
m_delay[2] = delay1 - delay5;
m_delay[3] = delay3 - delay5;
// Allpass delays
m_delay[4] = delay4;
m_delay[5] = delay5;
RecalculateWavesReverbParams();
PositionChanged();
}
void WavesReverb::PositionChanged()
{
MemsetZero(m_state);
}
#ifdef MODPLUG_TRACKER
CString WavesReverb::GetParamName(PlugParamIndex param)
{
switch(param)
{
case kRvbInGain: return _T("InGain");
case kRvbReverbMix: return _T("ReverbMix");
case kRvbReverbTime: return _T("ReverbTime");
case kRvbHighFreqRTRatio: return _T("HighFreqRTRatio");
}
return CString();
}
CString WavesReverb::GetParamLabel(PlugParamIndex param)
{
switch(param)
{
case kRvbInGain:
case kRvbReverbMix:
return _T("dB");
case kRvbReverbTime:
return _T("ms");
}
return CString();
}
CString WavesReverb::GetParamDisplay(PlugParamIndex param)
{
float value = m_param[param];
switch(param)
{
case kRvbInGain:
case kRvbReverbMix:
value = GainInDecibel(value);
break;
case kRvbReverbTime:
value = ReverbTime();
break;
case kRvbHighFreqRTRatio:
value = HighFreqRTRatio();
break;
}
CString s;
s.Format(_T("%.2f"), value);
return s;
}
#endif // MODPLUG_TRACKER
void WavesReverb::RecalculateWavesReverbParams()
{
// Recalculate filters
const double ReverbTimeSmp = -3000.0 / (m_SndFile.GetSampleRate() * ReverbTime());
const double ReverbTimeSmpHF = ReverbTimeSmp * (1.0 / HighFreqRTRatio() - 1.0);
m_coeffs[0] = static_cast<float>(std::pow(10.0, m_delay[4] * ReverbTimeSmp));
m_coeffs[1] = static_cast<float>(std::pow(10.0, m_delay[5] * ReverbTimeSmp));
double sum = 0.0;
for(uint32 pair = 0; pair < 4; pair++)
{
double gain1 = std::pow(10.0, m_delay[pair] * ReverbTimeSmp);
double gain2 = (1.0 - std::pow(10.0, (m_delay[pair] + m_delay[4 + pair / 2]) * ReverbTimeSmpHF)) * 0.5;
double gain3 = gain1 * m_coeffs[pair / 2];
double gain4 = gain3 * (((gain3 + 1.0) * gain3 + 1.0) * gain3 + 1.0) + 1.0;
m_coeffs[2 + pair * 2] = static_cast<float>(gain1 * (1.0 - gain2));
m_coeffs[3 + pair * 2] = static_cast<float>(gain1 * gain2);
sum += gain4 * gain4;
}
double inGain = std::pow(10.0, GainInDecibel(m_param[kRvbInGain]) * 0.05);
double reverbMix = std::pow(10.0, GainInDecibel(m_param[kRvbReverbMix]) * 0.1);
m_dryFactor = static_cast<float>(std::sqrt(1.0 - reverbMix) * inGain);
m_wetFactor = static_cast<float>(std::sqrt(reverbMix) * (4.0 / std::sqrt(sum) * inGain));
}
} // namespace DMO
#else
MPT_MSVC_WORKAROUND_LNK4221(WavesReverb)
#endif // !NO_PLUGINS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,107 @@
/*
* WavesReverb.h
* -------------
* Purpose: Implementation of the DMO WavesReverb 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.
*/
#ifndef NO_PLUGINS
#include "../PlugInterface.h"
OPENMPT_NAMESPACE_BEGIN
namespace DMO
{
class WavesReverb final : public IMixPlugin
{
protected:
enum Parameters
{
kRvbInGain = 0,
kRvbReverbMix,
kRvbReverbTime,
kRvbHighFreqRTRatio,
kRvbNumParameters
};
std::array<float, kRvbNumParameters> m_param;
// Parameters and coefficients
float m_dryFactor;
float m_wetFactor;
std::array<float, 10> m_coeffs;
std::array<uint32, 6> m_delay;
// State
struct ReverbState
{
uint32 combPos, allpassPos;
float comb[4096][4];
float allpass1[1024][2];
float allpass2[1024][2];
} m_state;
public:
static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
WavesReverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
void Release() override { delete this; }
int32 GetUID() const override { return 0x87FC0268; }
int32 GetVersion() const override { return 0; }
void Idle() override { }
uint32 GetLatency() const override { return 0; }
void Process(float *pOutL, float *pOutR, uint32 numFrames) override;
float RenderSilence(uint32) override { return 0.0f; }
int32 GetNumPrograms() const override { return 0; }
int32 GetCurrentProgram() override { return 0; }
void SetCurrentProgram(int32) override { }
PlugParamIndex GetNumParameters() const override { return kRvbNumParameters; }
PlugParamValue GetParameter(PlugParamIndex index) override;
void SetParameter(PlugParamIndex index, PlugParamValue value) override;
void Resume() override;
void Suspend() override { m_isResumed = false; }
void PositionChanged() override;
bool IsInstrument() const override { return false; }
bool CanRecieveMidiEvents() override { return false; }
bool ShouldProcessSilence() override { return true; }
#ifdef MODPLUG_TRACKER
CString GetDefaultEffectName() override { return _T("WavesReverb"); }
CString GetParamName(PlugParamIndex param) override;
CString GetParamLabel(PlugParamIndex) override;
CString GetParamDisplay(PlugParamIndex param) override;
CString GetCurrentProgramName() override { return CString(); }
void SetCurrentProgramName(const CString &) override { }
CString GetProgramName(int32) override { return CString(); }
bool HasEditor() const override { return false; }
#endif
int GetNumInputChannels() const override { return 2; }
int GetNumOutputChannels() const override { return 2; }
protected:
static float GainInDecibel(float param) { return -96.0f + param * 96.0f; }
float ReverbTime() const { return 0.001f + m_param[kRvbReverbTime] * 2999.999f; }
float HighFreqRTRatio() const { return 0.001f + m_param[kRvbHighFreqRTRatio] * 0.998f; }
void RecalculateWavesReverbParams();
};
} // namespace DMO
OPENMPT_NAMESPACE_END
#endif // !NO_PLUGINS