FIR filter and object-oriented Swift
The Swift1 language is a successor to Apple’s Objective-C2. LadyBird3 is an open-source browser written in C++. Recently, I learned that its developers is looking for successor to C++. They compared Rust and Swift languages and chose the latter. In this interview4, Kling, the original author of LadyBird, says Swift is better at object-oriented programming (OOP) than Rust.
I have written an implementation of FIR filter in object-oriented C++5. So I thought I would rewrite it in object-oriented Swift for the development experience.
Here is the base class. It’s the direct form FIR6.
protocol FilterProtocol {
func filter(input : Double) -> Double
}
class FIR : FilterProtocol {
var coeffs : [Double]
var delay_line : [Double]
init(coeffs : [Double]) {
self.coeffs = coeffs
self.delay_line = [Double](repeating: 0.0, count: coeffs.count - 1)
}
func filter(input : Double) -> Double {
var sum : Double = input * self.coeffs[0]
for i in 0...self.delay_line.count-1 {
sum += self.coeffs[i+1] * self.delay_line[i]
}
// Update delay line
for i in stride(from: self.delay_line.count-1, through: 1, by: -1) {
self.delay_line[i] = self.delay_line[i-1]
}
self.delay_line[0] = input
return sum
}
}
Here is the transposed form FIR6.
I override the filter()
function of the base class.
class FIRTransposed : FIR {
override func filter(input : Double) -> Double {
var product = [Double](repeating:0.0, count: coeffs.count)
for i in 0...self.coeffs.count-1 {
product[i] = input * self.coeffs[i]
}
let sum = product[0] + self.delay_line[self.delay_line.count-1]
// Update delay line
for i in 1...product.count-1 {
let reverse = delay_line.count - i
if reverse == 0 {
self.delay_line[reverse] = product[i]
} else {
self.delay_line[reverse] = product[i] + self.delay_line[reverse-1]
}
}
return sum
}
}
Here is the test bench:
func sendImpulse(filter: FIR) {
print("coefficients:", filter.coeffs)
print("delay line:", filter.delay_line)
var impulse = [Double](repeating: 0.0, count: coeffs.count)
impulse[0] = 1.0
var output = [Double](repeating: 0.0, count: coeffs.count)
for i in 0...impulse.count-1 {
output[i] = filter.filter(input: impulse[i])
}
print("impulse:", impulse)
print("output:", output)
print("delay line:", filter.delay_line)
}
var coeffs = [3.0, -2.0, 1.0]
var fir = FIR(coeffs: coeffs)
sendImpulse(filter: fir)
print("Transposed")
var firtranspose = FIRTransposed(coeffs: coeffs)
sendImpulse(filter: firtranspose)
Here is the output
coefficients: [3.0, -2.0, 1.0]
delay line: [0.0, 0.0]
impulse: [1.0, 0.0, 0.0]
output: [3.0, -2.0, 1.0]
delay line: [0.0, 0.0]
Transposed
coefficients: [3.0, -2.0, 1.0]
delay line: [0.0, 0.0]
impulse: [1.0, 0.0, 0.0]
output: [3.0, -2.0, 1.0]
delay line: [0.0, 0.0]
Program ended with exit code: 0
I wrote this program in Xcode on a Macbook. Writing in Swift feels more like writing in Python than in C++. There is a lot less boilerplate code. Unlike C++, Swift has native print support for arrays. Overall, it has the efficiency of writing in a scripting language and the speed of compiled executable.