Skip to content

Commit 3e15b1c

Browse files
committed
More tweaks
1 parent bd8322b commit 3e15b1c

File tree

11 files changed

+265
-331
lines changed

11 files changed

+265
-331
lines changed

examples/graphics/source/examples/FilterDemo.h

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,12 +1236,11 @@ class FilterDemo
12361236
}
12371237
else if (auto svf = std::dynamic_pointer_cast<yup::StateVariableFilter<float>> (currentAudioFilter))
12381238
{
1239-
svf->setParameters (getSvfMode (currentResponseTypeId), freq, 0.707 + q * (10.0f - 0.707), currentSampleRate);
1239+
svf->setParameters (getFilterMode (currentResponseTypeId), freq, 0.707 + q * (10.0f - 0.707), currentSampleRate);
12401240
}
12411241
else if (auto fof = std::dynamic_pointer_cast<yup::FirstOrderFilter<float>> (currentAudioFilter))
12421242
{
1243-
auto coeffs = getFirstOrderCoefficients (currentResponseTypeId, freq, gain, currentSampleRate);
1244-
fof->setCoefficients (coeffs);
1243+
fof->setParameters (getFilterMode (currentResponseTypeId), freq, gain, currentSampleRate);
12451244
}
12461245
else if (auto bf = std::dynamic_pointer_cast<yup::ButterworthFilter<float>> (currentAudioFilter))
12471246
{
@@ -1270,12 +1269,11 @@ class FilterDemo
12701269
}
12711270
else if (auto svf = std::dynamic_pointer_cast<yup::StateVariableFilter<float>> (currentUIFilter))
12721271
{
1273-
svf->setParameters (getSvfMode (currentResponseTypeId), freq, 0.707 + q * (10.0f - 0.707), currentSampleRate);
1272+
svf->setParameters (getFilterMode (currentResponseTypeId), freq, 0.707 + q * (10.0f - 0.707), currentSampleRate);
12741273
}
12751274
else if (auto fof = std::dynamic_pointer_cast<yup::FirstOrderFilter<float>> (currentUIFilter))
12761275
{
1277-
auto coeffs = getFirstOrderCoefficients (currentResponseTypeId, freq, gain, currentSampleRate);
1278-
fof->setCoefficients (coeffs);
1276+
fof->setParameters (getFilterMode (currentResponseTypeId), freq, gain, currentSampleRate);
12791277
}
12801278
else if (auto bf = std::dynamic_pointer_cast<yup::ButterworthFilter<float>> (currentUIFilter))
12811279
{
@@ -1405,44 +1403,6 @@ class FilterDemo
14051403
}
14061404
}
14071405

1408-
yup::StateVariableFilter<float>::Mode getSvfMode (int responseTypeId)
1409-
{
1410-
switch (responseTypeId)
1411-
{
1412-
case 1:
1413-
return yup::StateVariableFilter<float>::Mode::lowpass;
1414-
case 2:
1415-
return yup::StateVariableFilter<float>::Mode::highpass;
1416-
case 3:
1417-
case 4:
1418-
// SVF only has one bandpass mode, use it for both CSG and CPG
1419-
return yup::StateVariableFilter<float>::Mode::bandpass;
1420-
case 5:
1421-
return yup::StateVariableFilter<float>::Mode::notch;
1422-
default:
1423-
return yup::StateVariableFilter<float>::Mode::lowpass;
1424-
}
1425-
}
1426-
1427-
yup::FirstOrderCoefficients<double> getFirstOrderCoefficients (int responseTypeId, double freq, double gain, double sampleRate)
1428-
{
1429-
switch (responseTypeId)
1430-
{
1431-
case 1:
1432-
return yup::FilterDesigner<double>::designFirstOrderLowpass (freq, sampleRate);
1433-
case 2:
1434-
return yup::FilterDesigner<double>::designFirstOrderHighpass (freq, sampleRate);
1435-
case 7:
1436-
return yup::FilterDesigner<double>::designFirstOrderLowShelf (freq, gain, sampleRate);
1437-
case 8:
1438-
return yup::FilterDesigner<double>::designFirstOrderHighShelf (freq, gain, sampleRate);
1439-
case 9:
1440-
return yup::FilterDesigner<double>::designFirstOrderAllpass (freq, sampleRate);
1441-
default:
1442-
return yup::FilterDesigner<double>::designFirstOrderLowpass (freq, sampleRate);
1443-
}
1444-
}
1445-
14461406
// Audio components
14471407
yup::AudioDeviceManager deviceManager;
14481408
WhiteNoiseGenerator noiseGenerator;

