Skip to content

Commit 9ffd8d2

Browse files
committed
FIR Implementation
1 parent 35c4e5c commit 9ffd8d2

File tree

10 files changed

+875
-280
lines changed

10 files changed

+875
-280
lines changed

modules/yup_dsp/convolution/yup_PartitionedConvolver.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ class PartitionedConvolver::Impl
537537

538538
void setImpulseResponse (const float* impulseResponse, std::size_t length, const PartitionedConvolver::IRLoadOptions& options)
539539
{
540-
DirectFIR newFIR;
540+
DirectFIRFloat newFIR;
541541
std::vector<FFTLayer> newLayers (layers.size());
542542

543543
std::size_t trimmedLength = length;
@@ -765,7 +765,7 @@ class PartitionedConvolver::Impl
765765
std::size_t finalImpulseLength = 0;
766766
bool isPrepared = false;
767767

768-
DirectFIR directFIR;
768+
DirectFIRFloat directFIR;
769769
std::vector<FFTLayer> layers;
770770

771771
// Working buffers

modules/yup_dsp/designers/yup_FilterDesigner.cpp

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,172 @@ int FilterDesigner<CoeffType>::designLinkwitzRiley (
625625
return static_cast<int> (lowCoeffs.size());
626626
}
627627

628+
//==============================================================================
629+
// FIR Filter Design Implementations
630+
//==============================================================================
631+
632+
template <typename CoeffType>
633+
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRLowpass (
634+
int numCoefficients,
635+
CoeffType cutoffFreq,
636+
double sampleRate,
637+
WindowType windowType) noexcept
638+
{
639+
jassert (numCoefficients > 0);
640+
jassert (cutoffFreq > static_cast<CoeffType> (0.0));
641+
jassert (sampleRate > 0.0);
642+
jassert (cutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
643+
644+
numCoefficients = nextOdd (numCoefficients);
645+
std::vector<CoeffType> coefficients (numCoefficients);
646+
647+
const auto normalizedCutoff = static_cast<CoeffType> (2.0) * cutoffFreq / static_cast<CoeffType> (sampleRate);
648+
const int center = (numCoefficients - 1) / 2;
649+
650+
// Generate ideal lowpass sinc function
651+
for (int i = 0; i < numCoefficients; ++i)
652+
{
653+
if (i == center)
654+
{
655+
coefficients[i] = normalizedCutoff;
656+
}
657+
else
658+
{
659+
const auto x = MathConstants<CoeffType>::pi * normalizedCutoff * static_cast<CoeffType> (i - center);
660+
coefficients[i] = std::sin (x) / (MathConstants<CoeffType>::pi * static_cast<CoeffType> (i - center));
661+
}
662+
}
663+
664+
// Apply window function
665+
for (int i = 0; i < numCoefficients; ++i)
666+
{
667+
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients);
668+
coefficients[i] *= windowValue;
669+
}
670+
671+
// Normalization
672+
const auto sum = std::accumulate (coefficients.begin(), coefficients.end(), static_cast<CoeffType> (0.0));
673+
if (sum != static_cast<CoeffType> (0.0))
674+
for (auto& c : coefficients)
675+
c /= sum;
676+
677+
return coefficients;
678+
}
679+
680+
template <typename CoeffType>
681+
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRHighpass (
682+
int numCoefficients,
683+
CoeffType cutoffFreq,
684+
double sampleRate,
685+
WindowType windowType) noexcept
686+
{
687+
jassert (numCoefficients > 0);
688+
jassert (cutoffFreq > static_cast<CoeffType> (0.0));
689+
jassert (sampleRate > 0.0);
690+
jassert (cutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
691+
692+
// Generate lowpass first
693+
numCoefficients = nextOdd (numCoefficients);
694+
auto coefficients = designFIRLowpass (numCoefficients, cutoffFreq, sampleRate, windowType);
695+
696+
// Convert to highpass using spectral inversion
697+
const int center = (numCoefficients - 1) / 2;
698+
for (int i = 0; i < numCoefficients; ++i)
699+
coefficients[i] = -coefficients[i];
700+
701+
// Add unit impulse at center
702+
coefficients[center] += static_cast<CoeffType> (1.0);
703+
704+
// Normalization
705+
CoeffType hpi (0.0);
706+
for (int n = 0; n < numCoefficients; ++n)
707+
hpi += coefficients[n] * ((n & 1) ? static_cast<CoeffType> (-1.0) : static_cast<CoeffType> (1.0));
708+
709+
if (hpi != static_cast<CoeffType> (0.0))
710+
for (auto& c : coefficients)
711+
c /= hpi;
712+
713+
return coefficients;
714+
}
715+
716+
template <typename CoeffType>
717+
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandpass (
718+
int numCoefficients,
719+
CoeffType lowCutoffFreq,
720+
CoeffType highCutoffFreq,
721+
double sampleRate,
722+
WindowType windowType) noexcept
723+
{
724+
jassert (numCoefficients > 0);
725+
jassert (lowCutoffFreq > static_cast<CoeffType> (0.0));
726+
jassert (highCutoffFreq > lowCutoffFreq);
727+
jassert (sampleRate > 0.0);
728+
jassert (highCutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
729+
730+
numCoefficients = nextOdd (numCoefficients);
731+
std::vector<CoeffType> coefficients (numCoefficients);
732+
733+
const auto normalizedLow = static_cast<CoeffType> (2.0) * lowCutoffFreq / static_cast<CoeffType> (sampleRate);
734+
const auto normalizedHigh = static_cast<CoeffType> (2.0) * highCutoffFreq / static_cast<CoeffType> (sampleRate);
735+
const int center = (numCoefficients - 1) / 2;
736+
737+
// Generate ideal bandpass as difference of two sinc functions
738+
for (int i = 0; i < numCoefficients; ++i)
739+
{
740+
if (i == center)
741+
{
742+
coefficients[i] = normalizedHigh - normalizedLow;
743+
}
744+
else
745+
{
746+
const auto n = static_cast<CoeffType> (i - center);
747+
const auto xHigh = MathConstants<CoeffType>::pi * normalizedHigh * n;
748+
const auto xLow = MathConstants<CoeffType>::pi * normalizedLow * n;
749+
750+
coefficients[i] = (std::sin (xHigh) - std::sin (xLow)) / (MathConstants<CoeffType>::pi * n);
751+
}
752+
}
753+
754+
// Apply window function
755+
for (int i = 0; i < numCoefficients; ++i)
756+
{
757+
const auto windowValue = WindowFunctions<CoeffType>::getValue (windowType, i, numCoefficients);
758+
coefficients[i] *= windowValue;
759+
}
760+
761+
return coefficients;
762+
}
763+
764+
template <typename CoeffType>
765+
std::vector<CoeffType> FilterDesigner<CoeffType>::designFIRBandstop (
766+
int numCoefficients,
767+
CoeffType lowCutoffFreq,
768+
CoeffType highCutoffFreq,
769+
double sampleRate,
770+
WindowType windowType) noexcept
771+
{
772+
jassert (numCoefficients > 0);
773+
jassert (lowCutoffFreq > static_cast<CoeffType> (0.0));
774+
jassert (highCutoffFreq > lowCutoffFreq);
775+
jassert (sampleRate > 0.0);
776+
jassert (highCutoffFreq < static_cast<CoeffType> (sampleRate / 2.0));
777+
778+
// Generate bandpass first
779+
numCoefficients = nextOdd (numCoefficients);
780+
auto coefficients = designFIRBandpass (numCoefficients, lowCutoffFreq, highCutoffFreq, sampleRate, windowType);
781+
782+
// Convert to bandstop using spectral inversion
783+
const int center = (numCoefficients - 1) / 2;
784+
785+
for (int i = 0; i < numCoefficients; ++i)
786+
coefficients[i] = -coefficients[i];
787+
788+
// Add unit impulse at center
789+
coefficients[center] += static_cast<CoeffType> (1.0);
790+
791+
return coefficients;
792+
}
793+
628794
//==============================================================================
629795

630796
template class FilterDesigner<float>;

modules/yup_dsp/designers/yup_FilterDesigner.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,78 @@ class FilterDesigner
625625
{
626626
return designLinkwitzRiley (8, crossoverFreq, sampleRate, lowCoeffs, highCoeffs);
627627
}
628+
629+
//==============================================================================
630+
// FIR Filter Design
631+
//==============================================================================
632+
633+
/**
634+
Designs FIR lowpass filter coefficients using windowed sinc method.
635+
636+
@param numCoefficients The number of filter coefficients (filter order + 1)
637+
@param cutoffFreq The cutoff frequency in Hz
638+
@param sampleRate The sample rate in Hz
639+
@param windowType The window function to apply (default: Hanning)
640+
641+
@returns Vector of FIR coefficients suitable for DirectFIR
642+
*/
643+
static std::vector<CoeffType> designFIRLowpass (
644+
int numCoefficients,
645+
CoeffType cutoffFreq,
646+
double sampleRate,
647+
WindowType windowType = WindowType::hann) noexcept;
648+
649+
/**
650+
Designs FIR highpass filter coefficients using windowed sinc method.
651+
652+
@param numCoefficients The number of filter coefficients (filter order + 1)
653+
@param cutoffFreq The cutoff frequency in Hz
654+
@param sampleRate The sample rate in Hz
655+
@param windowType The window function to apply (default: Hanning)
656+
657+
@returns Vector of FIR coefficients suitable for DirectFIR
658+
*/
659+
static std::vector<CoeffType> designFIRHighpass (
660+
int numCoefficients,
661+
CoeffType cutoffFreq,
662+
double sampleRate,
663+
WindowType windowType = WindowType::hann) noexcept;
664+
665+
/**
666+
Designs FIR bandpass filter coefficients using windowed sinc method.
667+
668+
@param numCoefficients The number of filter coefficients (filter order + 1)
669+
@param lowCutoffFreq The lower cutoff frequency in Hz
670+
@param highCutoffFreq The upper cutoff frequency in Hz
671+
@param sampleRate The sample rate in Hz
672+
@param windowType The window function to apply (default: Hanning)
673+
674+
@returns Vector of FIR coefficients suitable for DirectFIR
675+
*/
676+
static std::vector<CoeffType> designFIRBandpass (
677+
int numCoefficients,
678+
CoeffType lowCutoffFreq,
679+
CoeffType highCutoffFreq,
680+
double sampleRate,
681+
WindowType windowType = WindowType::hann) noexcept;
682+
683+
/**
684+
Designs FIR bandstop filter coefficients using windowed sinc method.
685+
686+
@param numCoefficients The number of filter coefficients (filter order + 1)
687+
@param lowCutoffFreq The lower cutoff frequency in Hz
688+
@param highCutoffFreq The upper cutoff frequency in Hz
689+
@param sampleRate The sample rate in Hz
690+
@param windowType The window function to apply (default: Hanning)
691+
692+
@returns Vector of FIR coefficients suitable for DirectFIR
693+
*/
694+
static std::vector<CoeffType> designFIRBandstop (
695+
int numCoefficients,
696+
CoeffType lowCutoffFreq,
697+
CoeffType highCutoffFreq,
698+
double sampleRate,
699+
WindowType windowType = WindowType::hann) noexcept;
628700
};
629701

630702
} // namespace yup

0 commit comments

Comments
 (0)