Wireless Audio Visualizer – Part I

My last YouTube video, showing a wireless VU-meter module with a small audio spectrum analyzer, was surprisingly well received. Soon after asking me for the code, some sneaky YouTuber now tries to make money out my idea and code, without even crediting me! Another reason for me to stop sharing code links via YouTube.

Here’s Part I of the project. Part II (the wireless module) is covered in the next post.

Audio Visualizer


The idea was to let an ESP32 ‘base station’ convert analog stereo output from my amplifier to digital data, make it calculate VU levels and FFT frequency magnitudes and broadcast their values via ESP-NOW. Any ESP-NOW ‘receiver’ within range could then use them for audio visualizion on a display. The base station can be placed out of sight and be powered by a 5V adapter plugged into an AC outlet on the back of my amplifier.

Part I – Base station

Any ESP32 can perform the tasks outlined above, but conversion of the AC audio signal would normally require some rectifier circuit or two voltage dividers, as an ESP32 can only handle DC voltages (in the 0-3.3V range). Although that totally works, I was lucky to come across the AI-Thinker AudioKit V2.2 (and some great libraries).

Powered by an ESP32-A1S chip with built-in es8388 codec, this board allows you to plug an analog stereo source directly into its LINEIN input. You can program it with Espressif’s own Audio Development Framework, but I prefer the Arduino IDE, allowing me to use Phil Schatzmann’s arduino-audio-tools library, truly a Swiss Army Knife for audio projects.

I first tried the above library’s streams-audiokit-audiokit example. It loops back the analog input via a digital (I2S) stream to the board’s earphone plug, but unfortunately it produced a terrible distortion. My guess that the used es8388 library caused the trouble seemed right when I used a different es8388 library instead. Its streams-i2s-i2s example produced crystal clear sound, and since that example further uses the arduino-audio-tools library, all of that library’s extensive functionality remains available.

Now that digital audio data flowed through the ESP32, I only had to find out how to tap it programmatically. Few libraries on Github will have better documentation and support than the arduino-audio-tools library. Built on the concept of Arduino streams, it allows you to control an audio stream from source to destination (sink). With a little help from Phil (issue #161), I figured out two tapping mechanisms that I could use for my base station.

1. For calculating VU levels, I defined the following subclass of the library’s I2S class:

The data array holds a stream chunk of 4608 uint8_t values, casted to 2304 int16_t values, corresponding with 1152 left-right sample value pairs. After the for loop, global variables left and right will hold the maximum peak-to-peak values of the analyzed sample for both stereo channels. A global boolean sampled is set to true, which will trigger a freeRTOS task to perform an ESP-NOW broadcast. An object of this customized MyI2S class can now be used as a stream destination. In the code it’s named i2s.

2. For the Fast Fourier Transformation, I used the AudioRealFFT library. It lets you create an object of the AudioRealFFT class that can also be used as a stream destination. I made its callback function fill a global array of 32 frequency magnitudes that will be broadcast via ESP-NOW. In the code it’s named fft.

Having two different destinations for the digital audio stream is no problem, thanks to the library’s MultiOutput class. An object of this class acts as a kind of ‘destination envelope’ that can hold multiple destination objects. In the code it’s named multi and holds the previously mentioned i2s and fft objects.

After specifying input and ouput of the es8388 codec

we have defined our audio stream from source (line input) to sink (headphone), including taps for calculating both VU levels and FFT magnitudes.

The complete code that I used for the base station is at the bottom of this post . Note that compilation requires version 2.0.2 or higher of the arduino-esp32 package. To avoid conflicts with any other installed es8388 libraries, I put the es8388.h and es8388.cpp files from thaaraak’s library in the sketch folder. In the Arduino IDE , I selected the ESP32 Dev Module with PSRAM enabled and the Huge App partition scheme (probably not necessary).

After uploading the sketch, an AudioKit V2.2 with es8388 codec (connected to an audio source) will broadcast the following struct:

You can check if it works by uploading the following sketch to an ESP32 and view the results (VU values) in the Serial Plotter.



Complete code for the base station: