I2S Volume Control With Raspberry Pi Pico and CircuitPython
January 05, 2023
Blog
In a recent article, I outlined how to play MP3 files on the Raspberry Pi Pico with no extra components, just a small speaker. I also talked about doing this with the addition of an amplifier. While either is fine for many applications, the Pico does not have a true ADC, meaning it instead uses less-than-perfect PWM to simulate an analog output. For better audio quality and output level control, you can instead use digital I2S signaling.
Since I2S, or Inter-IC Sound, is a digital signal, it uses an external device to process and amplify sounds, and you’re no longer restricted to PWM. For this I2S experimentation, I used Adafruit’s MAX98357 I2S mono amplifier breakout, which takes care of decoding and power duties. Other options may be more appropriate to your setup–such as something with stereo, line-level audio, and/or a 3.5mm jack–but this breakout is a good starting point.
Adafruit, in their typical style, has excellent information on hooking this board up to the Pico, and code for general sound output. Here I’m going to focus on MP3 playback, as well as how you can vary I2S volume output programmatically with the Pico and CircuitPython.
(Caption: Adafruit circuit diagram/Image Credit: Adafruit)
I hooked up my amplifier largely the same way as shown above, with the addition of an inline tuning resistor for manual volume control as needed. The I2S Tone Playback code found on Adafruit worked without issue, and it pulsed out a 440 Hz test tone. Digital audio was a go!
Single Track I2S Volume Modification
If you’d like to simply turn down the volume of a single recorded track programmatically, I posted simplified code for doing so here. Just vary line 26: mixer.voice[0].level = 1 to whatever level between 0 and 1 that you prefer. Hardware-wiseI also connected the SD pin to 3.3V as explained further here, and experimented with using capacitors (44nF and 33µF) to help stabilize the input.
Mix Recorded Clip Volumes Digitally With I2S
If you’d like to play back multiple audio clips together, todbot has a nice example of how you can mix two tracks simultaneously, and vary their individual volume levels. I modified his code to use GPIO 1-3 to match the previous hookup test. My modified example has a few other changes, including that one audio voice is set to play an MP3 file, while the other loads a WAV file. This shows how to set either type up, and that they can be used together if needed.
For my example, the WAV file is found here (wav/drumsacuff_22k_s16.wav), and the MP3 file here (bariuke-22050-32k.mp3). You’ll need to create two separate directories for the different types of files: wav and mp3. This type of folder organization is perhaps overkill here, but it provides a nice framework for future modification. When producing your sound files, you’ll need to make sure the sample rate matches up with what your code expects!
Impressive Performance, Variable Volume (Don’t Toss Your Hi-Fi)
Better quality: I2S playback of baritone ukulele tune on @Raspberry_Pi Pico #BariUke pic.twitter.com/vaF1ib6jma
— Jeremy Cook 🤖 (@JeremySCook) December 21, 2022
While my setup may not be perfect even after my hardware experimentation, overall I’m quite impressed with how well you can source audio from the $4 Pico dev board. At the same time, such a setup is no substitute for a system costing orders of magnitude more that the Pico via I2S. This capability seems perfect for audible user interfaces and the like, which need decent quality and/or variable volume, but not audiophile-level sound production.