modules/yup_dsp/base/yup_FilterBase.h

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,6 @@
2424
namespace yup
2525
{
2626

27-
//==============================================================================
28-
/**
29-
Filter capabilities trait to specify which modes a filter type supports.
30-
31-
This allows compile-time checking of filter capabilities and runtime
32-
validation of mode compatibility.
33-
*/
34-
template <typename FilterType>
35-
struct FilterCapabilities
36-
{
37-
static constexpr auto supportedModes =
38-
FilterMode::lowpass | FilterMode::highpass | FilterMode::bandpass | FilterMode::bandstop |
39-
FilterMode::peak | FilterMode::lowshelf | FilterMode::highshelf |
40-
FilterMode::allpass;
41-
};
42-
4327
//==============================================================================
4428
/**
4529
Base interface for all digital filters.
@@ -72,6 +56,19 @@ class FilterBase
7256
/** Virtual destructor */
7357
virtual ~FilterBase() = default;
7458

59+
//==============================================================================
60+
virtual FilterModeType getSupportedModes() const noexcept
61+
{
62+
return FilterMode::lowpass | FilterMode::highpass | FilterMode::bandpass | FilterMode::bandstop |
63+
FilterMode::peak | FilterMode::lowshelf | FilterMode::highshelf | FilterMode::allpass;
64+
}
65+
66+
67+
virtual bool supportsMode (FilterModeType mode) const noexcept
68+
{
69+
return getSupportedModes().test (mode);
70+
}
71+
7572
//==============================================================================
7673
/** Resets the filter's internal state to zero */
7774
virtual void reset() noexcept = 0;
@@ -84,6 +81,7 @@ class FilterBase
8481
*/
8582
virtual void prepare (double sampleRate, int maximumBlockSize) = 0;
8683

84+
//==============================================================================
8785
/**
8886
Processes a single sample.
8987
@@ -115,6 +113,14 @@ class FilterBase
115113
}
116114

117115
//==============================================================================
116+
/**
117+
Returns the complex frequency response at the given frequency.
118+
119+
@param frequency The frequency in Hz
120+
@returns The complex frequency response
121+
*/
122+
virtual Complex<CoeffType> getComplexResponse (CoeffType frequency) const = 0;
123+
118124
/**
119125
Returns the magnitude response at the given frequency.
120126
@@ -139,14 +145,6 @@ class FilterBase
139145
return std::arg (response);
140146
}
141147

142-
/**
143-
Returns the complex frequency response at the given frequency.
144-
145-
@param frequency The frequency in Hz
146-
@returns The complex frequency response
147-
*/
148-
virtual Complex<CoeffType> getComplexResponse (CoeffType frequency) const = 0;
149-
150148
//==============================================================================
151149
/**
152150
Returns the poles and zeros of this filter.

modules/yup_dsp/base/yup_FilterCharacteristics.h

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ namespace yup
2626

2727
//==============================================================================
2828

29+
/**
30+
Calculate the magnitude response of a filter.
31+
32+
@param filter The filter to calculate the magnitude response of.
33+
@param buffer The buffer to store the magnitude response in.
34+
@param minFreq The minimum frequency to calculate the response at.
35+
@param maxFreq The maximum frequency to calculate the response at.
36+
*/
2937
template <typename FloatType, typename FilterType>
3038
void calculateFilterMagnitudeResponse (FilterType& filter, Span<Complex<FloatType>> buffer, double minFreq, double maxFreq)
3139
{
@@ -36,10 +44,9 @@ void calculateFilterMagnitudeResponse (FilterType& filter, Span<Complex<FloatTyp
3644
const double freq = minFreq * std::pow (maxFreq / minFreq, ratio);
3745

3846
// Get complex response
39-
auto response = filter.getComplexResponse (freq);
47+
auto magnitude = filter.getMagnitudeResponse (freq);
4048

4149
// Calculate magnitude in dB
42-
double magnitude = std::abs (response);
4350
double magnitudeDb = 20.0 * std::log10 (yup::jmax (magnitude, 1e-12));
4451

4552
buffer[i] = { static_cast<FloatType> (freq), static_cast<FloatType> (magnitudeDb) };
@@ -48,6 +55,15 @@ void calculateFilterMagnitudeResponse (FilterType& filter, Span<Complex<FloatTyp
4855

4956
//==============================================================================
5057

58+
/**
59+
Calculate the phase response of a filter.
60+
61+
@param filter The filter to calculate the phase response of.
62+
@param buffer The buffer to store the phase response in.
63+
@param minFreq The minimum frequency to calculate the response at.
64+
@param maxFreq The maximum frequency to calculate the response at.
65+
*/
66+
5167
template <typename FloatType, typename FilterType>
5268
void calculateFilterPhaseResponse (FilterType& filter, Span<Complex<FloatType>> buffer, double minFreq, double maxFreq)
5369
{
@@ -58,10 +74,9 @@ void calculateFilterPhaseResponse (FilterType& filter, Span<Complex<FloatType>>
5874
const double freq = minFreq * std::pow (maxFreq / minFreq, ratio);
5975

6076
// Get complex response
61-
auto response = filter.getComplexResponse (freq);
77+
auto phaseRad = filter.getPhaseResponse (freq);
6278

6379
// Calculate phase in degrees
64-
double phaseRad = std::arg (response);
6580
double phaseDeg = phaseRad * 180.0 / yup::MathConstants<double>::pi;
6681

6782
buffer[i] = { static_cast<FloatType> (freq), static_cast<FloatType> (phaseDeg) };
@@ -70,6 +85,15 @@ void calculateFilterPhaseResponse (FilterType& filter, Span<Complex<FloatType>>
7085

7186
//==============================================================================
7287

88+
/**
89+
Calculate the group delay of a filter.
90+
91+
@param filter The filter to calculate the group delay of.
92+
@param buffer The buffer to store the group delay in.
93+
@param minFreq The minimum frequency to calculate the response at.
94+
@param maxFreq The maximum frequency to calculate the response at.
95+
@param sampleRate The sample rate of the filter.
96+
*/
7397
template <typename FloatType, typename FilterType>
7498
void calculateFilterGroupDelay (FilterType& filter, Span<Complex<FloatType>> buffer, double minFreq, double maxFreq, double sampleRate)
7599
{
@@ -78,26 +102,23 @@ void calculateFilterGroupDelay (FilterType& filter, Span<Complex<FloatType>> buf
78102
// Logarithmic frequency sweep
79103
const double ratio = static_cast<double> (i) / (buffer.size() - 1);
80104
const double freq = minFreq * std::pow (maxFreq / minFreq, ratio);
105+
const double deltaFreq = freq * 0.01; // Small frequency step
81106

82107
// Calculate group delay (numerical derivative of phase)
83108
double groupDelay = 0.0;
84109
if (i > 0 && i < buffer.size() - 1)
85110
{
86-
const double deltaFreq = freq * 0.01; // Small frequency step
87-
auto responseLow = filter.getComplexResponse (freq - deltaFreq);
88-
auto responseHigh = filter.getComplexResponse (freq + deltaFreq);
89-
90-
double phaseLow = std::arg (responseLow);
91-
double phaseHigh = std::arg (responseHigh);
111+
auto phaseLow = filter.getPhaseResponse (freq - deltaFreq);
112+
auto phaseHigh = filter.getPhaseResponse (freq + deltaFreq);
92113

93114
// Unwrap phase difference
94115
double phaseDiff = phaseHigh - phaseLow;
95116
while (phaseDiff > yup::MathConstants<double>::pi)
96-
phaseDiff -= 2.0 * yup::MathConstants<double>::pi;
117+
phaseDiff -= yup::MathConstants<double>::twoPi;
97118
while (phaseDiff < -yup::MathConstants<double>::pi)
98-
phaseDiff += 2.0 * yup::MathConstants<double>::pi;
119+
phaseDiff += yup::MathConstants<double>::twoPi;
99120

100-
groupDelay = -phaseDiff / (2.0 * deltaFreq * 2.0 * yup::MathConstants<double>::pi) * sampleRate;
121+
groupDelay = -phaseDiff / (2.0 * deltaFreq * yup::MathConstants<double>::twoPi) * sampleRate;
101122
}
102123

103124
buffer[i] = { static_cast<FloatType> (freq), static_cast<FloatType> (groupDelay) };

modules/yup_dsp/base/yup_FilterMode.h

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ namespace yup
3333
*/
3434
namespace FilterModeFlags
3535
{
36-
struct lowpass; /**< Low-pass filter */
37-
struct highpass; /**< High-pass filter */
38-
struct bandpassCsg; /**< Band-pass filter (constant skirt gain, peak gain = Q) */
39-
struct bandpassCpg; /**< Band-pass filter (constant peak gain = 0dB) */
40-
struct bandstop; /**< Band-stop (notch) filter */
41-
struct peak; /**< Peaking filter */
42-
struct lowshelf; /**< Low-shelf filter */
43-
struct highshelf; /**< High-shelf filter */
44-
struct allpass; /**< All-pass filter */
45-
}
36+
struct lowpass; /**< Low-pass filter */
37+
struct highpass; /**< High-pass filter */
38+
struct bandpassCsg; /**< Band-pass filter (constant skirt gain, peak gain = Q) */
39+
struct bandpassCpg; /**< Band-pass filter (constant peak gain = 0dB) */
40+
struct bandstop; /**< Band-stop (notch) filter */
41+
struct peak; /**< Peaking filter */
42+
struct lowshelf; /**< Low-shelf filter */
43+
struct highshelf; /**< High-shelf filter */
44+
struct allpass; /**< All-pass filter */
45+
} // namespace FilterModeFlags
4646

4747
/**
4848
Type-safe filter mode using FlagSet.
@@ -51,19 +51,20 @@ namespace FilterModeFlags
5151
while maintaining type safety and enabling compile-time capability checking.
5252
*/
5353
using FilterModeType = FlagSet<uint32_t,
54-
FilterModeFlags::lowpass,
55-
FilterModeFlags::highpass,
56-
FilterModeFlags::bandpassCsg,
57-
FilterModeFlags::bandpassCpg,
58-
FilterModeFlags::bandstop,
59-
FilterModeFlags::peak,
60-
FilterModeFlags::lowshelf,
61-
FilterModeFlags::highshelf,
62-
FilterModeFlags::allpass>;
54+
FilterModeFlags::lowpass,
55+
FilterModeFlags::highpass,
56+
FilterModeFlags::bandpassCsg,
57+
FilterModeFlags::bandpassCpg,
58+
FilterModeFlags::bandstop,
59+
FilterModeFlags::peak,
60+
FilterModeFlags::lowshelf,
61+
FilterModeFlags::highshelf,
62+
FilterModeFlags::allpass>;
6363

6464
//==============================================================================
6565
/** Pre-defined filter modes for convenience */
66-
namespace FilterMode {
66+
namespace FilterMode
67+
{
6768
static inline constexpr auto lowpass = FilterModeType::declareValue<FilterModeFlags::lowpass>();
6869
static inline constexpr auto highpass = FilterModeType::declareValue<FilterModeFlags::highpass>();
6970
static inline constexpr auto bandpassCsg = FilterModeType::declareValue<FilterModeFlags::bandpassCsg>();
@@ -75,7 +76,39 @@ static inline constexpr auto highshelf = FilterModeType::declareValue<FilterMode
7576
static inline constexpr auto allpass = FilterModeType::declareValue<FilterModeFlags::allpass>();
7677

7778
/** Composite modes */
78-
static inline constexpr auto bandpass = bandpassCsg | bandpassCpg; /**< Any band-pass filter variant */
79+
static inline constexpr auto bandpass = bandpassCsg | bandpassCpg; /**< Any band-pass filter variant */
7980
} // namespace FilterMode
8081

82+
//==============================================================================
83+
/**
84+
Resolves a composite filter mode to the best supported variant for a specific filter.
85+
86+
@param requestedMode The mode requested (could be composite like 'bandpass')
87+
@param supportedModes The modes actually supported by the filter
88+
@returns The resolved specific mode, or empty FilterMode if none supported
89+
*/
90+
constexpr FilterModeType resolveFilterMode (FilterModeType requestedMode, FilterModeType supportedModes) noexcept
91+
{
92+
// If the exact mode is supported, use it
93+
if (supportedModes.test (requestedMode))
94+
return requestedMode;
95+
96+
// Handle composite mode resolution
97+
if (requestedMode.test (FilterMode::bandpass))
98+
{
99+
// Priority order: CSG first, then CPG
100+
if (supportedModes.test (FilterMode::bandpassCsg))
101+
return FilterMode::bandpassCsg;
102+
103+
else if (supportedModes.test (FilterMode::bandpassCpg))
104+
return FilterMode::bandpassCpg;
105+
}
106+
107+
// Could add more composite mode logic here in the future
108+
// e.g., if we had FilterMode::shelf = lowshelf | highshelf
109+
110+
// No supported variant found
111+
return FilterMode::lowpass; // Empty/null mode
112+
}
113+
81114
} // namespace yup

modules/yup_dsp/filters/yup_BiquadFilter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class BiquadFilter : public Biquad<SampleType, CoeffType>
6161
*/
6262
void setParameters (FilterModeType mode, CoeffType frequency, CoeffType q, CoeffType gainDb, double sampleRate) noexcept
6363
{
64+
mode = resolveFilterMode (mode, this->getSupportedModes());
65+
6466
if (filterMode != mode
6567
|| ! approximatelyEqual (centerFreq, frequency)
6668
|| ! approximatelyEqual (qFactor, q)
@@ -130,6 +132,8 @@ class BiquadFilter : public Biquad<SampleType, CoeffType>
130132
*/
131133
void setMode (FilterModeType mode) noexcept
132134
{
135+
mode = resolveFilterMode (mode, this->getSupportedModes());
136+
133137
if (filterMode != mode)
134138
{
135139
filterMode = mode;

0 commit comments

Comments
 (0)