Adafruit_Learning_System_Gu.../NeoTrellis_M4_RPG_Sound_Board/code.py
2022-02-23 12:56:10 -05:00

161 lines
5.3 KiB
Python

# 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