This project was born after thinking that I'd really like to have something like a Launchpad to control and mix sound ambiances while DMing a Dungeons and Dragons game.
What I wanted was a way to create an immersive atmosphere at the table, by being able to start, stop, pause and resume multiple individual soundtracks, adjust their volume, and flash pretty colors.
I used a Pimoroni Keypad, as well as a Raspberry Pi Pico, for a total budget of roughly 30 euro. The black casing was 3D-printed using the rgb_keypad_-_bottom.stl
file from this Thingiverse model.
The code.py
script runs on a Raspberry Pi Pico, running CircuitPython, itself connected onto the Pimoroni Keypad. The first 12 keys control what audio tracks to play/stop, and the last 4 keys allow you to control the volume of individual tracks, as well as pause the whole stream.
When a key (or a combination of Volume up/down + track key) is pressed, a JSON-formatted message is sent over USB. This message is read by the mixer.py
script, a curses app displaying each individual track, associated with their volume bar.
The mixer.py
script currently uses pygame.mixer
to play the individual track sound files over separate channels. While this works, the startup time can be excruciatingly slow, as each sound file must be fully loaded in memory before the app can start.
This is due to the fact that pygame
can handle sound 2 different ways:
- it can stream large sound files as background music via
mixer.music
(which is what we want!), but it can only play one track at a time() - it can play multiple sound files on different channels via
pygame.mixer.{Sound,Channel}
, but it has to fully load these sound files into memory. It's usually ok because these sounds files are very small, as it's mostly for quick sound effects.
We're trying to shoehorn both mixer.Sound
and mixer.music
together, which has sadly proven to not work, as pygame implement streaming from an audio file directly in the mixer.music
class, without exposing it as a standalone utility.
One way I found to circumvent the previously stated limitations was to implement a slightly more complex web application composed of 3 elements:
- the keypad CircuitPython code
- a webpage in charge of displaying the soundbars and active tracks as well as actually controlling the audio tracks
- a Flask webserver receiving the keypad messages over USB and serving them to the webpage over a websocket, as well as serving the static audio files to the webpage
As the browser is really good at streaming <audio>
elements, the app can start immediately without having to load all audio files in memory.
The key colors were generated from iwanthue and are stored in the COLORS
list, in pico/code.py
. Any changes to the colors will be reflected in the web UI, as they are advertised to the web-server at propagated to the UI when the keypad starts.
(This guide assumes that CircuitPython has been installed on the pico. If that is not the case, follow these instructions first.)
Open a terminal, then run the following commands:
$ cd ~/Downloads
$ curl -L https://github.com/brouberol/pico-mixer/archive/refs/heads/main.zip -o main.zip
$ unzip main.zip
$ cd pico-mixer-main
$ python3 -m pip install --user poetry
$ make install
Plug the keypad, and run:
$ make pico-sync
The keypad should light up. Unplug it.
Now, copy all the sounds files you would like to play (12 max) under the pico_mixer_web/assets/sounds
folder, and replace each example title
attribute under the config.json
file with the name of a sound file you copied under sounds
. Feel free to add a couple of descriptive tags under the tags
attribute. Save the config.json
file.
Run the following command to start the webserver:
$ make webmixer
At that point, the webserver will start and the webpage will open. Plug the keypad in. You are now ready.
(This guide assumes that CircuitPython has been installed on the pico. If that is not the case, follow these instructions first.)
Before being able to execute this application on your Windows machine, you will need to install the Python programming language. To do this, go to the Windows download page, click on the "Latest Python 3 Release" link, and follow the installation instructions.
Now that Python is installed, click on the green Code
button at the top of this page, and then on Download zip
. This will download the project as a zip file in your Downloads
folder. Unzip it by right-clicking on the archive, and click on "Extract here".
Open the pico-mixer-main
folder, and its subfolder, until you can see a README.md
file. Open the pico
folder. Plug the keypad to your computer using the USB cable. A file explorer window should open. Copy all the files in the current pico
folder to the CIRCUITPY
USB volume. At that point, the keypad should light up. Unplug it. Go back to the parent folder.
Double click on the win-install
file to install all dependencies.
Now, copy all the sounds files you would like to play (12 max) under the pico_mixer_web\assets\sounds
folder, and open the config
file with a text editor, such as Notepad. Replace each example title
attribute with the name of a sound file you copied under sounds
and, feel free to add a couple of descriptive tags under the tags
attribute. Save the file.
We can now execute the web server, by double clicking on win-run
. This will start the app and open your internet browser on http://localhost:8000
.
Plug the keypad in. At that point, you should see as many bars as you have sound files (12 max), with colors, and you should see the 🔌 ✅ emoji, indicating that the keypad is plugged and recognized.
Press a key, and lo and behold, a sound should play. If that is not the case, check out the output of the command you ran in the terminal. If you see some lines with 404 -
, this means that you made a typo in the title
attribute of that track, in the config.json
file, and that it does not match the filename of the actual sound file. Fix the typo, kill the webserver by pressing Ctrl-C
and restart it. If nothing works, checke the Q/A at the bottom of the webpage.
Warning: if you find instructions unclear or struggle to run through them, please have a look at the Need help❓ section in the app, and possibly this comment first.