mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #267 from TomHarte/AudioCleanup
Resolves dangling C-isms in my FIR filter, and introduces composition.
This commit is contained in:
commit
fd346bac3e
@ -334,9 +334,9 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
|
|||||||
unsigned int taps = 11;
|
unsigned int taps = 11;
|
||||||
// unsigned int taps = 21;
|
// unsigned int taps = 21;
|
||||||
// while(1) {
|
// while(1) {
|
||||||
float coefficients[21];
|
// float coefficients[21];
|
||||||
SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
|
SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
|
||||||
luminance_filter.get_coefficients(coefficients);
|
std::vector<float> coefficients = luminance_filter.get_coefficients();
|
||||||
|
|
||||||
// int sample = 0;
|
// int sample = 0;
|
||||||
// int c = 0;
|
// int c = 0;
|
||||||
|
@ -46,7 +46,7 @@ float FIRFilter::ino(float a) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FIRFilter::coefficients_for_idealised_filter_response(short *filterCoefficients, float *A, float attenuation, unsigned int numberOfTaps) {
|
void FIRFilter::coefficients_for_idealised_filter_response(short *filter_coefficients, float *A, float attenuation, size_t number_of_taps) {
|
||||||
/* calculate alpha, which is the Kaiser-Bessel window shape factor */
|
/* calculate alpha, which is the Kaiser-Bessel window shape factor */
|
||||||
float a; // to take the place of alpha in the normal derivation
|
float a; // to take the place of alpha in the normal derivation
|
||||||
|
|
||||||
@ -59,46 +59,46 @@ void FIRFilter::coefficients_for_idealised_filter_response(short *filterCoeffici
|
|||||||
a = 0.5842f * powf(attenuation - 21.0f, 0.4f) + 0.7886f * (attenuation - 21.0f);
|
a = 0.5842f * powf(attenuation - 21.0f, 0.4f) + 0.7886f * (attenuation - 21.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float *filterCoefficientsFloat = new float[numberOfTaps];
|
std::vector<float> filter_coefficients_float(number_of_taps);
|
||||||
|
|
||||||
/* work out the right hand side of the filter coefficients */
|
/* work out the right hand side of the filter coefficients */
|
||||||
unsigned int Np = (numberOfTaps - 1) / 2;
|
size_t Np = (number_of_taps - 1) / 2;
|
||||||
float I0 = ino(a);
|
float I0 = ino(a);
|
||||||
float NpSquared = static_cast<float>(Np * Np);
|
float Np_squared = static_cast<float>(Np * Np);
|
||||||
for(unsigned int i = 0; i <= Np; i++) {
|
for(unsigned int i = 0; i <= Np; ++i) {
|
||||||
filterCoefficientsFloat[Np + i] =
|
filter_coefficients_float[Np + i] =
|
||||||
A[i] *
|
A[i] *
|
||||||
ino(a * sqrtf(1.0f - (static_cast<float>(i * i) / NpSquared) )) /
|
ino(a * sqrtf(1.0f - (static_cast<float>(i * i) / Np_squared) )) /
|
||||||
I0;
|
I0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* coefficients are symmetrical, so copy from right hand side to left side */
|
/* coefficients are symmetrical, so copy from right hand side to left side */
|
||||||
for(unsigned int i = 0; i < Np; i++) {
|
for(size_t i = 0; i < Np; ++i) {
|
||||||
filterCoefficientsFloat[i] = filterCoefficientsFloat[numberOfTaps - 1 - i];
|
filter_coefficients_float[i] = filter_coefficients_float[number_of_taps - 1 - i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* scale back up so that we retain 100% of input volume */
|
/* scale back up so that we retain 100% of input volume */
|
||||||
float coefficientTotal = 0.0f;
|
float coefficientTotal = 0.0f;
|
||||||
for(unsigned int i = 0; i < numberOfTaps; i++) {
|
for(size_t i = 0; i < number_of_taps; ++i) {
|
||||||
coefficientTotal += filterCoefficientsFloat[i];
|
coefficientTotal += filter_coefficients_float[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we'll also need integer versions, potentially */
|
/* we'll also need integer versions, potentially */
|
||||||
float coefficientMultiplier = 1.0f / coefficientTotal;
|
float coefficientMultiplier = 1.0f / coefficientTotal;
|
||||||
for(unsigned int i = 0; i < numberOfTaps; i++) {
|
for(size_t i = 0; i < number_of_taps; ++i) {
|
||||||
filterCoefficients[i] = (short)(filterCoefficientsFloat[i] * kCSKaiserBesselFilterFixedMultiplier * coefficientMultiplier);
|
filter_coefficients[i] = (short)(filter_coefficients_float[i] * FixedMultiplier * coefficientMultiplier);
|
||||||
}
|
|
||||||
|
|
||||||
delete[] filterCoefficientsFloat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FIRFilter::get_coefficients(float *coefficients) {
|
|
||||||
for(unsigned int i = 0; i < number_of_taps_; i++) {
|
|
||||||
coefficients[i] = static_cast<float>(filter_coefficients_[i]) / kCSKaiserBesselFilterFixedMultiplier;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FIRFilter::FIRFilter(unsigned int number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation) {
|
std::vector<float> FIRFilter::get_coefficients() const {
|
||||||
|
std::vector<float> coefficients;
|
||||||
|
for(auto short_coefficient: filter_coefficients_) {
|
||||||
|
coefficients.push_back(static_cast<float>(short_coefficient) / FixedMultiplier);
|
||||||
|
}
|
||||||
|
return coefficients;
|
||||||
|
}
|
||||||
|
|
||||||
|
FIRFilter::FIRFilter(size_t number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation) {
|
||||||
// we must be asked to filter based on an odd number of
|
// we must be asked to filter based on an odd number of
|
||||||
// taps, and at least three
|
// taps, and at least three
|
||||||
if(number_of_taps < 3) number_of_taps = 3;
|
if(number_of_taps < 3) number_of_taps = 3;
|
||||||
@ -108,30 +108,52 @@ FIRFilter::FIRFilter(unsigned int number_of_taps, float input_sample_rate, float
|
|||||||
number_of_taps |= 1;
|
number_of_taps |= 1;
|
||||||
|
|
||||||
// store instance variables
|
// store instance variables
|
||||||
number_of_taps_ = number_of_taps;
|
filter_coefficients_.resize(number_of_taps);
|
||||||
filter_coefficients_ = new short[number_of_taps_];
|
|
||||||
|
|
||||||
/* calculate idealised filter response */
|
/* calculate idealised filter response */
|
||||||
unsigned int Np = (number_of_taps - 1) / 2;
|
size_t Np = (number_of_taps - 1) / 2;
|
||||||
float twoOverSampleRate = 2.0f / input_sample_rate;
|
float two_over_sample_rate = 2.0f / input_sample_rate;
|
||||||
|
|
||||||
float *A = new float[Np+1];
|
std::vector<float> A(Np+1);
|
||||||
A[0] = 2.0f * (high_frequency - low_frequency) / input_sample_rate;
|
A[0] = 2.0f * (high_frequency - low_frequency) / input_sample_rate;
|
||||||
for(unsigned int i = 1; i <= Np; i++) {
|
for(unsigned int i = 1; i <= Np; ++i) {
|
||||||
float iPi = static_cast<float>(i) * static_cast<float>(M_PI);
|
float i_pi = static_cast<float>(i) * static_cast<float>(M_PI);
|
||||||
A[i] =
|
A[i] =
|
||||||
(
|
(
|
||||||
sinf(twoOverSampleRate * iPi * high_frequency) -
|
sinf(two_over_sample_rate * i_pi * high_frequency) -
|
||||||
sinf(twoOverSampleRate * iPi * low_frequency)
|
sinf(two_over_sample_rate * i_pi * low_frequency)
|
||||||
) / iPi;
|
) / i_pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_, A, attenuation, number_of_taps_);
|
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_.data(), A.data(), attenuation, number_of_taps);
|
||||||
|
|
||||||
/* clean up */
|
|
||||||
delete[] A;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FIRFilter::~FIRFilter() {
|
FIRFilter::FIRFilter(const std::vector<float> &coefficients) {
|
||||||
delete[] filter_coefficients_;
|
for(auto coefficient: coefficients) {
|
||||||
|
filter_coefficients_.push_back(static_cast<short>(coefficient * FixedMultiplier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FIRFilter FIRFilter::operator+(const FIRFilter &rhs) const {
|
||||||
|
std::vector<float> coefficients = get_coefficients();
|
||||||
|
std::vector<float> rhs_coefficients = rhs.get_coefficients();
|
||||||
|
|
||||||
|
std::vector<float> sum;
|
||||||
|
for(size_t i = 0; i < coefficients.size(); ++i) {
|
||||||
|
sum.push_back((coefficients[i] + rhs_coefficients[i]) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FIRFilter(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
FIRFilter FIRFilter::operator*(const FIRFilter &rhs) const {
|
||||||
|
std::vector<float> coefficients = get_coefficients();
|
||||||
|
std::vector<float> rhs_coefficients = rhs.get_coefficients();
|
||||||
|
|
||||||
|
std::vector<float> sum;
|
||||||
|
for(size_t i = 0; i < coefficients.size(); ++i) {
|
||||||
|
sum.push_back(coefficients[i] * rhs_coefficients[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FIRFilter(sum);
|
||||||
}
|
}
|
||||||
|
@ -9,34 +9,24 @@
|
|||||||
#ifndef FIRFilter_hpp
|
#ifndef FIRFilter_hpp
|
||||||
#define FIRFilter_hpp
|
#define FIRFilter_hpp
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
The FIR filter takes a 1d PCM signal with
|
|
||||||
a given sample rate and filters it according
|
|
||||||
to a specified filter (band pass only at
|
|
||||||
present, more to come if required). The number
|
|
||||||
of taps (ie, samples considered simultaneously
|
|
||||||
to make an output sample) is configurable;
|
|
||||||
smaller numbers permit a filter that operates
|
|
||||||
more quickly and with less lag but less
|
|
||||||
effectively.
|
|
||||||
|
|
||||||
FIR filters are window functions; expected use is
|
|
||||||
to point sample an input that has been subject to
|
|
||||||
a filter.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <Accelerate/Accelerate.h>
|
#include <Accelerate/Accelerate.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace SignalProcessing {
|
namespace SignalProcessing {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The FIR filter takes a 1d PCM signal with a given sample rate and applies a band-pass filter to it.
|
||||||
|
|
||||||
|
The number of taps (ie, samples considered simultaneously to make an output sample) is configurable;
|
||||||
|
smaller numbers permit a filter that operates more quickly and with less lag but less effectively.
|
||||||
|
*/
|
||||||
class FIRFilter {
|
class FIRFilter {
|
||||||
private:
|
private:
|
||||||
static constexpr float kCSKaiserBesselFilterFixedMultiplier = 32767.0f;
|
static constexpr float FixedMultiplier = 32767.0f;
|
||||||
static constexpr int kCSKaiserBesselFilterFixedShift = 15;
|
static constexpr int FixedShift = 15;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
@ -48,9 +38,8 @@ class FIRFilter {
|
|||||||
@param high_frequency The highest frequency of signal to retain in the output.
|
@param high_frequency The highest frequency of signal to retain in the output.
|
||||||
@param attenuation The attenuation of the discarded frequencies.
|
@param attenuation The attenuation of the discarded frequencies.
|
||||||
*/
|
*/
|
||||||
FIRFilter(unsigned int number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation);
|
FIRFilter(size_t number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation);
|
||||||
|
FIRFilter(const std::vector<float> &coefficients);
|
||||||
~FIRFilter();
|
|
||||||
|
|
||||||
/*! A suggested default attenuation value. */
|
/*! A suggested default attenuation value. */
|
||||||
constexpr static float DefaultAttenuation = 60.0f;
|
constexpr static float DefaultAttenuation = 60.0f;
|
||||||
@ -61,31 +50,44 @@ class FIRFilter {
|
|||||||
@param src The source buffer to apply the filter to.
|
@param src The source buffer to apply the filter to.
|
||||||
@returns The result of applying the filter.
|
@returns The result of applying the filter.
|
||||||
*/
|
*/
|
||||||
inline short apply(const short *src) {
|
inline short apply(const short *src) const {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
short result;
|
short result;
|
||||||
vDSP_dotpr_s1_15(filter_coefficients_, 1, src, 1, &result, number_of_taps_);
|
vDSP_dotpr_s1_15(filter_coefficients_.data(), 1, src, 1, &result, filter_coefficients_.size());
|
||||||
return result;
|
return result;
|
||||||
#else
|
#else
|
||||||
int outputValue = 0;
|
int outputValue = 0;
|
||||||
for(unsigned int c = 0; c < number_of_taps_; c++) {
|
for(size_t c = 0; c < filter_coefficients_.size(); ++c) {
|
||||||
outputValue += filter_coefficients_[c] * src[c];
|
outputValue += filter_coefficients_[c] * src[c];
|
||||||
}
|
}
|
||||||
return (short)(outputValue >> kCSKaiserBesselFilterFixedShift);
|
return (short)(outputValue >> FixedShift);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned int get_number_of_taps() {
|
/*! @returns The number of taps used by this filter. */
|
||||||
return number_of_taps_;
|
inline size_t get_number_of_taps() const {
|
||||||
|
return filter_coefficients_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_coefficients(float *coefficients);
|
/*! @returns The weighted coefficients that describe this filter. */
|
||||||
|
std::vector<float> get_coefficients() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns A filter that would have the effect of adding (and scaling) the outputs of the two filters.
|
||||||
|
Defined only if both have the same number of taps.
|
||||||
|
*/
|
||||||
|
FIRFilter operator+(const FIRFilter &) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns A filter that would have the effect of applying the two filters in succession.
|
||||||
|
Defined only if both have the same number of taps.
|
||||||
|
*/
|
||||||
|
FIRFilter operator*(const FIRFilter &) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
short *filter_coefficients_;
|
std::vector<short> filter_coefficients_;
|
||||||
unsigned int number_of_taps_;
|
|
||||||
|
|
||||||
static void coefficients_for_idealised_filter_response(short *filterCoefficients, float *A, float attenuation, unsigned int numberOfTaps);
|
static void coefficients_for_idealised_filter_response(short *filterCoefficients, float *A, float attenuation, size_t numberOfTaps);
|
||||||
static float ino(float a);
|
static float ino(float a);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user