Stories from Victor Azizi
Build your own cheap (digital) equalizer with free lip-sync capabilities
This project started because I have some speakers that are not well-equalized. After searching the internet for solutions, I came to the conclusion that most digital equalizers supporting toslink optical audio are _wildly_ overpriced, and since the audio signal I want to use was optical I decided to create my own.
I will make another blog-post about using a measurement microphone to create the correct filters to apply to the audio signal. In this blogpost I assume you know which "digital knobs" you want to tune.
Buying the required hardware
To make your own equalizer you need a few things:
A cheap Single Board Computer
(Optionally, if your SBC does not have the required ports): an USB sound card
Some cables to tie it all together

I chose to use:
a Raspberry Pi Zero 2 W with pre-installed rpiOS card.
A Sound BlasterX G6 card (cheaper options are available online)
A bunch of cables
Getting access to the RPI
After trying a bunch of tutorials online, I found out that they are all deprecated. Currently the only correct way to hook up the RPI to Wifi is to re-image the SD card with the rpi-imager . All previous methods are deprecated.
Installing the ALSA filter
One thing that never changed (and hopefully will never change!) is the linux ALSA subsystem. We will use the ALSA subsystem to create our equalizer and link it in our audio stream.
First the LADSPA headers have to be installed on the system. This is because the ACDf plugin uses these headers. For debian based systems this should be something like:
apt-get update apt-get install ladspa-sdk
Download and install the ACDf LADSPA filter from audio.claub.net/LADSPA-plugins.html
wget http://audio.claub.net/software/LADSPA/ACDf_v4.1.tar tar -xf ACDf_v4.1.tar cd ACDf_v4.1 make install
Letting ALSA know about the equalizer, and its settings
We have to create a file for ALSA so that it can apply our equalizer file with the ACDf filter. ALSA usually expects these in /etc/alsa/conf.d. Lets create the file 99-audio-filter.conf
Setting it all into motion with alsaloop
The idea is to let alsaloop run forever to pass the input audio through the filter to the output audio. By setting the loop delay you get lip-syncing for free!
hwdev=$(arecord -l | grep BlasterX | cut -d" " -f2 | cut -d":" -f1) # get your device number, probably adjust this on your own device
alsaloop -b -S 1 -t 13000 -f s32_LE -C "plughw:${hwdev},0" -P my_speaker --daemonize
This probably requires some explanation, all options of "alsaloop" can be found by executing alsaloop --help. In this loop non-blocking mode is enabled. The sync mode is set to simple, my experience learns that you get choppy sound otherwise. The delay is set to 13000 usec, you can adjust this value to lip-sync your audio. The sample format is set to the highest supported by my device (s32_LE). Then I specify the input device (-C) and ouput device (-P). The output device also applies the filter as specified in our configuration. Lastly the loop is daemonized so it will run in the background.
This loop might get out of sync given enough time, thus it is a good idea to periodically refresh it with a cron-job script, you can usually put it in something like /etc/cron.hourly/refresh_alsaloop:
Lastly alsaloop should also run on startup. To do this create (or amend) the file /etc/rc.local
The end result
A very cheap wifi-connected, lip-syncing, digital/optical equalizer that works seamlessly.