first commit drum trigger code
This commit is contained in:
parent
1276083b2a
commit
cb33b7a0b7
1 changed files with 224 additions and 0 deletions
224
Drum_Trigger_2040/code.py
Normal file
224
Drum_Trigger_2040/code.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
# SPDX-FileCopyrightText: 2022 John Park for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Drum Trigger Sequencer 2040
|
||||
# Based on code by Tod Kurt @todbot https://github.com/todbot/picostepseq
|
||||
|
||||
# Uses General MIDI drum notes on channel 10
|
||||
# Range is note 35/B0 - 81/A4, but classic 808 set is defined here
|
||||
|
||||
import time
|
||||
from adafruit_ticks import ticks_ms, ticks_diff, ticks_add
|
||||
import board
|
||||
from digitalio import DigitalInOut, Pull
|
||||
import keypad
|
||||
import adafruit_aw9523
|
||||
import usb_midi
|
||||
from adafruit_seesaw import seesaw, rotaryio, digitalio
|
||||
from adafruit_debouncer import Debouncer
|
||||
from adafruit_ht16k33 import segments
|
||||
|
||||
|
||||
# define I2C
|
||||
i2c = board.STEMMA_I2C()
|
||||
|
||||
num_steps = 16 # number of steps/switches
|
||||
num_drums = 11 # primary 808 drums used here, but you can use however many you like
|
||||
# Beat timing assumes 4/4 time signature, e.g. 4 beats per measure, 1/4 note gets the beat
|
||||
bpm = 120 # default BPM
|
||||
beat_time = 60/bpm # time length of a single beat
|
||||
beat_millis = beat_time * 1000 # time length of single beat in milliseconds
|
||||
steps_per_beat = 4 # subdivide beats down to to 16th notes
|
||||
steps_millis = beat_millis / steps_per_beat # time length of a beat subdivision, e.g. 1/16th note
|
||||
|
||||
step_counter = 0 # goes from 0 to length of sequence - 1
|
||||
sequence_length = 16 # how many notes stored in a sequence
|
||||
curr_drum = 0
|
||||
playing = False
|
||||
|
||||
# Setup button
|
||||
start_button_in = DigitalInOut(board.A2)
|
||||
start_button_in.pull = Pull.UP
|
||||
start_button = Debouncer(start_button_in)
|
||||
|
||||
|
||||
# Setup switches
|
||||
switch_pins = (
|
||||
board.TX, board.RX, board.D2, board.D3,
|
||||
board.D4, board.D5, board.D6, board.D7,
|
||||
board.D8, board.D9, board.D10, board.MOSI,
|
||||
board.MISO, board.SCK, board.A0, board.A1
|
||||
)
|
||||
switches = keypad.Keys(switch_pins, value_when_pressed=False, pull=True)
|
||||
|
||||
# Setup LEDs
|
||||
leds = adafruit_aw9523.AW9523(i2c, address=0x5B) # both jumperes soldered on board
|
||||
for led in range(num_steps): # turn them off
|
||||
leds.set_constant_current(led, 0)
|
||||
leds.LED_modes = 0xFFFF # constant current mode
|
||||
leds.directions = 0xFFFF # output
|
||||
|
||||
# Values for LED brightness 0-255
|
||||
offled = 0
|
||||
dimled = 2
|
||||
midled = 20
|
||||
highled = 150
|
||||
|
||||
for led in range(num_steps): # dramatic boot up light sequence
|
||||
leds.set_constant_current(led, dimled)
|
||||
time.sleep(0.05)
|
||||
time.sleep(0.5)
|
||||
#
|
||||
# STEMMA QT Rotary encoder setup
|
||||
rotary_seesaw = seesaw.Seesaw(i2c, addr=0x36) # default address is 0x36
|
||||
encoder = rotaryio.IncrementalEncoder(rotary_seesaw)
|
||||
last_encoder_pos = 0
|
||||
rotary_seesaw.pin_mode(24, rotary_seesaw.INPUT_PULLUP) # setup the button pin
|
||||
knobbutton_in = digitalio.DigitalIO(rotary_seesaw, 24) # use seesaw digitalio
|
||||
knobbutton = Debouncer(knobbutton_in) # create debouncer object for button
|
||||
encoder_pos = -encoder.position
|
||||
|
||||
# MIDI setup
|
||||
midi = usb_midi.ports[1]
|
||||
|
||||
drum_names = [
|
||||
"Bass", "Snar", "LTom", "MTom", "HTom",
|
||||
"Clav", "Clap", "Cowb", "Cymb", "OHat", "CHat"
|
||||
]
|
||||
drum_notes = [36, 38, 41, 43, 45, 37, 39, 56, 49, 46, 42] # general midi drum notes matched to 808
|
||||
|
||||
# default starting sequence needs to match number of drums in num_drums
|
||||
sequence = [
|
||||
[ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 ], # bass drum
|
||||
[ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ], # snare
|
||||
[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ], # low tom
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 ], # mid tom
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ], # high tom
|
||||
[ 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], # rimshot/claves
|
||||
[ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 ], # handclap/maracas
|
||||
[ 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 ], # cowbell
|
||||
[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], # cymbal
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ], # hihat open
|
||||
[ 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0 ] # hihat closed
|
||||
]
|
||||
|
||||
def play_drum(note):
|
||||
midi_msg_on = bytearray([0x99, note, 120]) # 0x90 is noteon ch 1, 0x99 is noteon ch 10
|
||||
midi_msg_off = bytearray([0x89, note, 0])
|
||||
midi.write(midi_msg_on)
|
||||
midi.write(midi_msg_off)
|
||||
|
||||
def light_steps(step, state):
|
||||
if state:
|
||||
leds.set_constant_current(step, midled)
|
||||
else:
|
||||
leds.set_constant_current(step, offled)
|
||||
|
||||
def light_beat(step):
|
||||
leds.set_constant_current(step, highled)
|
||||
|
||||
def edit_mode_toggle():
|
||||
# pylint: disable=global-statement
|
||||
global edit_mode
|
||||
# pylint: disable=used-before-assignment
|
||||
edit_mode = (edit_mode + 1) % num_modes
|
||||
display.fill(0)
|
||||
if edit_mode == 0:
|
||||
display.print(bpm)
|
||||
elif edit_mode == 1:
|
||||
display.print(drum_names[curr_drum])
|
||||
|
||||
def print_sequence():
|
||||
print("sequence = [ ")
|
||||
for k in range(num_drums):
|
||||
print(" [" + ",".join('1' if e else '0' for e in sequence[k]) + "], #", drum_names[k])
|
||||
print("]")
|
||||
|
||||
# set the leds
|
||||
for j in range(sequence_length):
|
||||
light_steps(j, sequence[curr_drum][j])
|
||||
|
||||
display = segments.Seg14x4(i2c, address=(0x71))
|
||||
display.brightness = 0.3
|
||||
display.fill(0)
|
||||
display.show()
|
||||
display.print(bpm)
|
||||
display.show()
|
||||
|
||||
edit_mode = 0 # 0=bpm, 1=voices
|
||||
num_modes = 2
|
||||
|
||||
print("Drum Trigger 2040")
|
||||
|
||||
|
||||
display.fill(0)
|
||||
display.show()
|
||||
display.marquee("Drum", 0.05, loop=False)
|
||||
time.sleep(0.5)
|
||||
display.marquee("Trigger", 0.075, loop=False)
|
||||
time.sleep(0.5)
|
||||
display.marquee("2040", 0.05, loop=False)
|
||||
time.sleep(1)
|
||||
display.marquee("BPM", 0.05, loop=False)
|
||||
time.sleep(0.75)
|
||||
display.marquee(str(bpm), 0.1, loop=False)
|
||||
|
||||
|
||||
while True:
|
||||
start_button.update()
|
||||
if start_button.fell: # pushed encoder button plays/stops transport
|
||||
if playing is True:
|
||||
print_sequence()
|
||||
playing = not playing
|
||||
step_counter = 0
|
||||
last_step = int(ticks_add(ticks_ms(), -steps_millis))
|
||||
print("*** Play:", playing)
|
||||
|
||||
if playing:
|
||||
now = ticks_ms()
|
||||
diff = ticks_diff(now, last_step)
|
||||
if diff >= steps_millis:
|
||||
late_time = ticks_diff(int(diff), int(steps_millis))
|
||||
last_step = ticks_add(now, - late_time//2)
|
||||
|
||||
light_beat(step_counter) # brighten current step
|
||||
for i in range(num_drums):
|
||||
if sequence[i][step_counter]: # if there's a 1 at the step for the seq, play it
|
||||
play_drum(drum_notes[i])
|
||||
light_steps(step_counter, sequence[curr_drum][step_counter]) # return led to step value
|
||||
step_counter = (step_counter + 1) % sequence_length
|
||||
encoder_pos = -encoder.position # only check encoder while playing between steps
|
||||
knobbutton.update()
|
||||
if knobbutton.fell:
|
||||
edit_mode_toggle()
|
||||
else: # check the encoder all the time when not playing
|
||||
encoder_pos = -encoder.position
|
||||
knobbutton.update()
|
||||
if knobbutton.fell: # change edit mode, refresh display
|
||||
edit_mode_toggle()
|
||||
|
||||
# switches add or remove steps
|
||||
switch = switches.events.get()
|
||||
if switch:
|
||||
if switch.pressed:
|
||||
i = switch.key_number
|
||||
sequence[curr_drum][i] = not sequence[curr_drum][i] # toggle step
|
||||
light_steps(i, sequence[curr_drum][i]) # toggle light
|
||||
|
||||
if encoder_pos != last_encoder_pos:
|
||||
encoder_delta = encoder_pos - last_encoder_pos
|
||||
if edit_mode == 0:
|
||||
bpm = bpm + encoder_delta # or (encoder_delta * 5)
|
||||
bpm = min(max(bpm, 10), 400)
|
||||
beat_time = 60/bpm # time length of a single beat
|
||||
beat_millis = beat_time * 1000
|
||||
steps_millis = beat_millis / steps_per_beat
|
||||
display.fill(0)
|
||||
display.print(bpm)
|
||||
if edit_mode == 1:
|
||||
curr_drum = (curr_drum + encoder_delta) % num_drums
|
||||
# quickly set the step leds
|
||||
for i in range(sequence_length):
|
||||
light_steps(i, sequence[curr_drum][i])
|
||||
display.print(drum_names[curr_drum])
|
||||
last_encoder_pos = encoder_pos
|
||||
Loading…
Reference in a new issue