1 #pragma once
2 
3 #include "effects/effectprocessor.h"
4 #include "engine/filters/enginefilterdelay.h"
5 #include "util/defs.h"
6 #include "util/math.h"
7 #include "util/sample.h"
8 #include "util/types.h"
9 
10 class LVMixEQEffectGroupStateConstants {
11   public:
12     LVMixEQEffectGroupStateConstants() = delete;
13 
14     static constexpr SINT kMaxDelay = 3300; // allows a 30 Hz filter at 97346;
15     static constexpr SINT kRampDone = -1;
16     static constexpr double kStartupLoFreq = 246;
17     static constexpr double kStartupHiFreq = 2484;
18 };
19 
20 template<class LPF>
21 class LVMixEQEffectGroupState : public EffectState {
22   public:
LVMixEQEffectGroupState(const mixxx::EngineParameters & bufferParameters)23     explicit LVMixEQEffectGroupState(const mixxx::EngineParameters& bufferParameters)
24             : EffectState(bufferParameters),
25               m_oldLow(1.0),
26               m_oldMid(1.0),
27               m_oldHigh(1.0),
28               m_rampHoldOff(LVMixEQEffectGroupStateConstants::kRampDone),
29               m_oldSampleRate(bufferParameters.sampleRate()),
30               m_loFreq(LVMixEQEffectGroupStateConstants::kStartupLoFreq),
31               m_hiFreq(LVMixEQEffectGroupStateConstants::kStartupHiFreq) {
32         m_pLowBuf = SampleUtil::alloc(bufferParameters.samplesPerBuffer());
33         m_pBandBuf = SampleUtil::alloc(bufferParameters.samplesPerBuffer());
34         m_pHighBuf = SampleUtil::alloc(bufferParameters.samplesPerBuffer());
35 
36         m_low1 = new LPF(bufferParameters.sampleRate(),
37                 LVMixEQEffectGroupStateConstants::kStartupLoFreq);
38         m_low2 = new LPF(bufferParameters.sampleRate(),
39                 LVMixEQEffectGroupStateConstants::kStartupHiFreq);
40         m_delay2 = new EngineFilterDelay<LVMixEQEffectGroupStateConstants::kMaxDelay>();
41         m_delay3 = new EngineFilterDelay<LVMixEQEffectGroupStateConstants::kMaxDelay>();
42         setFilters(bufferParameters.sampleRate(),
43                 LVMixEQEffectGroupStateConstants::kStartupLoFreq,
44                 LVMixEQEffectGroupStateConstants::kStartupHiFreq);
45     }
46 
~LVMixEQEffectGroupState()47     ~LVMixEQEffectGroupState() override {
48         delete m_low1;
49         delete m_low2;
50         delete m_delay2;
51         delete m_delay3;
52         SampleUtil::free(m_pLowBuf);
53         SampleUtil::free(m_pBandBuf);
54         SampleUtil::free(m_pHighBuf);
55     }
56 
setFilters(mixxx::audio::SampleRate sampleRate,double lowFreq,double highFreq)57     void setFilters(
58             mixxx::audio::SampleRate sampleRate,
59             double lowFreq,
60             double highFreq) {
61         SINT delayLow1 = m_low1->setFrequencyCornersForIntDelay(
62                 lowFreq / sampleRate, LVMixEQEffectGroupStateConstants::kMaxDelay);
63         SINT delayLow2 = m_low2->setFrequencyCornersForIntDelay(
64                 highFreq / sampleRate, LVMixEQEffectGroupStateConstants::kMaxDelay);
65 
66         m_delay2->setDelay((delayLow1 - delayLow2) * 2);
67         m_delay3->setDelay(delayLow1 * 2);
68         m_groupDelay = delayLow1 * 2;
69     }
70 
processChannel(const CSAMPLE * pInput,CSAMPLE * pOutput,SINT numSamples,mixxx::audio::SampleRate sampleRate,double dLow,double dMid,double dHigh,double loFreq,double hiFreq)71     void processChannel(
72             const CSAMPLE* pInput,
73             CSAMPLE* pOutput,
74             SINT numSamples,
75             mixxx::audio::SampleRate sampleRate,
76             double dLow,
77             double dMid,
78             double dHigh,
79             double loFreq,
80             double hiFreq) {
81         if (m_oldSampleRate != sampleRate ||
82                 (m_loFreq != loFreq) ||
83                 (m_hiFreq != hiFreq)) {
84             m_loFreq = loFreq;
85             m_hiFreq = hiFreq;
86             m_oldSampleRate = sampleRate;
87             setFilters(sampleRate, loFreq, hiFreq);
88         }
89 
90         // Since a Bessel Low pass Filter has a constant group delay in the pass band,
91         // we can subtract or add the filtered signal to the dry signal if we compensate this delay
92         // The dry signal represents the high gain
93         // Then the higher low pass is added and at least the lower low pass result.
94         auto fLow = static_cast<CSAMPLE>(dLow - dMid);
95         auto fMid = static_cast<CSAMPLE>(dMid - dHigh);
96         auto fHigh = static_cast<CSAMPLE>(dHigh);
97 
98         // Note: We do not call pauseFilter() here because this will introduce a
99         // buffer size-dependent start delay. During such start delay some unwanted
100         // frequencies are slipping though or wanted frequencies are damped.
101         // We know the exact group delay here so we can just hold off the ramping.
102         if (fHigh != 0 || m_oldHigh != 0) {
103             m_delay3->process(pInput, m_pHighBuf, numSamples);
104         }
105 
106         if (fMid != 0 || m_oldMid != 0) {
107             m_delay2->process(pInput, m_pBandBuf, numSamples);
108             m_low2->process(m_pBandBuf, m_pBandBuf, numSamples);
109         }
110 
111         if (fLow != 0 || m_oldLow != 0) {
112             m_low1->process(pInput, m_pLowBuf, numSamples);
113         }
114 
115         // Test code for comparing streams as two stereo channels
116         //for (SINT i = 0; i < numSamples; i +=2) {
117         //    pOutput[i] = pState->m_pLowBuf[i];
118         //    pOutput[i + 1] = pState->m_pBandBuf[i];
119         //}
120 
121         if (fLow == m_oldLow &&
122                 fMid == m_oldMid &&
123                 fHigh == m_oldHigh) {
124             SampleUtil::copy3WithGain(pOutput,
125                     m_pLowBuf, fLow,
126                     m_pBandBuf, fMid,
127                     m_pHighBuf, fHigh,
128                     numSamples);
129         } else {
130             SINT copySamples = 0;
131             SINT rampingSamples = numSamples;
132             if ((fLow != 0 && m_oldLow == 0) ||
133                     (fMid != 0 && m_oldMid == 0) ||
134                     (fHigh != 0 && m_oldHigh == 0)) {
135                 // we have just switched at least one filter on
136                 // Hold off ramping for the group delay
137                 if (m_rampHoldOff == LVMixEQEffectGroupStateConstants::kRampDone) {
138                     // multiply the group delay * 2 to ensure that the filter is
139                     // settled it is actually at a factor of 1,8 at default setting
140                     m_rampHoldOff = m_groupDelay * 2;
141                     // ensure that we have at least 128 samples for ramping
142                     // (the smallest buffer, that suits for de-clicking)
143                     SINT rampingSamples = numSamples - (m_rampHoldOff % numSamples);
144                     if (rampingSamples < 128) {
145                         m_rampHoldOff += rampingSamples;
146                     }
147                 }
148 
149                 // ramping is done in one of the following calls if
150                 // pState->m_rampHoldOff >= numSamples;
151                 copySamples = math_min(m_rampHoldOff, numSamples);
152                 m_rampHoldOff -= copySamples;
153                 rampingSamples = numSamples - copySamples;
154 
155                 SampleUtil::copy3WithGain(pOutput,
156                         m_pLowBuf, m_oldLow,
157                         m_pBandBuf, m_oldMid,
158                         m_pHighBuf, m_oldHigh,
159                         copySamples);
160             }
161 
162             if (rampingSamples) {
163                 SampleUtil::copy3WithRampingGain(&pOutput[copySamples],
164                         &m_pLowBuf[copySamples], m_oldLow, fLow,
165                         &m_pBandBuf[copySamples], m_oldMid, fMid,
166                         &m_pHighBuf[copySamples], m_oldHigh, fHigh,
167                         rampingSamples);
168 
169                 m_oldLow = fLow;
170                 m_oldMid = fMid;
171                 m_oldHigh = fHigh;
172                 m_rampHoldOff = LVMixEQEffectGroupStateConstants::kRampDone;
173             }
174         }
175     }
176 
processChannelAndPause(const CSAMPLE * pInput,CSAMPLE * pOutput,SINT numSamples)177     void processChannelAndPause(
178             const CSAMPLE* pInput,
179             CSAMPLE* pOutput,
180             SINT numSamples) {
181         // Note: We do not call pauseFilter() here because this will introduce a
182         // buffer size-dependent start delay. During such start delay some unwanted
183         // frequencies are slipping though or wanted frequencies are damped.
184         // We know the exact group delay here so we can just hold off the ramping.
185         m_delay3->processAndPauseFilter(pInput, m_pHighBuf, numSamples);
186 
187         if (m_oldMid != 0) {
188             m_delay2->processAndPauseFilter(pInput, m_pBandBuf, numSamples);
189             m_low2->processAndPauseFilter(m_pBandBuf, m_pBandBuf, numSamples);
190         }
191 
192         if (m_oldLow != 0) {
193             m_low1->processAndPauseFilter(pInput, m_pLowBuf, numSamples);
194         }
195 
196         SampleUtil::copy3WithRampingGain(pOutput,
197                 m_pLowBuf, m_oldLow, 0.0,
198                 m_pBandBuf, m_oldMid, 0.0,
199                 m_pHighBuf, m_oldHigh, 1.0,
200                 numSamples);
201     }
202 
203     /*
204 
205         SINT copySamples = 0;
206         SINT rampingSamples = numSamples;
207         if ((fLow && !m_oldLow) ||
208                 (fMid && !m_oldMid) ||
209                 (fHigh && !m_oldHigh)) {
210             // we have just switched at least one filter on
211             // Hold off ramping for the group delay
212             if (m_rampHoldOff == LVMixEQEffectGroupStateConstants::kRampDone) {
213                 // multiply the group delay * 2 to ensure that the filter is
214                 // settled it is actually at a factor of 1,8 at default setting
215                 m_rampHoldOff = m_groupDelay * 2;
216                 // ensure that we have at least 128 samples for ramping
217                 // (the smallest buffer, that suits for de-clicking)
218                 SINT rampingSamples = numSamples - (m_rampHoldOff % numSamples);
219                 if (rampingSamples < 128) {
220                     m_rampHoldOff += rampingSamples;
221                 }
222             }
223 
224             // ramping is done in one of the following calls if
225             // pState->m_rampHoldOff >= numSamples;
226             copySamples = math_min(m_rampHoldOff, numSamples);
227             m_rampHoldOff -= copySamples;
228             rampingSamples = numSamples - copySamples;
229 
230             SampleUtil::copy3WithGain(pOutput,
231                     m_pLowBuf, m_oldLow,
232                     m_pBandBuf, m_oldMid,
233                     m_pHighBuf, m_oldHigh,
234                     copySamples);
235         }
236 
237         if (rampingSamples) {
238             SampleUtil::copy3WithRampingGain(&pOutput[copySamples],
239                     &m_pLowBuf[copySamples], m_oldLow, fLow,
240                     &m_pBandBuf[copySamples], m_oldMid, fMid,
241                     &m_pHighBuf[copySamples], m_oldHigh, fHigh,
242                     rampingSamples);
243 
244             m_oldLow = fLow;
245             m_oldMid = fMid;
246             m_oldHigh = fHigh;
247             m_rampHoldOff = LVMixEQEffectGroupStateConstants::kRampDone;
248 
249         }
250     }
251 
252 */
253 
254   private:
255     LPF* m_low1;
256     LPF* m_low2;
257     EngineFilterDelay<LVMixEQEffectGroupStateConstants::kMaxDelay>* m_delay2;
258     EngineFilterDelay<LVMixEQEffectGroupStateConstants::kMaxDelay>* m_delay3;
259 
260     CSAMPLE_GAIN m_oldLow;
261     CSAMPLE_GAIN m_oldMid;
262     CSAMPLE_GAIN m_oldHigh;
263 
264     SINT m_rampHoldOff;
265     SINT m_groupDelay;
266 
267     mixxx::audio::SampleRate m_oldSampleRate;
268     double m_loFreq;
269     double m_hiFreq;
270 
271     CSAMPLE* m_pLowBuf;
272     CSAMPLE* m_pBandBuf;
273     CSAMPLE* m_pHighBuf;
274 };
275