CircuitPython Audio

Amplified audio is available via the Speaker terminals.

 

At left is the Circuit Playground Express and Crickit version.

And this is the Feather and Crickit version.

 

Be sure you order the correct Crickit board for the type of microcontroller you plan to use in your project. While the Crickits are nearly identical in capability, they are not the same in making connections to either Circuit Playground Express or Feather.

The Speaker block on the Crickit HAT for Raspberry Pi is easily to use, on the edge of the board. The onboard amplifier is very handy to provide audio for various projects.

Audio animatronics! By adding a voice or sound effects to your robot you can make a cool interactive project. We take advantage of CircuitPython's ability to play WAV files over the true-analog output pin A0.

This is one of the few outputs that does not go through the seesaw chip. Instead, the audio is played directly from the CircuitPython board and the Crickit only amplifies it!

Audio File Formats

CircuitPython supports Mono (not stereo) 22 KHz sample rate (or less) and 16-bit WAV format. The reason for mono is that there's only one output, 22 KHz or less because the Circuit Playground can't handle more data than that (and also it wont sound much better) and the DAC output is 10-bit so anything over 16-bit will just take up room without better quality

CircuitPython does not support OGG or MP3. Just WAV!

Since the WAV file must fit on the CircuitPython file system, it must be under 2 MB

We have a detailed guide on how to generate WAV files here

Amplifier Details

The onboard amplifier is a mono "Class D" audio amp with BTL (Bridge Tied Load) output.

That means you cannot plug the speaker output into another amplifier, it must connect directly to a speaker!

You can use just about any 4 to 8Ω speaker (6 Ω is OK too, just not as common). The amplifier can drive up to 3 Watts into 4Ω and 1 Watt into 8Ω. That means its ok to drive a 5 Watt speaker, it just wont be as loud as it could be with a bigger amp (but you wont damage the amp). You can also drive speakers that are smaller, like an 8Ω 0.5 W but make sure you don't turn the audio volume potentiometer up, as it could damage the speaker by overpowering it.

Basic Audio Playback

Download: file
import audioio
import board
    
wavfile = "howto.wav"
f = open(wavfile, "rb")
wav = audioio.WaveFile(f)
a = audioio.AudioOut(board.A0)
a.play(wav)

# You can now do all sorts of stuff here while the audio plays
# such as move servos, motors, read sensors...

# Or wait for the audio to finish playing:
while a.playing:
    pass
    
f.close()

Here is the audio file we're using for this example

You must drag/copy this onto your CIRCUITPY disk drive, it's a big file so it will take a minute to copy over

Import Libraries

We start by importing the libraries that we need to make audio output  import audioioThen we importboard, our standardhardware library.

Create wave file and audio output

Next we set the name of the file we want to open, which is a wave file wavfile = "howto.wav" and then open the file as a readable binary and store the file object in f which is what we use to actually read audio from: f = open(wavfile, "rb")

Now we will ask the audio playback system to load the wave data from the file wav = audioio.WaveFile(f) and finally request that the audio is played through the A0 analog output pin a = audioio.AudioOut(board.A0)

The audio file is now locked-and-loaded, and can be played at any time with a.play(wav)

Audio playback occurs in the background, using "DMA" (direct memory access) so you can control servos, motors, read sensors, whatever you like, while the DMA is happening. Since it happens asynchronously, you may want to figure out when its done playing. You can do that by checking the value of a.playing if it's True then its still processing audio, it will return False when complete.

Interactive Audio

OK just playing an audio file is one thing, but maybe you want to have some interactivity, such as waiting for the person to touch something or press a button? Here's an example of using a time-delay and then pausing until something occurs:

Download: file
from busio import I2C
from adafruit_seesaw.seesaw import Seesaw
import audioio
import board
import time

# Create seesaw object
i2c = I2C(board.SCL, board.SDA)
seesaw = Seesaw(i2c)

# what counts as a 'touch'
CAPTOUCH_THRESH = 500

wavfile = "howto.wav"
f = open(wavfile, "rb")
wav = audioio.WaveFile(f)
a = audioio.AudioOut(board.A0)
a.play(wav)

t = time.monotonic()  # this is the time when we started

# wait until we're at timecode 5.5 seconds into the audio
while time.monotonic() - t < 5.5:
    pass

a.pause()   # pause the audio

print("Waiting for Capacitive touch!")
while seesaw.touch_read(0) < CAPTOUCH_THRESH:
    pass

a.resume()   # resume the audio

# You can now do all sorts of stuff here while the audio plays
# such as move servos, motors, read sensors...

# Or wait for the audio to finish playing:
while a.playing:
    pass
print("Done!")

You may want to have the audio track match to motion or events in your robot. To do that you can do some tricks with time.monotonic(). That's our way of know true time passage, it returns a floating point (fractional) value in seconds. Note its hard to get the exact precise second so use > and < rather than checking for = equality because minute variations will make it hard to get the time delta exactly when it occurs.

This guide was first published on May 16, 2018. It was last updated on May 16, 2018. This page (CircuitPython Audio) was last updated on Sep 16, 2019.