Time to download the software and sounds.

Connect your NeoTrellis M4 to your computer via a known good power+data USB cable. Your computer (PC/Mac/Linux/Chromebook) should indicate in the file explorer/finder a new flash disk drive named CIRCUITPY.

Click on the Download Project Zip button in the top left of the code listing below to get a zip file of all the needed project files. This includes:

  • code.py
  • color_names.py
  • welcome.wav
  • /xmas subdirectory (contains 32 pre-made audio clips)

Open the ZIP file in your computer File Explorer/Finder. Plug in your NeoTrellis and open the CIRCUITPY flash drive that appears when NeoTrellis is plugged in.

Copy code.py, color_names.py and welcome.wav to the root/main directory.

Copy the directory of sounds, /xmas to the root directory.

Files that have a period in the first character of the filename may be made on Mac computers, no worries.

Click Download Project Zip below to get the full project with all the files
# SPDX-FileCopyrightText: 2018 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import time
import board
import audioio
import audiocore
import adafruit_fancyled.adafruit_fancyled as fancy
import adafruit_trellism4
from color_names import * # pylint: disable=wildcard-import,unused-wildcard-import


PLAY_SAMPLES_ON_START = False

SAMPLE_FOLDER = "/xmas/"  # the name of the folder containing the samples
# This soundboard can select up to *32* sound clips! each one has a filename
# which will be inside the SAMPLE_FOLDER above, and a *color* in a tuple ()
SAMPLES = [("01.wav", RED),
           ("02.wav", RED),
           ("03.wav", RED),
           ("04.wav", GREEN),
           ("05.wav", GREEN),
           ("06.wav", RED),
           ("07.wav", RED),
           ("08.wav", RED),
           ("09.wav", RED),
           ("10.wav", RED),
           ("11.wav", GREEN),
           ("12.wav", GREEN),
           ("13.wav", GREEN),
           ("14.wav", GREEN),
           ("15.wav", RED),
           ("16.wav", RED),
           ("17.wav", RED),
           ("18.wav", GREEN),
           ("19.wav", GREEN),
           ("20.wav", GREEN),
           ("21.wav", GREEN),
           ("22.wav", GREEN),
           ("23.wav", GREEN),
           ("24.wav", RED),
           ("25.wav", RED),
           ("26.wav", RED),
           ("27.wav", RED),
           ("28.wav", ORANGE),
           ("29.wav", ORANGE),
           ("30.wav", RED),
           ("31.wav", RED),
           ("32.wav", RED)]

# For the intro, pick any number of colors to make a fancy gradient!
INTRO_SWIRL = [RED, GREEN, BLUE]
# the color for the selected sample
SELECTED_COLOR = TEAL

# Our keypad + neopixel driver
trellis = adafruit_trellism4.TrellisM4Express(rotation=0)

# Play the welcome wav (if its there)
with audioio.AudioOut(board.A1, right_channel=board.A0) as audio:
    try:
        f = open("welcome.wav", "rb")
        wave = audiocore.WaveFile(f)
        audio.play(wave)
        swirl = 0  # we'll swirl through the colors in the gradient
        while audio.playing:
            for i in range(32):
                palette_index = ((swirl+i) % 32) / 32
                color = fancy.palette_lookup(INTRO_SWIRL, palette_index)
                # display it!
                trellis.pixels[(i%8, i//8)] = color.pack()
            swirl += 1
            time.sleep(0.005)
        f.close()
        # Clear all pixels
        trellis.pixels.fill(0)
        # just hold a moment
        time.sleep(0.5)
    except OSError:
        # no biggie, they probably deleted it
        pass

# 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)
    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!")

# Clear all pixels
trellis.pixels.fill(0)

# turn on maybe play all of the buttons
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]
            if PLAY_SAMPLES_ON_START:
                audio.play(wav)
                while audio.playing:
                    pass
    except OSError:
        # File not found! skip to next
        pass

def stop_playing_sample(playback_details):
    print("playing: ", playback_details)
    audio.stop()
    trellis.pixels[playback_details['neopixel_location']] = playback_details['neopixel_color']
    playback_details['file'].close()
    playback_details['voice'] = None

current_press = set()
currently_playing = {'voice' : None}
last_samplenum = None
while True:
    pressed = set(trellis.pressed_keys)
    # if pressed:
    #    print("Pressed:", pressed)

    just_pressed = pressed - current_press
    just_released = current_press - pressed

    # if just_pressed:
    #    print("Just pressed", just_pressed)
    for down in just_pressed:
        sample_num = down[1]*8 + down[0]
        print(sample_num)
        try:
            filename = SAMPLE_FOLDER+SAMPLES[sample_num][0]
            f = open(filename, "rb")
            wav = audiocore.WaveFile(f)

            # is something else playing? interrupt it!
            if currently_playing['voice'] != None:
                print("Interrupt")
                stop_playing_sample(currently_playing)

            trellis.pixels[down] = SELECTED_COLOR
            audio.play(wav)
            # voice, neopixel tuple, color, and sample, file handle
            currently_playing = {
                'voice': 0,
                'neopixel_location': down,
                'neopixel_color': SAMPLES[sample_num][1],
                'sample_num': sample_num,
                'file': f}
        except OSError:
            pass # File not found! skip to next

    # if just_released:
    #    print("Just released:", just_released)

    # check if any samples are done
    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

This guide was first published on Nov 26, 2018. It was last updated on Mar 17, 2024.

This page (Code and Audio Files) was last updated on Mar 17, 2024.

Text editor powered by tinymce.