sequencer code, debouncer, and samples first commit

This commit is contained in:
John Edgar Park 2019-01-04 12:54:17 -08:00
parent 7af22472fc
commit 4ed8f3483d
6 changed files with 241 additions and 0 deletions

View file

@ -0,0 +1,145 @@
"""
Opto Mechanical Disc Step Sequencer from John Park's Workshop
Crickit Feather M4 Express, Crickit FeatherWing, continuous servo,
four reflection sensors, speaker
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.
"""
import audioio
import board
from digitalio import DigitalInOut, Direction
from adafruit_crickit import crickit
from debouncer import Debouncer
# You get 4 samples, they must all have the same sample rate and must
# all be mono or stereo (no mix-n-match!)
# mixer info https://circuitpython.readthedocs.io/en/latest/shared-bindings/audioio/Mixer.html
VOICES = ["bd_tek.wav", "elec_hi_snare.wav", "ch_01.wav", "clap_01.wav"]
# Parse the first file to figure out what format its in
with open(VOICES[0], "rb") as f:
wav = audioio.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.A0)
elif wav.channel_count == 2:
# audio = audioio.AudioOut(board.A0, right_channel=board.A0)
audio = audioio.AudioOut(board.A0)
else:
raise RuntimeError("Must be mono or stereo waves!")
mixer = audioio.Mixer(voice_count=4,
sample_rate=wav.sample_rate,
channel_count=wav.channel_count,
bits_per_sample=wav.bits_per_sample,
samples_signed=True)
audio.play(mixer)
samples = []
# Read the 4 wave files, convert to stereo samples, and store
# (show load status on neopixels and play audio once loaded too!)
for v in VOICES:
wave_file = open(v, "rb")
print(v)
# OK we managed to open the wave OK
sample = audioio.WaveFile(wave_file)
# debug play back on load!
mixer.play(sample, voice=0)
while mixer.playing:
pass
samples.append(sample)
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT
# For signal control, we'll chat directly with seesaw, use 'ss' to shorten typing!
ss = crickit.seesaw
# define and set up inputs to use the debouncer
def make_criket_signal_debouncer(pin): # create pin signal objects
ss.pin_mode(pin, ss.INPUT_PULLUP)
return Debouncer(lambda : ss.digital_read(pin))
# The IR sensors on are pullups, connect to ground to activate
clock_pin = make_criket_signal_debouncer(crickit.SIGNAL1)
voice_1_pin = make_criket_signal_debouncer(crickit.SIGNAL2)
voice_2_pin = make_criket_signal_debouncer(crickit.SIGNAL3)
voice_3_pin = make_criket_signal_debouncer(crickit.SIGNAL4)
voice_4_pin = make_criket_signal_debouncer(crickit.SIGNAL5)
# Crickit capacitive touch pads
touch_1_pad = Debouncer(lambda: crickit.touch_1.value)
touch_4_pad = Debouncer(lambda: crickit.touch_4.value)
touch_2_3_pad = Debouncer(lambda: crickit.touch_2.value and crickit.touch_3.value)
crickit.continuous_servo_1.set_pulse_width_range(min_pulse=500, max_pulse=2500)
speed = -0.04 #this is clockwise/forward at a moderate tempo
def play_voice(vo):
mixer.stop_voice(vo)
mixer.play(samples[vo], voice=vo, loop=False)
while True:
clock_pin.update() #debouncer at work
voice_1_pin.update()
voice_2_pin.update()
voice_3_pin.update()
voice_4_pin.update()
touch_1_pad.update()
touch_4_pad.update()
touch_2_3_pad.update()
crickit.continuous_servo_1.throttle = speed # spin the disc at speed defined by touch pads
if clock_pin.fell: # sensor noticed change from white (reflection) to black (no reflection)
# this means a clock tick has begun, time to check if any steps will play
led.value = 0
if voice_1_pin.value: # a black step (no reflection) mark during clock tick, play a sound!
led.value = 1 # light up LED when step is read
# print('| .kick. | | | |')
play_voice(0)
if voice_2_pin.value:
led.value = 1
# print('| | .snare. | | |')
play_voice(1)
if voice_3_pin.value:
led.value = 1
# print('| | | .closed hat. | |')
play_voice(2)
if voice_4_pin.value:
led.value = 1
# print('| | | | .clap. |')
play_voice(3)
if touch_4_pad.rose: # speed it up
speed -= 0.001
# print("speed: %s" % speed)
if touch_1_pad.rose: # slow it down
speed += 0.001
# you can comment out the next two lines if you want to go backwards
# however, the clock ticks may not register with the default template spacing
if speed >= 0: # to prevent backwards
speed = 0
# print("speed: %s" % speed)
if touch_2_3_pad.rose: # stop the disc
speed = 0
# print("speed: %s" % speed)

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,96 @@
"""
GPIO Pin Debouncer
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.
"""
import time
import digitalio
class Debouncer(object):
"""Debounce an input pin"""
DEBOUNCED_STATE = 0x01
UNSTABLE_STATE = 0x02
CHANGED_STATE = 0x04
def __init__(self, pin_or_predicate, mode=digitalio.Pull.UP, interval=0.010):
"""Make am instance.
:param int/function pin_or_predicate: the pin (from board) to debounce
:param int mode: digitalio.Pull.UP or .DOWN (default is no pull up/down)
:param int interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds)
"""
self.state = 0x00
if isinstance(pin_or_predicate, int):
p = digitalio.DigitalInOut(pin_or_predicate)
p.direction = digitalio.Direction.INPUT
p.pull = mode
self.f = lambda : p.value
else:
self.f = pin_or_predicate
if self.f():
self.__set_state(Debouncer.DEBOUNCED_STATE | Debouncer.UNSTABLE_STATE)
self.previous_time = 0
if interval is None:
self.interval = 0.010
else:
self.interval = interval
def __set_state(self, bits):
self.state |= bits
def __unset_state(self, bits):
self.state &= ~bits
def __toggle_state(self, bits):
self.state ^= bits
def __get_state(self, bits):
return (self.state & bits) != 0
def update(self):
"""Update the debouncer state. Must be called before using any of the properties below"""
now = time.monotonic()
self.__unset_state(Debouncer.CHANGED_STATE)
current_state = self.f()
if current_state != self.__get_state(Debouncer.UNSTABLE_STATE):
self.previous_time = now
self.__toggle_state(Debouncer.UNSTABLE_STATE)
else:
if now - self.previous_time >= self.interval:
if current_state != self.__get_state(Debouncer.DEBOUNCED_STATE):
self.previous_time = now
self.__toggle_state(Debouncer.DEBOUNCED_STATE)
self.__set_state(Debouncer.CHANGED_STATE)
@property
def value(self):
"""Return the current debounced value of the input."""
return self.__get_state(Debouncer.DEBOUNCED_STATE)
@property
def rose(self):
"""Return whether the debounced input went from low to high at the most recent update."""
return self.__get_state(self.DEBOUNCED_STATE) and self.__get_state(self.CHANGED_STATE)
@property
def fell(self):
"""Return whether the debounced input went from high to low at the most recent update."""
return (not self.__get_state(self.DEBOUNCED_STATE)) and self.__get_state(self.CHANGED_STATE)

Binary file not shown.

Binary file not shown.