Skip to content

Commit 0804d36

Browse files
authored
Merge pull request #2419 from jedgarpark/meowsic-midi
meowsic midi code
2 parents fa2633e + 11819ee commit 0804d36

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

Meowsic_MIDI/code.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# SPDX-FileCopyrightText: 2023 John Park for Adafruit
2+
#
3+
# SPDX-License-Identifier: MIT
4+
# Meowsic Toy Piano MIDI Keyboard
5+
6+
import keypad
7+
import board
8+
import busio
9+
import supervisor
10+
from adafruit_simplemath import map_range
11+
from adafruit_msa3xx import MSA311
12+
import usb_midi
13+
import adafruit_midi
14+
from adafruit_midi.note_on import NoteOn
15+
from adafruit_midi.note_off import NoteOff
16+
from adafruit_midi.control_change import ControlChange
17+
from adafruit_midi.program_change import ProgramChange
18+
# from adafruit_midi.start import Start
19+
# from adafruit_midi.stop import Stop
20+
from adafruit_midi.pitch_bend import PitchBend
21+
22+
supervisor.runtime.autoreload = False # prevent unwanted restarts due to OS weirdness
23+
24+
i2c = board.STEMMA_I2C()
25+
msa = MSA311(i2c)
26+
27+
key_matrix = keypad.KeyMatrix(
28+
column_pins=(board.D2, board.D3, board.D4, board.D5, board.D6, board.D7, board.D8, board.D9),
29+
row_pins=(board.D10, board.MOSI, board.MISO, board.CLK, board.A0, board.A1)
30+
)
31+
32+
midi_uart = busio.UART(board.TX, None, baudrate=31250)
33+
34+
midi_usb_channel = 1
35+
midi_usb = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=midi_usb_channel-1)
36+
midi_serial_channel = 4
37+
midi_serial = adafruit_midi.MIDI(midi_out=midi_uart, out_channel=midi_serial_channel-1)
38+
39+
octave = 4
40+
note_offset = 9 # first note on keyboard is an A, first key in keypad matrix is 0
41+
42+
def send_note_on(note, octv):
43+
note = ((note+note_offset)+(12*octv))
44+
midi_usb.send(NoteOn(note, 120))
45+
midi_serial.send(NoteOn(note, 120))
46+
47+
def send_note_off(note, octv):
48+
note = ((note+note_offset)+(12*octv))
49+
midi_usb.send(NoteOff(note, 0))
50+
midi_serial.send(NoteOff(note, 0))
51+
52+
def send_cc(number, val):
53+
midi_usb.send(ControlChange(number, val))
54+
midi_serial.send(ControlChange(number, val))
55+
56+
def send_pc(bank, folder, patch):
57+
send_cc(0, bank)
58+
send_cc(32, folder)
59+
midi_usb.send(ProgramChange(patch))
60+
midi_serial.send(ProgramChange(patch))
61+
62+
def send_bend(bend_val):
63+
midi_usb.send(PitchBend(bend_val))
64+
midi_serial.send(PitchBend(bend_val))
65+
66+
def send_midi_panic():
67+
for x in range(128):
68+
midi_usb.send(NoteOff(x, 0))
69+
midi_serial.send(NoteOff(x, 0))
70+
71+
# key ranges
72+
piano_keys = range(0, 28) # note 'range()' excludes last value, so add one
73+
toes = ( list(range(28, 33)) + list(range(35, 40)) ) # L/R toe series is interruped by 33, 34
74+
clef_button = 33
75+
nose_button = 47
76+
face_button = 34
77+
record_button = 44
78+
play_button = 45
79+
80+
# patch assigments
81+
patch_list = (
82+
(0,0,0), # piano
83+
(1,0,0), # bells
84+
(1,0,1), # meow
85+
(2,0,0), # organ
86+
(3,0,0), # banjo
87+
(4,0,1), # rock
88+
(5,0,0), # blues
89+
(6,0,0), # samba
90+
(7,0,1), # tencho
91+
(8,0,4) # disco
92+
)
93+
94+
# accelerometer filtering variables
95+
slop = 0.2 # threshold for accelerometer send
96+
filter_percent = 0.5 # ranges from 0.0 to 1.0
97+
accel_data_y = msa.acceleration[1]
98+
last_accel_data_y = msa.acceleration[1]
99+
100+
cone_mode = False # mod wheel vs pitch bend
101+
started = False # state of arp/seq play
102+
103+
104+
while True:
105+
new_data_y = msa.acceleration[1]
106+
accel_data_y = ((new_data_y * filter_percent) + (1-filter_percent) * accel_data_y) # smoothed
107+
if abs(accel_data_y - last_accel_data_y) > slop:
108+
if cone_mode is True: # pitch bend mode
109+
pitch = int(map_range(accel_data_y, 9, -9, 0, 16383))
110+
send_bend(pitch)
111+
112+
else:
113+
modulation = int(map_range(accel_data_y, 9, -9, 0, 127))
114+
send_cc(1, modulation)
115+
116+
last_accel_data_y = accel_data_y
117+
118+
event = key_matrix.events.get()
119+
if event:
120+
if event.pressed:
121+
key = event.key_number
122+
# Note keys
123+
if key in piano_keys: # its one of the piano keys
124+
send_note_on(key, octave)
125+
# Patch buttons (cat toes)
126+
if key in toes:
127+
pc_key = toes.index(key) # remove offset for patch list indexing
128+
send_pc(patch_list[pc_key][0], patch_list[pc_key][1], patch_list[pc_key][2])
129+
# Play key
130+
if key is play_button:
131+
if not started:
132+
# use midi learning and a CC
133+
send_cc(16, 127) # map to seq/arp on/off Synth One
134+
# midi_usb.send(Start())
135+
started = True
136+
else:
137+
send_cc(16, 0)
138+
# midi_usb.send(Stop())
139+
started = False
140+
# Record key
141+
if key is record_button:
142+
if cone_mode:
143+
send_bend(8191) # 'zero' out pitch bend to center position
144+
cone_mode = False
145+
else:
146+
send_cc(1, 64) # 'zero' out the mod wheel to center position
147+
cone_mode = True
148+
149+
# Clef
150+
if key is clef_button: # octave down
151+
octave = min(max((octave - 1), 0), 7 )
152+
# Face
153+
if key is face_button: # octave up
154+
octave = min(max((octave + 1), 0), 7 )
155+
# STOP
156+
if key is nose_button:
157+
send_midi_panic() # all notes off
158+
159+
if event.released:
160+
key = event.key_number
161+
if key in piano_keys:
162+
send_note_off(key, octave)

0 commit comments

Comments
 (0)