Skip to content

Commit 0a4b74a

Browse files
committed
More FIR specializations
1 parent cf8851f commit 0a4b74a

File tree

8 files changed

+402
-115
lines changed

8 files changed

+402
-115
lines changed

examples/graphics/source/examples/FilterDemo.h

Lines changed: 216 additions & 10 deletions
Large diffs are not rendered by default.

modules/yup_dsp/convolution/yup_PartitionedConvolver.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -563,16 +563,8 @@ class PartitionedConvolver::Impl
563563
trimmedLength = trimSilenceFromEnd (impulseResponse, length, *options.trimEndSilenceBelowDb);
564564

565565
// Update DirectFIR in-place
566-
std::vector<float> directCoefficients;
567-
568566
const auto directCoefficientsCount = std::min (directFIRCoefficientCount, trimmedLength);
569-
if (directCoefficientsCount > 0)
570-
{
571-
directCoefficients.reserve (directCoefficientsCount);
572-
directCoefficients.assign (impulseResponse, impulseResponse + directCoefficientsCount);
573-
}
574-
575-
newFIR.setCoefficients (std::move (directCoefficients), headroomScale);
567+
newFIR.setCoefficients (impulseResponse, directCoefficientsCount, headroomScale);
576568

577569
// Update FFT layers
578570
std::size_t consumed = directCoefficientsCount;

