Skip to content

Commit be4af96

Browse files
committed
Fix Comb filter
1 parent ee490ba commit be4af96

4 files changed

Lines changed: 58 additions & 11 deletions

File tree

examples/graphics/source/examples/FilterDemo.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,10 @@ class FrequencyResponsePlot : public yup::Component
531531
return;
532532
}
533533

534-
const int numPoints = 512;
534+
const int numPoints = isCombFilter() ? 4096 : 512;
535+
536+
minDb = isCombFilter() ? -80.0 : -60.0;
537+
maxDb = isCombFilter() ? 40.0 : 20.0;
535538

536539
responseData.clear();
537540
responseData.resize (numPoints);
@@ -629,7 +632,9 @@ class FrequencyResponsePlot : public yup::Component
629632
}
630633

631634
// Horizontal dB lines
632-
for (double db = -60.0; db <= 20.0; db += 20.0)
635+
const auto gridStepDb = isCombFilter() ? 40.0 : 20.0;
636+
637+
for (double db = minDb; db <= maxDb; db += gridStepDb)
633638
{
634639
float y = dbToY (db, bounds);
635640
g.strokeLine ({ bounds.getX(), y }, { bounds.getRight(), y });
@@ -700,7 +705,9 @@ class FrequencyResponsePlot : public yup::Component
700705
}
701706

702707
// dB labels
703-
for (double db = -60.0; db <= 20.0; db += 20.0)
708+
const auto gridStepDb = isCombFilter() ? 40.0 : 20.0;
709+
710+
for (double db = minDb; db <= maxDb; db += gridStepDb)
704711
{
705712
float y = dbToY (db, bounds);
706713
yup::String label = yup::String (db, 0) + " dB";
@@ -720,6 +727,11 @@ class FrequencyResponsePlot : public yup::Component
720727
return static_cast<float> (bounds.getBottom() - ratio * bounds.getHeight());
721728
}
722729

730+
bool isCombFilter() const
731+
{
732+
return dynamic_cast<yup::CombFilter<float>*> (filter.get()) != nullptr;
733+
}
734+
723735
std::shared_ptr<yup::FilterBase<float, double>> filter;
724736
std::vector<yup::Complex<float>> responseData;
725737
std::vector<yup::Complex<float>> phaseData;

modules/yup_dsp/filters/yup_CombFilter.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,13 @@ class CombFilter : public FilterBase<SampleType, CoeffType>
209209
*/
210210
Complex<CoeffType> getComplexResponse (CoeffType responseFrequency) const override
211211
{
212-
const auto omega = frequencyToAngular (responseFrequency, static_cast<CoeffType> (this->sampleRate));
213-
const auto delay = getDelayForResponse();
214-
const auto delayed = polar (static_cast<CoeffType> (1), -omega * delay);
212+
const auto delayed = getFractionalDelayResponse (responseFrequency);
215213
const auto denominator = Complex<CoeffType> (static_cast<CoeffType> (1)) - delayLineFeedback * delayed;
216214

217215
if (std::abs (denominator) <= std::numeric_limits<CoeffType>::epsilon())
218-
return Complex<CoeffType> (static_cast<CoeffType> (1));
216+
return Complex<CoeffType> (static_cast<CoeffType> (1))
217+
+ (static_cast<CoeffType> (0.5) * delayed)
218+
/ (denominator + Complex<CoeffType> (std::numeric_limits<CoeffType>::epsilon()));
219219

220220
return Complex<CoeffType> (static_cast<CoeffType> (1))
221221
+ (static_cast<CoeffType> (0.5) * delayed) / denominator;
@@ -359,6 +359,26 @@ class CombFilter : public FilterBase<SampleType, CoeffType>
359359
static_cast<CoeffType> (this->sampleRate) / frequency);
360360
}
361361

362+
Complex<CoeffType> getFractionalDelayResponse (CoeffType responseFrequency) const noexcept
363+
{
364+
const auto delay = getDelayForResponse();
365+
const auto readDelay = static_cast<CoeffType> (std::ceil (delay));
366+
const auto fraction = readDelay - delay;
367+
const auto fraction2 = fraction * fraction;
368+
const auto fraction3 = fraction2 * fraction;
369+
370+
const auto weight0 = static_cast<CoeffType> (-0.5) * fraction + fraction2 - static_cast<CoeffType> (0.5) * fraction3;
371+
const auto weight1 = static_cast<CoeffType> (1) - static_cast<CoeffType> (2.5) * fraction2 + static_cast<CoeffType> (1.5) * fraction3;
372+
const auto weight2 = static_cast<CoeffType> (0.5) * fraction + static_cast<CoeffType> (2) * fraction2 - static_cast<CoeffType> (1.5) * fraction3;
373+
const auto weight3 = static_cast<CoeffType> (-0.5) * fraction2 + static_cast<CoeffType> (0.5) * fraction3;
374+
const auto omega = frequencyToAngular (responseFrequency, static_cast<CoeffType> (this->sampleRate));
375+
376+
return weight0 * polar (static_cast<CoeffType> (1), -omega * (readDelay + static_cast<CoeffType> (1)))
377+
+ weight1 * polar (static_cast<CoeffType> (1), -omega * readDelay)
378+
+ weight2 * polar (static_cast<CoeffType> (1), -omega * (readDelay - static_cast<CoeffType> (1)))
379+
+ weight3 * polar (static_cast<CoeffType> (1), -omega * (readDelay - static_cast<CoeffType> (2)));
380+
}
381+
362382
//==============================================================================
363383
std::size_t delayLineSizeInSamples = defaultDelayLineSize;
364384
std::size_t delayLineMask = defaultDelayLineSize - 1;

modules/yup_dsp/yup_dsp.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,6 @@
139139
#include "frequency/yup_FFTProcessor.h"
140140
#include "frequency/yup_SpectrumAnalyzerState.h"
141141

142-
// Wavetable and antialiased oscillators (before FilterDesigner for AA-IIR2 coefficient types)
143-
#include "oscillators/yup_Wavetable.h"
144-
#include "oscillators/yup_AaIir2Oscillator.h"
145-
146142
// Base filter interfaces and common structures
147143
#include "base/yup_FilterMode.h"
148144
#include "base/yup_FilterBase.h"

tests/yup_dsp/yup_CombFilter.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,22 @@ TEST (CombFilterTests, ComplexResponseIsFinite)
124124
EXPECT_TRUE (std::isfinite (response.real()));
125125
EXPECT_TRUE (std::isfinite (response.imag()));
126126
}
127+
128+
TEST (CombFilterTests, ComplexResponseMatchesIntegerDelayFeedForwardComb)
129+
{
130+
CombFilter<double> filter;
131+
filter.prepare (sampleRate, blockSize);
132+
filter.setParameters (sampleRate / 8.0, 0.0, 0.0, sampleRate);
133+
134+
EXPECT_NEAR (std::abs (filter.getComplexResponse (0.0)), 1.5, 1e-9);
135+
EXPECT_NEAR (std::abs (filter.getComplexResponse (sampleRate / 16.0)), 0.5, 1e-9);
136+
}
137+
138+
TEST (CombFilterTests, ComplexResponseIncludesFeedbackResonance)
139+
{
140+
CombFilter<double> filter;
141+
filter.prepare (sampleRate, blockSize);
142+
filter.setParameters (sampleRate / 8.0, 0.81, 0.0, sampleRate);
143+
144+
EXPECT_NEAR (std::abs (filter.getComplexResponse (sampleRate / 8.0)), 6.0, 1e-9);
145+
}

0 commit comments

Comments
 (0)