Text Editor

Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.

Alternatively, you can use any text editor that saves simple text files

Download the Project Bundle

Your project will use a specific set of CircuitPython libraries, and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.

Drag the contents of the uncompressed bundle directory onto your KeeBoar board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.

Upload the Code and Libraries to the KB RP2040

After downloading the Project Bundle, plug your KB2040 into the computer USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the KB2040's CIRCUITPY drive. 

  • lib folder
  • wavs folder
  • code.py
# SPDX-FileCopyrightText: 2023 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import os
import audiocore
import board
import audiobusio
import audiomixer
from digitalio import DigitalInOut, Pull
from adafruit_debouncer import Button

button_pins = (
                board.D2, board.D3, board.D4, board.D5,
                board.D6, board.D7, board.D8, board.D9,
                board.D10, board.MOSI, board.MISO, board.SCK,
)
buttons = []   # will hold list of Debouncer button objects
for pin in button_pins:   # set up each pin
    tmp_pin = DigitalInOut(pin)  # defaults to input
    tmp_pin.pull = Pull.UP      # turn on internal pull-down resistor
    buttons.append(Button(tmp_pin, value_when_pressed=False))

# get the filenames in aplha order from files in the 'wavs' directory
sounds = []
for filename in sorted(os.listdir("/wavs")):
    filename = filename.lower()
    if filename.endswith(".wav") and not filename.startswith("."):
        sounds.append(filename)

audio = audiobusio.I2SOut(bit_clock=board.A1, word_select=board.A2, data=board.A3)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=11025, channel_count=1,
                         bits_per_sample=16, samples_signed=True)

audio.play(mixer)
mixer.voice[0].level = 0.5

def play_sound(sound_number):
    wave_file = open(("wavs/" + sounds[sound_number]), "rb")
    wave = audiocore.WaveFile(wave_file)
    mixer.voice[0].play(wave, loop=False)


while True:
    for i in range(len(buttons)):
        buttons[i].update()
        if buttons[i].pressed:
            play_sound(i)

Make Your Own

Here's a great guide on prepping your own audio files for use on microcontrollers.

To convert your own .wav files, use audio software such as Audacity to save them with these settings:

  • bits per sample: 16
  • sample rate: 11kHz 
  • channels: mono

A great place to start is freesound.org. The sounds used in this project are:

https://freesound.org/s/86938/
https://freesound.org/s/125557/
https://freesound.org/s/200338/
https://freesound.org/s/339918/
https://freesound.org/s/353180/
https://freesound.org/s/405628/
https://freesound.org/s/417281/
https://freesound.org/s/434085/
https://freesound.org/s/523952/
https://freesound.org/s/612857/
https://freesound.org/s/619087/
https://freesound.org/s/633976/

How It Works

The code does two key things -- wait for button presses and play audio. Here's how it does it.

First, we import libraries, including os for reading the file system, audiocore, audiobusio, and audiomixer for audio playback/mixing, and digitalio and adafruit_debouncer for button input.

import os
import audiocore
import board
import audiobusio
import audiomixer
from digitalio import DigitalInOut, Pull
from adafruit_debouncer import Button

Button Setup

Next, we set up the button input pins and button objects using the debouncer library.

button_pins = (
                board.D2, board.D3, board.D4, board.D5,
                board.D6, board.D7, board.D8, board.D9,
                board.D10, board.MOSI, board.MISO, board.SCK,
)
buttons = []   # will hold list of Debouncer button objects
for pin in button_pins:   # set up each pin
    tmp_pin = DigitalInOut(pin)  # defaults to input
    tmp_pin.pull = Pull.UP      # turn on internal resistor
    buttons.append(Button(tmp_pin, value_when_pressed=False))

Sound Setup

The sounds list is created by alphabetically sorting the .wav files found in the wavs/ directory. This means you can place track numbers in front of your file names to assign them to the desired spot on the wheel.

Then, the audio object is created to send digital audio over I2S to the amp. The mixer object is created to play the chosen wav, and the audio mixer is set to play with a half-volume voice level of 0.5.

sounds = []
for filename in sorted(os.listdir("/wavs")):
    filename = filename.lower()
    if filename.endswith(".wav") and not filename.startswith("."):
        sounds.append(filename)

audio = audiobusio.I2SOut(bit_clock=board.A1, word_select=board.A2, data=board.A3)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=11025, channel_count=1,
                         bits_per_sample=16, samples_signed=True)

audio.play(mixer)
mixer.voice[0].level = 0.5

play_sound() Function

The play_sound() function is defined to receive an integer number which it then uses to open the corresponding wav file and play it once.

def play_sound(sound_number):
    wave_file = open(("wavs/" + sounds[sound_number]), "rb")
    wave = audiocore.WaveFile(wave_file)
    mixer.voice[0].play(wave, loop=False)

Main Loop

The main loop of the program checks for button presses and when one is pressed it calls the play_sound() function to play the sound.

while True:
    for i in range(len(buttons)):
        buttons[i].update()
        if buttons[i].pressed:
            play_sound(i)

This guide was first published on Feb 01, 2023. It was last updated on May 24, 2024.

This page (Code the See N Say) was last updated on May 24, 2024.

Text editor powered by tinymce.