modules/yup_dsp/designers/yup_FilterDesigner.cpp

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -630,19 +630,21 @@ int FilterDesigner<CoeffType>::designLinkwitzRiley (
630630
//==============================================================================
631631

632632
template <typename CoeffType>
633-
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRLowpass (
633+
void FilterDesigner<CoeffType>::designFIRLowpass (
634+
std::vector<CoeffType>& coefficients,
634635
int numCoefficients,
635636
CoeffType cutoffFreq,
636637
double sampleRate,
637-
WindowType windowType) noexcept
638+
WindowType windowType,
639+
CoeffType windowParameter) noexcept
638640
{
639641
jassert (numCoefficients > 0);
640642
jassert (cutoffFreq > static_cast<CoeffType> (0.0));
641643
jassert (sampleRate > 0.0);
642644
jassert (cutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
643645

644646
numCoefficients = nextOdd (numCoefficients);
645-
std::vector<CoeffType> coefficients (numCoefficients);
647+
coefficients.resize (numCoefficients);
646648

647649
const auto normalizedCutoff = static_cast<CoeffType> (2.0) * cutoffFreq / static_cast<CoeffType> (sampleRate);
648650
const int center = (numCoefficients - 1) / 2;
@@ -664,25 +666,27 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRLowpass (
664666
// Apply window function
665667
for (int i = 0; i < numCoefficients; ++i)
666668
{
667-
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients);
669+
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients, windowParameter);
668670
coefficients[i] *= windowValue;
669671
}
670672

671673
// Normalization
672674
const auto sum = std::accumulate (coefficients.begin(), coefficients.end(), static_cast<CoeffType> (0.0));
673675
if (sum != static_cast<CoeffType> (0.0))
676+
{
674677
for (auto& c : coefficients)
675678
c /= sum;
676-
677-
return coefficients;
679+
}
678680
}
679681

680682
template <typename CoeffType>
681-
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRHighpass (
683+
void FilterDesigner<CoeffType>::designFIRHighpass (
684+
std::vector<CoeffType>& coefficients,
682685
int numCoefficients,
683686
CoeffType cutoffFreq,
684687
double sampleRate,
685-
WindowType windowType) noexcept
688+
WindowType windowType,
689+
CoeffType windowParameter) noexcept
686690
{
687691
jassert (numCoefficients > 0);
688692
jassert (cutoffFreq > static_cast<CoeffType> (0.0));
@@ -691,7 +695,7 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRHighpass (
691695

692696
// Generate lowpass first
693697
numCoefficients = nextOdd (numCoefficients);
694-
auto coefficients = designFIRLowpass (numCoefficients, cutoffFreq, sampleRate, windowType);
698+
designFIRLowpass (coefficients, numCoefficients, cutoffFreq, sampleRate, windowType);
695699

696700
// Convert to highpass using spectral inversion
697701
const int center = (numCoefficients - 1) / 2;
@@ -707,19 +711,21 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRHighpass (
707711
hpi += coefficients[n] * ((n & 1) ? static_cast<CoeffType> (-1.0) : static_cast<CoeffType> (1.0));
708712

709713
if (hpi != static_cast<CoeffType> (0.0))
714+
{
710715
for (auto& c : coefficients)
711716
c /= hpi;
712-
713-
return coefficients;
717+
}
714718
}
715719

716720
template <typename CoeffType>
717-
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandpass (
721+
void FilterDesigner<CoeffType>::designFIRBandpass (
722+
std::vector<CoeffType>& coefficients,
718723
int numCoefficients,
719724
CoeffType lowCutoffFreq,
720725
CoeffType highCutoffFreq,
721726
double sampleRate,
722-
WindowType windowType) noexcept
727+
WindowType windowType,
728+
CoeffType windowParameter) noexcept
723729
{
724730
jassert (numCoefficients > 0);
725731
jassert (lowCutoffFreq > static_cast<CoeffType> (0.0));
@@ -728,7 +734,7 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandpass (
728734
jassert (highCutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
729735

730736
numCoefficients = nextOdd (numCoefficients);
731-
std::vector<CoeffType> coefficients (numCoefficients);
737+
coefficients.resize (numCoefficients);
732738

733739
const auto normalizedLow = static_cast<CoeffType> (2.0) * lowCutoffFreq / static_cast<CoeffType> (sampleRate);
734740
const auto normalizedHigh = static_cast<CoeffType> (2.0) * highCutoffFreq / static_cast<CoeffType> (sampleRate);
@@ -754,20 +760,20 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandpass (
754760
// Apply window function
755761
for (int i = 0; i < numCoefficients; ++i)
756762
{
757-
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients);
763+
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients, windowParameter);
758764
coefficients[i] *= windowValue;
759765
}
760-
761-
return coefficients;
762766
}
763767

764768
template <typename CoeffType>
765-
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandstop (
769+
void FilterDesigner<CoeffType>::designFIRBandstop (
770+
std::vector<CoeffType>& coefficients,
766771
int numCoefficients,
767772
CoeffType lowCutoffFreq,
768773
CoeffType highCutoffFreq,
769774
double sampleRate,
770-
WindowType windowType) noexcept
775+
WindowType windowType,
776+
CoeffType windowParameter) noexcept
771777
{
772778
jassert (numCoefficients > 0);
773779
jassert (lowCutoffFreq > static_cast<CoeffType> (0.0));
@@ -777,7 +783,7 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandstop (
777783

778784
// Generate bandpass first
779785
numCoefficients = nextOdd (numCoefficients);
780-
auto coefficients = designFIRBandpass (numCoefficients, lowCutoffFreq, highCutoffFreq, sampleRate, windowType);
786+
designFIRBandpass (coefficients, numCoefficients, lowCutoffFreq, highCutoffFreq, sampleRate, windowType);
781787

782788
// Convert to bandstop using spectral inversion
783789
const int center = (numCoefficients - 1) / 2;
@@ -787,8 +793,6 @@ std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandstop (
787793

788794
// Add unit impulse at center
789795
coefficients[center] += static_cast<CoeffType> (1.0);
790-
791-
return coefficients;
792796
}
793797

794798
//==============================================================================

modules/yup_dsp/designers/yup_FilterDesigner.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -640,11 +640,13 @@ class FilterDesigner
640640
641641
@returns Vector of FIR coefficients suitable for DirectFIR
642642
*/
643-
static std::vector<CoeffType> designFIRLowpass (
643+
static void designFIRLowpass (
644+
std::vector<CoeffType>& coefficients,
644645
int numCoefficients,
645646
CoeffType cutoffFreq,
646647
double sampleRate,
647-
WindowType windowType = WindowType::hann) noexcept;
648+
WindowType windowType = WindowType::hann,
649+
CoeffType windowParameter = CoeffType (8)) noexcept;
648650

649651
/**
650652
Designs FIR highpass filter coefficients using windowed sinc method.
@@ -656,11 +658,13 @@ class FilterDesigner
656658
657659
@returns Vector of FIR coefficients suitable for DirectFIR
658660
*/
659-
static std::vector<CoeffType> designFIRHighpass (
661+
static void designFIRHighpass (
662+
std::vector<CoeffType>& coefficients,
660663
int numCoefficients,
661664
CoeffType cutoffFreq,
662665
double sampleRate,
663-
WindowType windowType = WindowType::hann) noexcept;
666+
WindowType windowType = WindowType::hann,
667+
CoeffType windowParameter = CoeffType (8)) noexcept;
664668

665669
/**
666670
Designs FIR bandpass filter coefficients using windowed sinc method.
@@ -673,12 +677,14 @@ class FilterDesigner
673677
674678
@returns Vector of FIR coefficients suitable for DirectFIR
675679
*/
676-
static std::vector<CoeffType> designFIRBandpass (
680+
static void designFIRBandpass (
681+
std::vector<CoeffType>& coefficients,
677682
int numCoefficients,
678683
CoeffType lowCutoffFreq,
679684
CoeffType highCutoffFreq,
680685
double sampleRate,
681-
WindowType windowType = WindowType::hann) noexcept;
686+
WindowType windowType = WindowType::hann,
687+
CoeffType windowParameter = CoeffType (8)) noexcept;
682688

683689
/**
684690
Designs FIR bandstop filter coefficients using windowed sinc method.
@@ -691,12 +697,14 @@ class FilterDesigner
691697
692698
@returns Vector of FIR coefficients suitable for DirectFIR
693699
*/
694-
static std::vector<CoeffType> designFIRBandstop (
700+
static void designFIRBandstop (
701+
std::vector<CoeffType>& coefficients,
695702
int numCoefficients,
696703
CoeffType lowCutoffFreq,
697704
CoeffType highCutoffFreq,
698705
double sampleRate,
699-
WindowType windowType = WindowType::hann) noexcept;
706+
WindowType windowType = WindowType::hann,
707+
CoeffType windowParameter = CoeffType (8)) noexcept;
700708
};
701709

702710
} // namespace yup

modules/yup_dsp/filters/yup_DirectFIR.h

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -100,29 +100,17 @@ class DirectFIR : public FilterBase<SampleType, CoeffType>
100100

101101
//==============================================================================
102102
/**
103-
Set the FIR filter coefficients.
103+
Set the FIR filter coefficients from a raw pointer.
104104
105-
@param coefficients Vector containing the FIR coefficients in time order
106-
@param scaling Scaling factor to apply to all coefficients
105+
@param coefficients Span of FIR coefficients array
106+
@param scaling Scaling factor to apply to all coefficients
107107
108108
@note This method is not real-time safe and should be called during initialization
109-
or when audio processing is paused.
109+
or when audio processing is paused, unless the coefficients vector has already been set with a greater or equal size.
110110
*/
111-
void setCoefficients (std::vector<CoeffType> coefficients, CoeffType scaling = CoeffType (1))
111+
void setCoefficients (yup::Span<const CoeffType> coefficients, CoeffType scaling = CoeffType (1))
112112
{
113-
currentScaling = scaling;
114-
if (! approximatelyEqual (currentScaling, CoeffType (1)))
115-
FloatVectorOperations::multiply (coefficients.data(), scaling, coefficients.size());
116-
117-
coefficientsReversed = std::move (coefficients);
118-
std::reverse (coefficientsReversed.begin(), coefficientsReversed.end());
119-
120-
numCoefficients = coefficientsReversed.size();
121-
paddedLen = (numCoefficients + 3u) & ~3u; // Round up to multiple of 4 for SIMD
122-
coefficientsReversed.resize (paddedLen, 0.0f);
123-
124-
history.assign (2 * numCoefficients, 0.0f);
125-
reset();
113+
setCoefficients (coefficients.data(), coefficients.size(), scaling);
126114
}
127115

128116
/**
@@ -133,7 +121,7 @@ class DirectFIR : public FilterBase<SampleType, CoeffType>
133121
@param scaling Scaling factor to apply to all coefficients
134122
135123
@note This method is not real-time safe and should be called during initialization
136-
or when audio processing is paused.
124+
or when audio processing is paused, unless the coefficients vector has already been set with a greater or equal size.
137125
*/
138126
void setCoefficients (const CoeffType* coefficients, std::size_t numCoefficientsIn, CoeffType scaling = CoeffType (1))
139127
{
@@ -144,8 +132,21 @@ class DirectFIR : public FilterBase<SampleType, CoeffType>
144132
return;
145133
}
146134

147-
std::vector<float> coefficientsVector (coefficients, coefficients + numCoefficientsIn);
148-
setCoefficients (std::move (coefficientsVector), scaling);
135+
numCoefficients = numCoefficientsIn;
136+
paddedLen = (numCoefficientsIn + 3u) & ~3u; // Round up to multiple of 4 for SIMD
137+
138+
coefficientsReversed.resize (paddedLen, 0.0f);
139+
140+
currentScaling = scaling;
141+
if (! approximatelyEqual (currentScaling, CoeffType (1)))
142+
FloatVectorOperations::copyWithMultiply (coefficientsReversed.data(), coefficients, scaling, static_cast<int> (numCoefficientsIn));
143+
else
144+
FloatVectorOperations::copy (coefficientsReversed.data(), coefficients, static_cast<int> (numCoefficientsIn));
145+
146+
std::reverse (coefficientsReversed.begin(), coefficientsReversed.begin() + numCoefficients);
147+
148+
history.resize (2 * numCoefficients, 0.0f);
149+
writeIndex = 0;
149150
}
150151

151152
/**
@@ -263,19 +264,29 @@ class DirectFIR : public FilterBase<SampleType, CoeffType>
263264
if (numCoefficients == 0)
264265
return Complex<CoeffType> (0, 0);
265266

267+
// ω = 2π f / Fs
266268
const CoeffType omega = MathConstants<CoeffType>::twoPi * frequency / static_cast<CoeffType> (this->sampleRate);
267269

268-
Complex<CoeffType> response (0, 0);
270+
// Standard FIR frequency response: H(e^{jω}) = Σ_{n=0}^{N-1} h[n] * e^{-jωn}
271+
// coefficientsReversed stores: [h[M-1], h[M-2], ..., h[1], h[0]]
272+
// So coefficientsReversed[k] = h[M-1-k], and we need: Σ h[n] * e^{-jωn}
273+
274+
// e^{-jω}
275+
const Complex<CoeffType> ez_neg { std::cos (omega), -std::sin (omega) };
276+
277+
// Accumulate: Σ_{n=0}^{N-1} h[n] * e^{-jωn}
278+
// Since coefficientsReversed[k] = h[M-1-k], we have h[n] = coefficientsReversed[M-1-n]
279+
Complex<CoeffType> sum { 0, 0 };
280+
Complex<CoeffType> ez_neg_n { 1, 0 }; // e^{-jω*0} = 1
269281

270-
// H(e^jω) = Σ h[n] * e^(-jωn) for n = 0 to N-1
271282
for (std::size_t n = 0; n < numCoefficients; ++n)
272283
{
273-
const CoeffType angle = -omega * static_cast<CoeffType> (n);
274-
Complex<CoeffType> exponential (std::cos (angle), std::sin (angle));
275-
response += static_cast<CoeffType> (coefficientsReversed[numCoefficients - 1 - n]) * exponential;
284+
const CoeffType h_n = coefficientsReversed[numCoefficients - 1 - n];
285+
sum += h_n * ez_neg_n;
286+
ez_neg_n *= ez_neg;
276287
}
277288

278-
return response;
289+
return sum;
279290
}
280291

281292
/**

0 commit comments

Comments
 (0)