123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- /*
- * 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
|