Random Sound Tin

2018-05-19

My daughter is approaching the 6 month mark. This is a time when kids start to develop gross motor skills and begin to express themselves through play. In anticipation, I jumped at the chance to make some dynamic toys for her in addition to the mostly rigid ones she’s collected so far.

Ended up doing a Friday late night build. Since I didn’t have a collection of sounds ready, I just dumped all the sound effects from the DOS game Blood as a test.

Hardware

A few items were at my disposal for this project:

  • 1 x Raspberry Pi model B+
  • 1 x JBL Clip with an aged battery (3.7v 600mAh)
  • 1 x cheap USB numpad
  • 1 x microUSB power pack – free conference knick-knack
  • 1 x 2GB microSD card
  • 1 x Chinese green tea tin

The primary component for this sound tin is the JBL Clip. After all, you can’t hear anything without a decent speaker! Disassembling it revealed a PCB with a footprint just the right size to fit on the floor of the tin.

A few holes drilled in a hexagonal pattern on one side of the tin serve as the speaker grill. The JBL Clip battery was losing capacity due to age but is still functional.

After the guts of the JBL Clip were mounted inside, the USB numpad needed a similar treatment; the only difference was that the keys were mounted on the outside.

Software

The SD card was loaded with the latest version of Raspbian Lite, leaving a little over 600MB free for sound files and everything else. I turned on console auto-login via raspi-config and created two bash scripts:

1. playRandom.sh

Used to randomly select a single WAV file within a folder and play it back with aplay.

#!/bin/sh

ls *.wav |sort -R |tail -$N |while read file; do
    aplay $file
    exit 0
done

2. randomSoundPlayer.sh

Used to continuously listen for key presses so it can either execute playRandom.sh or quit to the shell.

#!/bin/sh

echo Press Q to exit, any other key to play a random sound

while true; do
    read -t 0.3 -N 1 input
    if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
        echo
        break
    elif [[ -n $input ]]; then
        ./sfx/playRandom.sh &
    fi
done

All that was needed to finish this was to add a line at the end of ~/.bash_profile to run randomSoundPlayer.sh on login. This ensures the final step of any boot sequence on the device will start the sound player loop.

Getting sounds

There are lots of nice sound libraries and sound effects banks on the internet, but one particularly good example was “soundsnap.com”. I chose this one since it had an exceptionally simple filtering and searching mechanism (e.g. search all sounds for “animal” tag and duration of < 2 seconds).

Needed a few other pieces of glue code to turn this site into an easy to download source of sound effects:

  • puppeteer (headless Chromium API)
  • ffmpeg (optionally convert MP3 into PCM-WAV)
  • ffmpeg-normalize (normalize audio loudness)

I’ve put all the code together for this into a soundscraper. Just follow the readme instructions and you should have some scraped sounds for personal use in no time.

Normalizing the audio loudness

The downloaded sound files can be all over the place in terms of loudness. To ensure sounds are never too quite nor too loud, we have to normalize them: for this, I’ve selected the EBU R128 normalization preset in ffmpeg. This is a broadcasting standard norm that should be good enough for our purposes.

Using yarn normalize, we can run an ffmpeg-normalize in batch mode to convert all our non-normalized MP3s into MP2s. They end up in a mp3/normalized folder within our tool.