1
0
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:
Thomas Harte 2017-11-11 12:38:45 -05:00 committed by GitHub
commit fd346bac3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 73 deletions

View File

@ -334,9 +334,9 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
unsigned int taps = 11;
// unsigned int taps = 21;
// while(1) {
float coefficients[21];
// float coefficients[21];
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 c = 0;

View File

@ -46,7 +46,7 @@ float FIRFilter::ino(float a) {
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 */
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);
}
float *filterCoefficientsFloat = new float[numberOfTaps];
std::vector<float> filter_coefficients_float(number_of_taps);
/* 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 NpSquared = static_cast<float>(Np * Np);
for(unsigned int i = 0; i <= Np; i++) {
filterCoefficientsFloat[Np + i] =
float Np_squared = static_cast<float>(Np * Np);
for(unsigned int i = 0; i <= Np; ++i) {
filter_coefficients_float[Np + 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;
}
/* coefficients are symmetrical, so copy from right hand side to left side */
for(unsigned int i = 0; i < Np; i++) {
filterCoefficientsFloat[i] = filterCoefficientsFloat[numberOfTaps - 1 - i];
for(size_t i = 0; i < Np; ++i) {
filter_coefficients_float[i] = filter_coefficients_float[number_of_taps - 1 - i];
}
/* scale back up so that we retain 100% of input volume */
float coefficientTotal = 0.0f;
for(unsigned int i = 0; i < numberOfTaps; i++) {
coefficientTotal += filterCoefficientsFloat[i];
for(size_t i = 0; i < number_of_taps; ++i) {
coefficientTotal += filter_coefficients_float[i];
}
/* we'll also need integer versions, potentially */
float coefficientMultiplier = 1.0f / coefficientTotal;
for(unsigned int i = 0; i < numberOfTaps; i++) {
filterCoefficients[i] = (short)(filterCoefficientsFloat[i] * kCSKaiserBesselFilterFixedMultiplier * 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;
for(size_t i = 0; i < number_of_taps; ++i) {
filter_coefficients[i] = (short)(filter_coefficients_float[i] * FixedMultiplier * coefficientMultiplier);
}
}
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
// taps, and at least three
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;
// store instance variables
number_of_taps_ = number_of_taps;
filter_coefficients_ = new short[number_of_taps_];
filter_coefficients_.resize(number_of_taps);
/* calculate idealised filter response */
unsigned int Np = (number_of_taps - 1) / 2;
float twoOverSampleRate = 2.0f / input_sample_rate;
size_t Np = (number_of_taps - 1) / 2;
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;
for(unsigned int i = 1; i <= Np; i++) {
float iPi = static_cast<float>(i) * static_cast<float>(M_PI);
for(unsigned int i = 1; i <= Np; ++i) {
float i_pi = static_cast<float>(i) * static_cast<float>(M_PI);
A[i] =
(
sinf(twoOverSampleRate * iPi * high_frequency) -
sinf(twoOverSampleRate * iPi * low_frequency)
) / iPi;
sinf(two_over_sample_rate * i_pi * high_frequency) -
sinf(two_over_sample_rate * i_pi * low_frequency)
) / i_pi;
}
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_, A, attenuation, number_of_taps_);
/* clean up */
delete[] A;
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_.data(), A.data(), attenuation, number_of_taps);
}
FIRFilter::~FIRFilter() {
delete[] filter_coefficients_;
FIRFilter::FIRFilter(const std::vector<float> &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);
}

View File

@ -9,34 +9,24 @@
#ifndef 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__
#include <Accelerate/Accelerate.h>
#endif
#include <vector>
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 {
private:
static constexpr float kCSKaiserBesselFilterFixedMultiplier = 32767.0f;
static constexpr int kCSKaiserBesselFilterFixedShift = 15;
static constexpr float FixedMultiplier = 32767.0f;
static constexpr int FixedShift = 15;
public:
/*!
@ -48,9 +38,8 @@ class FIRFilter {
@param high_frequency The highest frequency of signal to retain in the output.
@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();
FIRFilter(size_t number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation);
FIRFilter(const std::vector<float> &coefficients);
/*! A suggested default attenuation value. */
constexpr static float DefaultAttenuation = 60.0f;
@ -61,31 +50,44 @@ class FIRFilter {
@param src The source buffer to apply the filter to.
@returns The result of applying the filter.
*/
inline short apply(const short *src) {
inline short apply(const short *src) const {
#ifdef __APPLE__
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;
#else
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];
}
return (short)(outputValue >> kCSKaiserBesselFilterFixedShift);
return (short)(outputValue >> FixedShift);
#endif
}
inline unsigned int get_number_of_taps() {
return number_of_taps_;
/*! @returns The number of taps used by this filter. */
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:
short *filter_coefficients_;
unsigned int number_of_taps_;
std::vector<short> filter_coefficients_;
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);
};