|
| 1 | +# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +""" |
| 6 | +Opto Mechanical Disc Step Sequencer from John Park's Workshop |
| 7 | + Crickit Feather M4 Express, Crickit FeatherWing, continuous servo, |
| 8 | + four reflection sensors, speaker |
| 9 | +
|
| 10 | +Adafruit invests time and resources providing this open source code. |
| 11 | +Please support Adafruit and open source hardware by purchasing |
| 12 | +products from Adafruit! |
| 13 | +
|
| 14 | +Written by Dave Astels for Adafruit Industries |
| 15 | +Copyright (c) 2018 Adafruit Industries |
| 16 | +Licensed under the MIT license. |
| 17 | +
|
| 18 | +All text above must be included in any redistribution. |
| 19 | +""" |
| 20 | + |
| 21 | +import audioio |
| 22 | +import audiocore |
| 23 | +import audiomixer |
| 24 | +import board |
| 25 | +from digitalio import DigitalInOut, Direction |
| 26 | +from adafruit_crickit import crickit |
| 27 | +from adafruit_debouncer import Debouncer |
| 28 | + |
| 29 | +# You get 4 samples, they must all have the same sample rate and must |
| 30 | +# all be mono or stereo (no mix-n-match!) |
| 31 | +# mixer info https://circuitpython.readthedocs.io/en/latest/shared-bindings/audiomixer/Mixer.html |
| 32 | + |
| 33 | +VOICES = ["bd_tek.wav", "elec_hi_snare.wav", "ch_01.wav", "clap_01.wav"] |
| 34 | +# Parse the first file to figure out what format its in |
| 35 | +with open(VOICES[0], "rb") as f: |
| 36 | + wav = audiocore.WaveFile(f) |
| 37 | + print("%d channels, %d bits per sample, %d Hz sample rate " % |
| 38 | + (wav.channel_count, wav.bits_per_sample, wav.sample_rate)) |
| 39 | + |
| 40 | + # Audio playback object - we'll go with either mono or stereo depending on |
| 41 | + # what we see in the first file |
| 42 | + if wav.channel_count == 1: |
| 43 | + audio = audioio.AudioOut(board.A0) |
| 44 | + elif wav.channel_count == 2: |
| 45 | + # audio = audioio.AudioOut(board.A0, right_channel=board.A0) |
| 46 | + audio = audioio.AudioOut(board.A0) |
| 47 | + else: |
| 48 | + raise RuntimeError("Must be mono or stereo waves!") |
| 49 | + mixer = audiomixer.Mixer(voice_count=4, |
| 50 | + sample_rate=wav.sample_rate, |
| 51 | + channel_count=wav.channel_count, |
| 52 | + bits_per_sample=wav.bits_per_sample, |
| 53 | + samples_signed=True) |
| 54 | + audio.play(mixer) |
| 55 | + |
| 56 | +samples = [] |
| 57 | +# Read the 4 wave files, convert to stereo samples, and store |
| 58 | +# (show load status on neopixels and play audio once loaded too!) |
| 59 | +for v in VOICES: |
| 60 | + wave_file = open(v, "rb") |
| 61 | + print(v) |
| 62 | + # OK we managed to open the wave OK |
| 63 | + sample = audiocore.WaveFile(wave_file) |
| 64 | + # debug play back on load! |
| 65 | + mixer.play(sample, voice=0) |
| 66 | + while mixer.playing: |
| 67 | + pass |
| 68 | + samples.append(sample) |
| 69 | + |
| 70 | + |
| 71 | +led = DigitalInOut(board.D13) |
| 72 | +led.direction = Direction.OUTPUT |
| 73 | + |
| 74 | +# For signal control, we'll chat directly with seesaw, use 'ss' to shorten typing! |
| 75 | +ss = crickit.seesaw |
| 76 | + |
| 77 | +# define and set up inputs to use the debouncer |
| 78 | +def make_criket_signal_debouncer(pin): # create pin signal objects |
| 79 | + ss.pin_mode(pin, ss.INPUT_PULLUP) |
| 80 | + return Debouncer(lambda : ss.digital_read(pin)) |
| 81 | + |
| 82 | +# The IR sensors on are pullups, connect to ground to activate |
| 83 | +clock_pin = make_criket_signal_debouncer(crickit.SIGNAL1) |
| 84 | +voice_1_pin = make_criket_signal_debouncer(crickit.SIGNAL2) |
| 85 | +voice_2_pin = make_criket_signal_debouncer(crickit.SIGNAL3) |
| 86 | +voice_3_pin = make_criket_signal_debouncer(crickit.SIGNAL4) |
| 87 | +voice_4_pin = make_criket_signal_debouncer(crickit.SIGNAL5) |
| 88 | +# Crickit capacitive touch pads |
| 89 | +touch_1_pad = Debouncer(lambda: crickit.touch_1.value) |
| 90 | +touch_4_pad = Debouncer(lambda: crickit.touch_4.value) |
| 91 | +touch_2_3_pad = Debouncer(lambda: crickit.touch_2.value and crickit.touch_3.value) |
| 92 | + |
| 93 | +crickit.continuous_servo_1.set_pulse_width_range(min_pulse=500, max_pulse=2500) |
| 94 | +speed = -0.04 #this is clockwise/forward at a moderate tempo |
| 95 | + |
| 96 | + |
| 97 | +def play_voice(vo): |
| 98 | + mixer.stop_voice(vo) |
| 99 | + mixer.play(samples[vo], voice=vo, loop=False) |
| 100 | + |
| 101 | +while True: |
| 102 | + clock_pin.update() #debouncer at work |
| 103 | + voice_1_pin.update() |
| 104 | + voice_2_pin.update() |
| 105 | + voice_3_pin.update() |
| 106 | + voice_4_pin.update() |
| 107 | + touch_1_pad.update() |
| 108 | + touch_4_pad.update() |
| 109 | + touch_2_3_pad.update() |
| 110 | + |
| 111 | + crickit.continuous_servo_1.throttle = speed # spin the disc at speed defined by touch pads |
| 112 | + |
| 113 | + if clock_pin.fell: # sensor noticed change from white (reflection) to black (no reflection) |
| 114 | + # this means a clock tick has begun, time to check if any steps will play |
| 115 | + led.value = 0 |
| 116 | + |
| 117 | + if voice_1_pin.value: # a black step (no reflection) mark during clock tick, play a sound! |
| 118 | + led.value = 1 # light up LED when step is read |
| 119 | + # print('| .kick. | | | |') |
| 120 | + play_voice(0) |
| 121 | + |
| 122 | + if voice_2_pin.value: |
| 123 | + led.value = 1 |
| 124 | + # print('| | .snare. | | |') |
| 125 | + play_voice(1) |
| 126 | + |
| 127 | + if voice_3_pin.value: |
| 128 | + led.value = 1 |
| 129 | + # print('| | | .closed hat. | |') |
| 130 | + play_voice(2) |
| 131 | + |
| 132 | + if voice_4_pin.value: |
| 133 | + led.value = 1 |
| 134 | + # print('| | | | .clap. |') |
| 135 | + play_voice(3) |
| 136 | + |
| 137 | + if touch_4_pad.rose: # speed it up |
| 138 | + speed -= 0.001 |
| 139 | + # print("speed: %s" % speed) |
| 140 | + |
| 141 | + if touch_1_pad.rose: # slow it down |
| 142 | + speed += 0.001 |
| 143 | + # you can comment out the next two lines if you want to go backwards |
| 144 | + # however, the clock ticks may not register with the default template spacing |
| 145 | + if speed >= 0: # to prevent backwards |
| 146 | + speed = 0 |
| 147 | + # print("speed: %s" % speed) |
| 148 | + |
| 149 | + if touch_2_3_pad.rose: # stop the disc |
| 150 | + speed = 0 |
| 151 | + # print("speed: %s" % speed) |
0 commit comments