1 #include "effects/builtin/threebandbiquadeqeffect.h"
2
3 #include "effects/builtin/equalizer_util.h"
4 #include "util/math.h"
5
6 namespace {
7
8 // The defaults are tweaked to match the Xone:23 EQ
9 // but allow 12 dB boost instead of just 6 dB
10 static const int kStartupSamplerate = 44100;
11 static const double kMinimumFrequency = 10.0;
12 static const double kMaximumFrequency = kStartupSamplerate / 2;
13 static const double kStartupLoFreq = 50.0;
14 static const double kStartupMidFreq = 1100.0;
15 static const double kStartupHiFreq = 12000.0;
16 static const double kQBoost = 0.3;
17 static const double kQKill = 0.9;
18 static const double kQKillShelve = 0.4;
19 static const double kBoostGain = 12;
20 static const double kKillGain = -26;
21
22
getCenterFrequency(double low,double high)23 double getCenterFrequency(double low, double high) {
24 double scaleLow = log10(low);
25 double scaleHigh = log10(high);
26
27 double scaleCenter = (scaleHigh - scaleLow) / 2 + scaleLow;
28 return pow(10, scaleCenter);
29 }
30
knobValueToBiquadGainDb(double value,bool kill)31 double knobValueToBiquadGainDb (double value, bool kill) {
32 if (kill) {
33 return kKillGain;
34 }
35
36 double ret = value - 1;
37 if (ret >= 0) {
38 ret *= kBoostGain;
39 } else {
40 ret *= -kKillGain;
41 }
42 return ret;
43 }
44
45 } // anonymous namespace
46
47
48 // static
getId()49 QString ThreeBandBiquadEQEffect::getId() {
50 return "org.mixxx.effects.threebandbiquadeq";
51 }
52
53 // static
getManifest()54 EffectManifestPointer ThreeBandBiquadEQEffect::getManifest() {
55 EffectManifestPointer pManifest(new EffectManifest());
56 pManifest->setId(getId());
57 pManifest->setName(QObject::tr("Biquad Equalizer"));
58 pManifest->setShortName(QObject::tr("BQ EQ"));
59 pManifest->setAuthor("The Mixxx Team");
60 pManifest->setVersion("1.0");
61 pManifest->setDescription(QObject::tr(
62 "A 3-band Equalizer with two biquad bell filters, a shelving high pass and kill switches.") +
63 " " + EqualizerUtil::adjustFrequencyShelvesTip());
64 pManifest->setEffectRampsFromDry(true);
65 pManifest->setIsMixingEQ(true);
66
67 EqualizerUtil::createCommonParameters(pManifest.data(), true);
68 return pManifest;
69 }
70
ThreeBandBiquadEQEffectGroupState(const mixxx::EngineParameters & bufferParameters)71 ThreeBandBiquadEQEffectGroupState::ThreeBandBiquadEQEffectGroupState(
72 const mixxx::EngineParameters& bufferParameters)
73 : EffectState(bufferParameters),
74 m_tempBuf(bufferParameters.samplesPerBuffer()),
75 m_oldLowBoost(0),
76 m_oldMidBoost(0),
77 m_oldHighBoost(0),
78 m_oldLowCut(0),
79 m_oldMidCut(0),
80 m_oldHighCut(0),
81 m_loFreqCorner(0),
82 m_highFreqCorner(0),
83 m_oldSampleRate(bufferParameters.sampleRate()) {
84
85 // Initialize the filters with default parameters
86
87 m_lowBoost = std::make_unique<EngineFilterBiquad1Peaking>(
88 bufferParameters.sampleRate() , kStartupLoFreq, kQBoost);
89 m_midBoost = std::make_unique<EngineFilterBiquad1Peaking>(
90 bufferParameters.sampleRate() , kStartupMidFreq, kQBoost);
91 m_highBoost = std::make_unique<EngineFilterBiquad1Peaking>(
92 bufferParameters.sampleRate() , kStartupHiFreq, kQBoost);
93 m_lowCut = std::make_unique<EngineFilterBiquad1Peaking>(
94 bufferParameters.sampleRate() , kStartupLoFreq, kQKill);
95 m_midCut = std::make_unique<EngineFilterBiquad1Peaking>(
96 bufferParameters.sampleRate() , kStartupMidFreq, kQKill);
97 m_highCut = std::make_unique<EngineFilterBiquad1HighShelving>(
98 bufferParameters.sampleRate() , kStartupHiFreq / 2, kQKillShelve);
99 }
100
~ThreeBandBiquadEQEffectGroupState()101 ThreeBandBiquadEQEffectGroupState::~ThreeBandBiquadEQEffectGroupState() {
102 }
103
setFilters(int sampleRate,double lowFreqCorner,double highFreqCorner)104 void ThreeBandBiquadEQEffectGroupState::setFilters(
105 int sampleRate, double lowFreqCorner, double highFreqCorner) {
106
107 double lowCenter = getCenterFrequency(kMinimumFrequency, lowFreqCorner);
108 double midCenter = getCenterFrequency(lowFreqCorner, highFreqCorner);
109 double highCenter = getCenterFrequency(highFreqCorner, kMaximumFrequency);
110
111
112 m_lowBoost->setFrequencyCorners(
113 sampleRate, lowCenter, kQBoost, m_oldLowBoost);
114 m_midBoost->setFrequencyCorners(
115 sampleRate, midCenter, kQBoost, m_oldMidBoost);
116 m_highBoost->setFrequencyCorners(
117 sampleRate, highCenter, kQBoost, m_oldHighBoost);
118 m_lowCut->setFrequencyCorners(
119 sampleRate, lowCenter, kQKill, m_oldLowCut);
120 m_midCut->setFrequencyCorners(
121 sampleRate, midCenter, kQKill, m_oldMidCut);
122 m_highCut->setFrequencyCorners(
123 sampleRate, highCenter / 2, kQKillShelve, m_oldHighCut);
124
125 }
126
ThreeBandBiquadEQEffect(EngineEffect * pEffect)127 ThreeBandBiquadEQEffect::ThreeBandBiquadEQEffect(EngineEffect* pEffect)
128 : m_pPotLow(pEffect->getParameterById("low")),
129 m_pPotMid(pEffect->getParameterById("mid")),
130 m_pPotHigh(pEffect->getParameterById("high")),
131 m_pKillLow(pEffect->getParameterById("killLow")),
132 m_pKillMid(pEffect->getParameterById("killMid")),
133 m_pKillHigh(pEffect->getParameterById("killHigh")) {
134 m_pLoFreqCorner = std::make_unique<ControlProxy>("[Mixer Profile]", "LoEQFrequency");
135 m_pHiFreqCorner = std::make_unique<ControlProxy>("[Mixer Profile]", "HiEQFrequency");
136 }
137
~ThreeBandBiquadEQEffect()138 ThreeBandBiquadEQEffect::~ThreeBandBiquadEQEffect() {
139 }
140
processChannel(const ChannelHandle & handle,ThreeBandBiquadEQEffectGroupState * pState,const CSAMPLE * pInput,CSAMPLE * pOutput,const mixxx::EngineParameters & bufferParameters,const EffectEnableState enableState,const GroupFeatureState & groupFeatures)141 void ThreeBandBiquadEQEffect::processChannel(
142 const ChannelHandle& handle,
143 ThreeBandBiquadEQEffectGroupState* pState,
144 const CSAMPLE* pInput,
145 CSAMPLE* pOutput,
146 const mixxx::EngineParameters& bufferParameters,
147 const EffectEnableState enableState,
148 const GroupFeatureState& groupFeatures) {
149 Q_UNUSED(handle);
150 Q_UNUSED(groupFeatures);
151
152 if (pState->m_oldSampleRate != bufferParameters.sampleRate() ||
153 (pState->m_loFreqCorner != m_pLoFreqCorner->get()) ||
154 (pState->m_highFreqCorner != m_pHiFreqCorner->get())) {
155 pState->m_loFreqCorner = m_pLoFreqCorner->get();
156 pState->m_highFreqCorner = m_pHiFreqCorner->get();
157 pState->m_oldSampleRate = bufferParameters.sampleRate();
158 pState->setFilters(bufferParameters.sampleRate(), pState->m_loFreqCorner, pState->m_highFreqCorner);
159 }
160
161
162 // Ramp to dry, when disabling, this will ramp from dry when enabling as well
163 double bqGainLow = 0;
164 double bqGainMid = 0;
165 double bqGainHigh = 0;
166 if (enableState != EffectEnableState::Disabling) {
167 bqGainLow = knobValueToBiquadGainDb(
168 m_pPotLow->value(), m_pKillLow->toBool());
169 bqGainMid = knobValueToBiquadGainDb(
170 m_pPotMid->value(), m_pKillMid->toBool());
171 bqGainHigh = knobValueToBiquadGainDb(
172 m_pPotHigh->value(), m_pKillHigh->toBool());
173 }
174
175 int activeFilters = 0;
176
177 if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) {
178 ++activeFilters;
179 }
180 if (bqGainLow < 0.0 || pState->m_oldLowCut < 0.0) {
181 ++activeFilters;
182 }
183 if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) {
184 ++activeFilters;
185 }
186 if (bqGainMid < 0.0 || pState->m_oldMidCut < 0.0) {
187 ++activeFilters;
188 }
189 if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) {
190 ++activeFilters;
191 }
192 if (bqGainHigh < 0.0 || pState->m_oldHighCut < 0.0) {
193 ++activeFilters;
194 }
195
196 QVarLengthArray<const CSAMPLE*, 6> inBuffer;
197 QVarLengthArray<CSAMPLE*, 6> outBuffer;
198
199 if (activeFilters % 2 == 0) {
200 inBuffer.append(pInput);
201 outBuffer.append(pState->m_tempBuf.data());
202
203 inBuffer.append(pState->m_tempBuf.data());
204 outBuffer.append(pOutput);
205
206 inBuffer.append(pOutput);
207 outBuffer.append(pState->m_tempBuf.data());
208
209 inBuffer.append(pState->m_tempBuf.data());
210 outBuffer.append(pOutput);
211
212 inBuffer.append(pOutput);
213 outBuffer.append(pState->m_tempBuf.data());
214
215 inBuffer.append(pState->m_tempBuf.data());
216 outBuffer.append(pOutput);
217 }
218 else
219 {
220 inBuffer.append(pInput);
221 outBuffer.append(pOutput);
222
223 inBuffer.append(pOutput);
224 outBuffer.append(pState->m_tempBuf.data());
225
226 inBuffer.append(pState->m_tempBuf.data());
227 outBuffer.append(pOutput);
228
229 inBuffer.append(pOutput);
230 outBuffer.append(pState->m_tempBuf.data());
231
232 inBuffer.append(pState->m_tempBuf.data());
233 outBuffer.append(pOutput);
234
235 inBuffer.append(pOutput);
236 outBuffer.append(pState->m_tempBuf.data());
237 }
238
239 int bufIndex = 0;
240
241 if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) {
242 if (bqGainLow != pState->m_oldLowBoost) {
243 double lowCenter = getCenterFrequency(
244 kMinimumFrequency, pState->m_loFreqCorner);
245 pState->m_lowBoost->setFrequencyCorners(
246 bufferParameters.sampleRate(), lowCenter, kQBoost, bqGainLow);
247 pState->m_oldLowBoost = bqGainLow;
248 }
249 if (bqGainLow > 0.0) {
250 pState->m_lowBoost->process(
251 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
252 } else {
253 pState->m_lowBoost->processAndPauseFilter(
254 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
255 }
256 ++bufIndex;
257 } else {
258 pState->m_lowBoost->pauseFilter();
259 }
260
261 if (bqGainLow < 0.0 || pState->m_oldLowCut < 0.0) {
262 if (bqGainLow != pState->m_oldLowCut) {
263 double lowCenter = getCenterFrequency(
264 kMinimumFrequency, pState->m_loFreqCorner);
265 pState->m_lowCut->setFrequencyCorners(
266 bufferParameters.sampleRate(), lowCenter, kQKill, bqGainLow);
267 pState->m_oldLowCut = bqGainLow;
268 }
269 if (bqGainLow < 0.0) {
270 pState->m_lowCut->process(
271 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
272 } else {
273 pState->m_lowCut->processAndPauseFilter(
274 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
275 }
276 ++bufIndex;
277 } else {
278 pState->m_lowCut->pauseFilter();
279 }
280
281 if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) {
282 if (bqGainMid != pState->m_oldMidBoost) {
283 double midCenter = getCenterFrequency(
284 pState->m_loFreqCorner, pState->m_highFreqCorner);
285 pState->m_midBoost->setFrequencyCorners(
286 bufferParameters.sampleRate(), midCenter, kQBoost, bqGainMid);
287 pState->m_oldMidBoost = bqGainMid;
288 }
289 if (bqGainMid > 0.0) {
290 pState->m_midBoost->process(
291 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
292 } else {
293 pState->m_midBoost->processAndPauseFilter(
294 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
295 }
296 ++bufIndex;
297 } else {
298 pState->m_midBoost->pauseFilter();
299 }
300
301
302 if (bqGainMid < 0.0 || pState->m_oldMidCut < 0.0) {
303 if (bqGainMid != pState->m_oldMidCut) {
304 double midCenter = getCenterFrequency(
305 pState->m_loFreqCorner, pState->m_highFreqCorner);
306 pState->m_midCut->setFrequencyCorners(
307 bufferParameters.sampleRate(), midCenter, kQKill, bqGainMid);
308 pState->m_oldMidCut = bqGainMid;
309 }
310 if (bqGainMid < 0.0) {
311 pState->m_midCut->process(
312 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
313 } else {
314 pState->m_midCut->processAndPauseFilter(
315 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
316 }
317 ++bufIndex;
318 } else {
319 pState->m_midCut->pauseFilter();
320 }
321
322 if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) {
323 if (bqGainHigh != pState->m_oldHighBoost) {
324 double highCenter = getCenterFrequency(
325 pState->m_highFreqCorner, kMaximumFrequency);
326 pState->m_highBoost->setFrequencyCorners(
327 bufferParameters.sampleRate(), highCenter, kQBoost, bqGainHigh);
328 pState->m_oldHighBoost = bqGainHigh;
329 }
330 if (bqGainHigh > 0.0) {
331 pState->m_highBoost->process(
332 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
333 } else {
334 pState->m_highBoost->processAndPauseFilter(
335 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
336 }
337 ++bufIndex;
338 } else {
339 pState->m_highBoost->pauseFilter();
340 }
341
342 if (bqGainHigh < 0.0 || pState->m_oldHighCut < 0.0) {
343 if (bqGainHigh != pState->m_oldHighCut) {
344 double highCenter = getCenterFrequency(
345 pState->m_highFreqCorner, kMaximumFrequency);
346 pState->m_highCut->setFrequencyCorners(
347 bufferParameters.sampleRate(), highCenter / 2, kQKillShelve, bqGainHigh);
348 pState->m_oldHighCut = bqGainHigh;
349 }
350 if (bqGainHigh < 0.0) {
351 pState->m_highCut->process(
352 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
353 } else {
354 pState->m_highCut->processAndPauseFilter(
355 inBuffer[bufIndex], outBuffer[bufIndex], bufferParameters.samplesPerBuffer());
356 }
357 ++bufIndex;
358 } else {
359 pState->m_highCut->pauseFilter();
360 }
361
362 if (activeFilters == 0) {
363 SampleUtil::copy(pOutput, pInput, bufferParameters.samplesPerBuffer());
364 }
365
366 if (enableState == EffectEnableState::Disabling) {
367 pState->m_lowBoost->pauseFilter();
368 pState->m_midBoost->pauseFilter();
369 pState->m_highBoost->pauseFilter();
370 pState->m_lowCut->pauseFilter();
371 pState->m_midCut->pauseFilter();
372 pState->m_highCut->pauseFilter();
373 }
374 }
375