1 #include "effects/builtin/moogladder4filtereffect.h"
2 #include "util/math.h"
3
4
5 static const double kMinCorner = 0.0003; // 13 Hz @ 44100
6 static const double kMaxCorner = 0.5; // 22050 Hz @ 44100
7
8 // static
getId()9 QString MoogLadder4FilterEffect::getId() {
10 return "org.mixxx.effects.moogladder4filter";
11 }
12
13 // static
getManifest()14 EffectManifestPointer MoogLadder4FilterEffect::getManifest() {
15 EffectManifestPointer pManifest(new EffectManifest());
16 pManifest->setId(getId());
17 pManifest->setName(QObject::tr("Moog Ladder 4 Filter"));
18 pManifest->setShortName(QObject::tr("Moog Filter"));
19 pManifest->setAuthor("The Mixxx Team");
20 pManifest->setVersion("1.0");
21 pManifest->setDescription(QObject::tr(
22 "A 4-pole Moog ladder filter, based on Antti Houvilainen's non linear digital implementation"));
23 pManifest->setEffectRampsFromDry(true);
24
25 EffectManifestParameterPointer lpf = pManifest->addParameter();
26 lpf->setId("lpf");
27 lpf->setName(QObject::tr("LPF"));
28 lpf->setDescription(QObject::tr("Corner frequency ratio of the low pass filter"));
29 lpf->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC);
30 lpf->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
31 lpf->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
32 lpf->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED_LEFT);
33 lpf->setNeutralPointOnScale(1);
34 lpf->setDefault(kMaxCorner);
35 lpf->setMinimum(kMinCorner);
36 lpf->setMaximum(kMaxCorner);
37
38 EffectManifestParameterPointer q = pManifest->addParameter();
39 q->setId("resonance");
40 q->setName(QObject::tr("Resonance"));
41 q->setShortName(QObject::tr("Res"));
42 q->setDescription(QObject::tr("Resonance of the filters. 4 = self oscillating"));
43 q->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC);
44 q->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
45 q->setUnitsHint(EffectManifestParameter::UnitsHint::SAMPLERATE);
46 q->setMinimum(0.0);
47 q->setMaximum(4.0);
48 q->setDefault(1.0);
49
50 EffectManifestParameterPointer hpf = pManifest->addParameter();
51 hpf->setId("hpf");
52 hpf->setName(QObject::tr("HPF"));
53 hpf->setDescription(QObject::tr("Corner frequency ratio of the high pass filter"));
54 hpf->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC);
55 hpf->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
56 hpf->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
57 hpf->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED_RIGHT);
58 hpf->setNeutralPointOnScale(0.0);
59 hpf->setDefault(kMinCorner);
60 hpf->setMinimum(kMinCorner);
61 hpf->setMaximum(kMaxCorner);
62
63 return pManifest;
64 }
65
MoogLadder4FilterGroupState(const mixxx::EngineParameters & bufferParameters)66 MoogLadder4FilterGroupState::MoogLadder4FilterGroupState(
67 const mixxx::EngineParameters& bufferParameters)
68 : EffectState(bufferParameters),
69 m_loFreq(kMaxCorner),
70 m_resonance(0),
71 m_hiFreq(kMinCorner),
72 m_samplerate(bufferParameters.sampleRate()) {
73 m_pBuf = SampleUtil::alloc(bufferParameters.samplesPerBuffer());
74 m_pLowFilter = new EngineFilterMoogLadder4Low(
75 bufferParameters.sampleRate(),
76 m_loFreq * bufferParameters.sampleRate(), m_resonance);
77 m_pHighFilter = new EngineFilterMoogLadder4High(
78 bufferParameters.sampleRate(),
79 m_hiFreq * bufferParameters.sampleRate(), m_resonance);
80 }
81
~MoogLadder4FilterGroupState()82 MoogLadder4FilterGroupState::~MoogLadder4FilterGroupState() {
83 SampleUtil::free(m_pBuf);
84 delete m_pLowFilter;
85 delete m_pHighFilter;
86 }
87
MoogLadder4FilterEffect(EngineEffect * pEffect)88 MoogLadder4FilterEffect::MoogLadder4FilterEffect(EngineEffect* pEffect)
89 : m_pLPF(pEffect->getParameterById("lpf")),
90 m_pResonance(pEffect->getParameterById("resonance")),
91 m_pHPF(pEffect->getParameterById("hpf")) {
92 }
93
~MoogLadder4FilterEffect()94 MoogLadder4FilterEffect::~MoogLadder4FilterEffect() {
95 //qDebug() << debugString() << "destroyed";
96 }
97
processChannel(const ChannelHandle & handle,MoogLadder4FilterGroupState * pState,const CSAMPLE * pInput,CSAMPLE * pOutput,const mixxx::EngineParameters & bufferParameters,const EffectEnableState enableState,const GroupFeatureState & groupFeatures)98 void MoogLadder4FilterEffect::processChannel(
99 const ChannelHandle& handle,
100 MoogLadder4FilterGroupState* pState,
101 const CSAMPLE* pInput, CSAMPLE* pOutput,
102 const mixxx::EngineParameters& bufferParameters,
103 const EffectEnableState enableState,
104 const GroupFeatureState& groupFeatures) {
105 Q_UNUSED(handle);
106 Q_UNUSED(groupFeatures);
107
108 double resonance = m_pResonance->value();
109 double hpf;
110 double lpf;
111 if (enableState == EffectEnableState::Disabling) {
112 // Ramp to dry, when disabling, this will ramp from dry when enabling as well
113 hpf = kMinCorner;
114 lpf = kMaxCorner;
115 } else {
116 hpf = m_pHPF->value();
117 lpf = m_pLPF->value();
118 }
119
120 if (pState->m_loFreq != lpf ||
121 pState->m_resonance != resonance ||
122 pState->m_samplerate != bufferParameters.sampleRate()) {
123 pState->m_pLowFilter->setParameter(bufferParameters.sampleRate(),
124 static_cast<float>(lpf * bufferParameters.sampleRate()),
125 static_cast<float>(resonance));
126 }
127
128 if (pState->m_hiFreq != hpf ||
129 pState->m_resonance != resonance ||
130 pState->m_samplerate != bufferParameters.sampleRate()) {
131 pState->m_pHighFilter->setParameter(bufferParameters.sampleRate(),
132 static_cast<float>(hpf * bufferParameters.sampleRate()),
133 static_cast<float>(resonance));
134 }
135
136 const CSAMPLE* pLpfInput = pState->m_pBuf;
137 CSAMPLE* pHpfOutput = pState->m_pBuf;
138 if (lpf >= kMaxCorner && pState->m_loFreq >= kMaxCorner) {
139 // Lpf disabled Hpf can write directly to output
140 pHpfOutput = pOutput;
141 pLpfInput = pHpfOutput;
142 }
143
144 if (hpf > kMinCorner) {
145 // hpf enabled, fade-in is handled in the filter when starting from pause
146 pState->m_pHighFilter->process(pInput, pHpfOutput, bufferParameters.samplesPerBuffer());
147 } else if (pState->m_hiFreq > kMinCorner) {
148 // hpf disabling
149 pState->m_pHighFilter->processAndPauseFilter(pInput,
150 pHpfOutput, bufferParameters.samplesPerBuffer());
151 } else {
152 // paused LP uses input directly
153 pLpfInput = pInput;
154 }
155
156 if (lpf < kMaxCorner) {
157 // lpf enabled, fade-in is handled in the filter when starting from pause
158 pState->m_pLowFilter->process(pLpfInput, pOutput, bufferParameters.samplesPerBuffer());
159 } else if (pState->m_loFreq < kMaxCorner) {
160 // hpf disabling
161 pState->m_pLowFilter->processAndPauseFilter(pLpfInput,
162 pOutput, bufferParameters.samplesPerBuffer());
163 } else if (pLpfInput == pInput) {
164 // Both disabled
165 if (pOutput != pInput) {
166 // We need to copy pInput pOutput
167 SampleUtil::copy(pOutput, pInput, bufferParameters.samplesPerBuffer());
168 }
169 }
170
171 pState->m_loFreq = lpf;
172 pState->m_resonance = resonance;
173 pState->m_hiFreq = hpf;
174 pState->m_samplerate = bufferParameters.sampleRate();
175 }
176