1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-01-23 01:16:10 +00:00
Files
CLK/SignalProcessing/FIRFilter.hpp
Thomas Harte 26064cb9a4 Tweak my way to a great-looking NTSC Master System.
Other things look bad though.
2026-01-16 22:38:37 -05:00

234 lines
5.4 KiB
C++

//
// LinearFilter.h
// Clock Signal
//
// Created by Thomas Harte on 01/10/2011.
// Copyright 2011 Thomas Harte. All rights reserved.
//
#pragma once
// Use the Accelerate framework to vectorise, unless this is a Qt build.
// Primarily that avoids gymnastics in the QMake file; it also eliminates
// a difference in the Qt build across platforms.
#if defined(__APPLE__) && !defined(TARGET_QT)
#include <Accelerate/Accelerate.h>
#define USE_ACCELERATE
#endif
#include <algorithm>
#include <cassert>
#include <cmath>
#include <functional>
#include <iterator>
#include <numeric>
#include <vector>
namespace SignalProcessing {
constexpr float FixedMultiplier = 32767.0f;
constexpr int FixedShift = 15;
enum class ScalarType {
Int16,
Float,
};
template <ScalarType type>
class FIRFilter {
public:
using CoefficientType = std::conditional_t<
type == ScalarType::Int16,
int16_t,
float
>;
FIRFilter() = default;
template <typename IteratorT>
FIRFilter(const IteratorT begin, const IteratorT end) {
// Copy into place, possibly converting to fixed point.
if constexpr (type == ScalarType::Float) {
std::copy(begin, end, std::back_inserter(coefficients_));
} else {
IteratorT it = begin;
while(it != end) {
coefficients_.push_back(int16_t(*it * FixedMultiplier));
++it;
}
}
// Trim.
static constexpr CoefficientType threshold = type == ScalarType::Int16 ? 2 : 0.0001f;
while(!coefficients_.empty()) {
if(
std::abs(coefficients_.front()) > threshold ||
std::abs(coefficients_.back()) > threshold
) break;
coefficients_.erase(coefficients_.begin());
if(!coefficients_.empty()) coefficients_.erase(coefficients_.end() - 1);
}
}
/*!
Applies the filter to one batch of input samples, returning the net result.
@param src The source buffer to apply the filter to.
@returns The result of applying the filter.
*/
CoefficientType apply(const CoefficientType *const src, const size_t stride = 1) const {
#ifdef USE_ACCELERATE
if constexpr (type == ScalarType::Int16) {
int16_t result;
vDSP_dotpr_s1_15(
coefficients_.data(),
1,
src,
vDSP_Stride(stride),
&result,
coefficients_.size()
);
return result;
} else {
float result;
vDSP_dotpr(
coefficients_.data(),
1,
src,
vDSP_Stride(stride),
&result,
coefficients_.size()
);
return result;
}
#endif
using AccumulatorType = std::conditional_t<
type == ScalarType::Int16,
int,
float
>;
AccumulatorType result = 0;
for(size_t c = 0; c < coefficients_.size(); ++c) {
result += coefficients_[c] * src[c * stride];
}
if constexpr (type == ScalarType::Int16) {
return CoefficientType(result >> FixedShift);
} else {
return result;
}
}
CoefficientType operator[](const size_t index) const {
return coefficients_[index];
}
CoefficientType &operator[](const size_t index) {
return coefficients_[index];
}
size_t size() const {
return coefficients_.size();
}
void resize(const size_t size) {
coefficients_.resize(size);
}
using iterator = std::vector<CoefficientType>::iterator;
iterator begin() {
return coefficients_.begin();
}
iterator end() {
return coefficients_.end();
}
template <typename IteratorT>
void copy_to(
IteratorT begin,
IteratorT end,
const std::function<void(IteratorT, CoefficientType)> &applier
) const {
auto dest = begin;
auto src = coefficients_.begin();
const auto destination_size = size_t(std::distance(begin, end));
if(destination_size <= coefficients_.size()) {
std::advance(src, (coefficients_.size() - destination_size) / 2);
} else {
std::advance(dest, (destination_size - coefficients_.size()) / 2);
}
auto steps = std::min(destination_size, coefficients_.size());
while(steps--) {
applier(dest, *src);
++dest;
++src;
}
}
template <typename IteratorT>
void copy_to(
const IteratorT begin,
const IteratorT end
) const {
copy_to(begin, end, [](const IteratorT target, const CoefficientType coefficient) {
*target = coefficient;
});
}
FIRFilter operator *(const CoefficientType rhs) {
auto copy = *this;
for(auto &coefficient: copy.coefficients_) {
if constexpr (type == ScalarType::Float) {
coefficient *= rhs;
} else {
coefficient = (coefficient * rhs) >> FixedShift;
}
}
return copy;
}
private:
std::vector<CoefficientType> coefficients_;
};
/*!
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.
*/
namespace KaiserBessel {
static constexpr float DefaultAttenuation = 60.0f;
/*!
@param number_of_taps The size of window for input data.
@param input_sample_rate The sampling rate of the input signal.
@param low_frequency The lowest 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.
*/
template <ScalarType type>
FIRFilter<type> filter(
size_t number_of_taps,
float input_sample_rate,
float low_frequency,
float high_frequency,
float attenuation = DefaultAttenuation
);
}
namespace Box {
template <ScalarType type>
FIRFilter<type> filter(
float units_per_sample,
float total_range
);
}
}