FIR Filter Optimization

Designing and implementing the upcoming APU TrueGain plug-in has involved creating a large number of FIR filters with very tight frequency response requirements. For TrueGain in particular, there are thousands of individual filters which give the plug-in its unique capabilities. There are minimum and linear phase variations of many different sample rates and many different frequency-response contours.

The naive approach of packaging all of these filters would result in massive download and installation sizes. Because plug-ins are typically self-contained, this means all the filters need to be packaged for macOS, Windows, x86, x64, ARM, VST, AU, AAX, Standalone application. Even with some basic optimizations, this could easily result in GBs of downloads.

That wasn’t acceptable, so I used a variety of techniques to reduce the size of the filters while still maintaining the required strict frequency response. Let’s go through some of these techniques..

Raw Filters

All filter operations in APU plug-ins are performed using ‘double’ precision floating point operations. For an ISO 226 contour at 192 kHz, you can end up with a filter of 30,000 taps or more. Given the size of each tap is 8 bytes, this results in a filter size of 240 kB or more. Multiply that by the sheer number of filters and you can see how quickly the size can grow!

Float16

Just because the real-time filter processing is done in double precision doesn’t mean the filter coefficients need to be stored that way. Even while maintaining the same tight frequency response, the filter coefficients themselves can be reduced to 16-bit floating point values. This immediately cuts the size of each filter to a quarter of its original size.

The conversion to float16 is done prior to filter validation, so we can be confident that the desired precision is not lost.

Linear Phase symmetry

The ‘master’ filters for APU projects are designed to be linear phase. Since linear phase filters are symmetric by nature (impulse response is the same forwards and backwards), we can immediately save half the size of the filter coefficients by only storing half of the coefficients. The other half can be derived from the first half by simply reversing the order of the coefficients.

This also has the nice side effect of enforcing symmetry during the filter design process. So long as the tolerance enforcement is done after ensuring symmetry, we can have confidence that the filters retain their linear phase characteristics as well as their frequency response.

Minimum Phase

Supporting minimum phase filters is a requirement for APU projects, and the naive approach to filter distribution would immediately double the size of the filter coefficients. However, we can actually derive the minimum phase coefficients from the linear phase coefficients.

For APU projects, the minimum phase coefficients are derived in C++ while maintaining the frequency response using a classic DSP method based on homomorphic signal processing in the real cepstrum domain.

Minimum phase filters don’t have the symmetry trait of linear phase, so we save quite a lot of size by deriving them at runtime. As with all the other optimizations, we perform validation after this process to ensure all tolerances are still met after the C++ conversion process.

Resampling

In order to ensure precision across all sample rates, all the ‘standard’ common sample rates are explicitly designed for. This means different sample rates each have their own separate design process and their own resulting set of FIR taps. However, this leaves the question of ‘unusual’ sample rates.

It wouldn’t be practical to design a filter for every possible sample rate of course, so instead a ‘standard’ set of sample rates are explicitly supported and other sample rates are resampled from one of these master filters to ensure the best possible frequency response.

Fortunately, the excellent r8brain library provides a very high quality resampling algorithm which can be used to convert the master filter coefficients to any sample rate. This is done from C++ during the plug-in initialization process, so the resulting filter coefficients are ready to use immediately.

Once again, this resampling process occurs prior to validation, so we can be confident that the resulting filter meets the required tolerances, even for oddball sample rates.

Conclusion

Designing these plug-ins often takes quite a lot of time, as there are many details as described above which need to be carefully considered. But the end result is a set of filters which are very precise but also fit within reasonable size constraints. The techniques described above are just a few of the many optimizations which have been applied to the APU TrueGain project.