We'll be using CircuitPython for this project. Are you new to using CircuitPython? No worries, there is a full getting started guide here.
Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in CircuitPython. You can learn about Mu and its installation in this tutorial.
There's a guide to get you up and running with Circuit Python specifically for the NeoTrellis M4. You should read it before starting to get the most recent CircuitPython build for the NeoTrellis M4 installed and running along with the required libraries.
Navigating the NeoTrellis
To get your NeoTrellis M4 set up to run this project's code, first follow these steps:
1) Update the bootloader for NeoTrellis from the NeoTrellis M4 guide
2) Install the latest CircuitPython for NeoTrellis from the NeoTrellis M4 guide
For this project you will need the following libraries:
- adafruit_trellism4.mpy
- neopixel.mpy
- adafruit_matrixkeypad.mpy
Download Code
Time to install the software, here's the code.py listing, click on the Download Project Bundle link in the top left to grab all the code.
# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Tabletop RPG soundboard for the NeoTrellisM4
Adafruit invests time and resources providing this open source code.
Please support Adafruit and open source hardware by purchasing
products from Adafruit!
Written by Dave Astels for Adafruit Industries
Copyright (c) 2018 Adafruit Industries
Licensed under the MIT license.
All text above must be included in any redistribution.
"""
# pylint: disable=wildcard-import,unused-wildcard-import,eval-used
import time
import board
import audioio
import audiocore
import audiomixer
import adafruit_trellism4
from color_names import *
# Our keypad + neopixel driver
trellis = adafruit_trellism4.TrellisM4Express(rotation=0)
SELECTED_COLOR = WHITE # the color for the selected sample
SAMPLE_FOLDER = '/samples/' # the name of the folder containing the samples
SAMPLES = []
BLACK = 0x000000
# load the sound & color specifications
with open('soundboard.txt', 'r') as f:
for line in f:
cleaned = line.strip()
if len(cleaned) > 0 and cleaned[0] != '#':
if cleaned == 'pass':
SAMPLES.append(('does_not_exist.wav', BLACK))
else:
f_name, color = cleaned.split(',', 1)
SAMPLES.append((f_name.strip(), eval(color.strip())))
# Parse the first file to figure out what format its in
channel_count = None
bits_per_sample = None
sample_rate = None
with open(SAMPLE_FOLDER+SAMPLES[0][0], 'rb') as f:
wav = audiocore.WaveFile(f)
channel_count = wav.channel_count
bits_per_sample = wav.bits_per_sample
sample_rate = wav.sample_rate
print('%d channels, %d bits per sample, %d Hz sample rate ' %
(wav.channel_count, wav.bits_per_sample, wav.sample_rate))
# Audio playback object - we'll go with either mono or stereo depending on
# what we see in the first file
if wav.channel_count == 1:
audio = audioio.AudioOut(board.A1)
elif wav.channel_count == 2:
audio = audioio.AudioOut(board.A1, right_channel=board.A0)
else:
raise RuntimeError('Must be mono or stereo waves!')
mixer = audiomixer.Mixer(voice_count=2,
sample_rate=sample_rate,
channel_count=channel_count,
bits_per_sample=bits_per_sample,
samples_signed=True)
audio.play(mixer)
# Clear all pixels
trellis.pixels.fill(0)
# Light up button with a valid sound file attached
for i, v in enumerate(SAMPLES):
filename = SAMPLE_FOLDER+v[0]
try:
with open(filename, 'rb') as f:
wav = audiocore.WaveFile(f)
print(filename,
'%d channels, %d bits per sample, %d Hz sample rate ' %
(wav.channel_count, wav.bits_per_sample, wav.sample_rate))
if wav.channel_count != channel_count:
pass
if wav.bits_per_sample != bits_per_sample:
pass
if wav.sample_rate != sample_rate:
pass
trellis.pixels[(i % 8, i // 8)] = v[1]
except OSError:
# File not found! skip to next
pass
def stop_playing_sample(details):
print('playing: ', details)
mixer.stop_voice(details['voice'])
trellis.pixels[details['neopixel_location']] = details['neopixel_color']
details['file'].close()
details['voice'] = None
current_press = set()
current_background = {'voice' : None}
currently_playing = {'voice' : None}
while True:
pressed = set(trellis.pressed_keys)
just_pressed = pressed - current_press
# just_released = current_press - pressed
for down in just_pressed:
sample_num = down[1]*8 + down[0]
try:
filename = SAMPLE_FOLDER+SAMPLES[sample_num][0]
f = open(filename, 'rb')
wav = audiocore.WaveFile(f)
if down[1] == 0: # background loop?
if current_background['voice'] != None:
print('Interrupt')
stop_playing_sample(current_background)
trellis.pixels[down] = WHITE
mixer.play(wav, voice=0, loop=True)
current_background = {
'voice': 0,
'neopixel_location': down,
'neopixel_color': SAMPLES[sample_num][1],
'sample_num': sample_num,
'file': f}
else:
if currently_playing['voice'] != None:
print('Interrupt')
stop_playing_sample(currently_playing)
trellis.pixels[down] = WHITE
mixer.play(wav, voice=1, loop=False)
currently_playing = {
'voice': 1,
'neopixel_location': down,
'neopixel_color': SAMPLES[sample_num][1],
'sample_num': sample_num,
'file': f}
except OSError:
pass # File not found! skip to next
# # check if any samples are done
# # this currently doesn't work with the mixer until it supports per voice "is_playing" checking
# if not audio.playing and currently_playing['voice'] != None:
# stop_playing_sample(currently_playing)
time.sleep(0.01) # a little delay here helps avoid debounce annoyances
current_press = pressed
The code.py CircuitPython code should run as soon as you copy the file onto the NeoTrellis CIRCUITPY drive. Be sure you copy all the files shown above to your CIRCUITPY drive.
Now you can start pressing buttons for your sound effects!
If some sounds work but not others, you'll need to edit the soundboard.txt file as explained on the previous page to ensure the file names are correct and you get the color you want for the button.
If you believe you have the soundboard.txt file correct and you do not hear the sound:
- Check your cabling to ensure you have powered amplified speakers and that the are on and volume at a good level.
- Be sure all of your sounds have been converted to 22,050 KHz, 16-bit PCM WAV files. Use all Mono or Stereo files, don't mix Mono and Stereo. This guide will help you with the conversions. MP3 files cannot be played in this project, sorry, they'll need conversion to WAV.
Page last edited January 22, 2025
Text editor powered by tinymce.