11# SPDX-FileCopyrightText: 2023 John Park for Adafruit
22#
33# SPDX-License-Identifier: MIT
4- # Meowsic Toy Piano MIDI Keyboard
4+ # Cyber Cat MIDI Keyboard conversion for Meowsic Cat Piano
5+
6+ # Functions:
7+ # --28 keys
8+ # --left five toe buttons: patches
9+ # --right five toe buttons: picking CC number for ice cream cone control
10+ # --volume arrows: octave up/down
11+ # --tempo arrows: pitchbend up/down
12+ # --on switch: reset
13+ # --nose button: midi panic
14+ # --record button: ice cream cone CC enable/disable (led indicator)
15+ # --play button: start stop arp or sequence in soft synth via cc 16 0/127
16+ # --treble clef button: hold notes (use nose to turn off all notes)
17+ # --face button: momentary CC 0/127 on CC number 17
518
619import keypad
720import board
821import busio
922import supervisor
23+ import digitalio
1024from adafruit_simplemath import map_range
1125from adafruit_msa3xx import MSA311
1226import usb_midi
1529from adafruit_midi .note_off import NoteOff
1630from adafruit_midi .control_change import ControlChange
1731from adafruit_midi .program_change import ProgramChange
18- # from adafruit_midi.start import Start
19- # from adafruit_midi.stop import Stop
2032from adafruit_midi .pitch_bend import PitchBend
2133
22- supervisor .runtime .autoreload = False # prevent unwanted restarts due to OS weirdness
34+ supervisor .runtime .autoreload = True # set False to prevent unwanted restarts due to OS weirdness
35+
36+ ledpin = digitalio .DigitalInOut (board .A3 )
37+ ledpin .direction = digitalio .Direction .OUTPUT
38+ ledpin .value = True
2339
2440i2c = board .STEMMA_I2C ()
2541msa = MSA311 (i2c )
3349
3450midi_usb_channel = 1
3551midi_usb = adafruit_midi .MIDI (midi_out = usb_midi .ports [1 ], out_channel = midi_usb_channel - 1 )
36- midi_serial_channel = 4
52+ midi_serial_channel = 1
3753midi_serial = adafruit_midi .MIDI (midi_out = midi_uart , out_channel = midi_serial_channel - 1 )
3854
3955octave = 4
@@ -59,104 +75,158 @@ def send_pc(bank, folder, patch):
5975 midi_usb .send (ProgramChange (patch ))
6076 midi_serial .send (ProgramChange (patch ))
6177
62- def send_bend (bend_val ):
63- midi_usb .send (PitchBend (bend_val ))
64- midi_serial .send (PitchBend (bend_val ))
78+ def send_bend (bend_start , bend_val , rate , bend_dir ):
79+ b = bend_start
80+ if bend_dir == 0 :
81+ while b > bend_val + rate :
82+ print (b )
83+ b = b - rate
84+ midi_usb .send (PitchBend (b ))
85+ midi_serial .send (PitchBend (b ))
86+ if bend_dir == 1 :
87+ while b < bend_val - rate :
88+ print (b )
89+ b = b + rate
90+ midi_usb .send (PitchBend (b ))
91+ midi_serial .send (PitchBend (b ))
6592
6693def send_midi_panic ():
6794 for x in range (128 ):
6895 midi_usb .send (NoteOff (x , 0 ))
6996 midi_serial .send (NoteOff (x , 0 ))
7097
7198# 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
99+ piano_keys = range (0 , 28 ) # 'range()' excludes last value, so add one
100+ patch_toes = list (range (28 , 33 ))
101+ cc_toes = list (range (35 , 40 ))
74102clef_button = 33
75103nose_button = 47
76104face_button = 34
77105record_button = 44
78106play_button = 45
107+ vol_down_button = 43
108+ vol_up_button = 42
109+ tempo_down_button = 41
110+ tempo_up_button = 40
79111
80112# patch assigments
81113patch_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
114+ (0 , 0 , 0 ), # bank 0, folder 0, patch 0
115+ (1 , 0 , 0 ),
116+ (1 , 0 , 1 ),
117+ (2 , 0 , 0 ),
118+ (3 , 0 , 0 ),
92119)
93120
121+ pb_max = 16383 # bend up value
122+ pb_default = 8192 # bend center value
123+ pb_min = 0 # bend down value
124+ pb_change_rate = 100 # interval for pitch bend, lower number is slower
125+ pb_return_rate = 100 # interval for pitch bend release
126+
94127# accelerometer filtering variables
95128slop = 0.2 # threshold for accelerometer send
96129filter_percent = 0.5 # ranges from 0.0 to 1.0
97130accel_data_y = msa .acceleration [1 ]
98131last_accel_data_y = msa .acceleration [1 ]
99132
100- cone_mode = False # mod wheel vs pitch bend
133+ # midi cc variables
134+ cc_enable = True
135+ cc_numbers = (1 , 43 , 44 , 14 , 15 ) # mod wheel, filter cutoff, resonance, user, user
136+ cc_current = 0
137+ cc_play = 16
138+ cc_face_number = 17
139+
101140started = False # state of arp/seq play
141+ note_hold = False
142+
143+ print ("Cyber Cat MIDI Keyboard" )
102144
103145
104146while 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 :
147+ if cc_enable :
148+ new_data_y = msa .acceleration [1 ]
149+ accel_data_y = ((new_data_y * filter_percent ) + (1 - filter_percent ) * accel_data_y ) # smooth
150+ if abs (accel_data_y - last_accel_data_y ) > slop :
113151 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
152+ send_cc (cc_numbers [cc_current ], modulation )
153+ last_accel_data_y = accel_data_y
117154
118155 event = key_matrix .events .get ()
119156 if event :
120157 if event .pressed :
121158 key = event .key_number
159+
122160 # Note keys
123- if key in piano_keys : # its one of the piano keys
161+ if key in piano_keys :
124162 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
163+
164+ # Volume buttons
165+ if key is vol_down_button :
166+ octave = min (max ((octave - 1 ), 0 ), 7 )
167+ if key is vol_up_button :
168+ octave = min (max ((octave + 1 ), 0 ), 7 )
169+
170+ # Tempo buttons
171+ if key is tempo_down_button :
172+ send_bend (pb_default , pb_min , pb_change_rate , 0 )
173+ if key is tempo_up_button :
174+ send_bend (pb_default , pb_max , pb_change_rate , 1 )
175+
176+ # Patch buttons (left cat toes)
177+ if key in patch_toes :
178+ pc_key = patch_toes .index (key ) # remove offset for patch list indexing
128179 send_pc (patch_list [pc_key ][0 ], patch_list [pc_key ][1 ], patch_list [pc_key ][2 ])
129- # Play key
180+
181+ # cc buttons (right cat toes)
182+ if key in cc_toes :
183+ cc_current = cc_toes .index (key ) # remove offset for cc list indexing
184+
185+ # Play key -- use MIDI learn to have arp/seq start or stop with this
130186 if key is play_button :
131187 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())
188+ send_cc (cc_play , 127 ) # map to seq/arp on/off Synth One, e.g.
135189 started = True
136190 else :
137- send_cc (16 , 0 )
138- # midi_usb.send(Stop())
191+ send_cc (cc_play , 0 )
139192 started = False
140- # Record key
193+
194+ # Record key -- enable icecream cone
141195 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
196+ if cc_enable is True :
197+ cc_enable = False
198+ ledpin .value = False
199+
200+ elif cc_enable is False :
201+ send_cc (cc_numbers [cc_current ], 0 ) # zero it
202+ cc_enable = True
203+ ledpin .value = True
148204
149205 # Clef
150- if key is clef_button : # octave down
151- octave = min (max ((octave - 1 ), 0 ), 7 )
206+ if key is clef_button : # hold
207+ note_hold = not note_hold
208+
152209 # Face
153- if key is face_button : # octave up
154- octave = min (max ((octave + 1 ), 0 ), 7 )
155- # STOP
210+ if key is face_button : # momentary cc
211+ send_cc (cc_face_number , 127 )
212+
213+ # Nose
156214 if key is nose_button :
157215 send_midi_panic () # all notes off
158216
159217 if event .released :
160218 key = event .key_number
161219 if key in piano_keys :
162- send_note_off (key , octave )
220+ if not note_hold :
221+ send_note_off (key , octave )
222+ if note_hold :
223+ pass
224+
225+ if key is face_button : # momentary cc release
226+ send_cc (cc_face_number , 0 )
227+
228+ if key is tempo_down_button :
229+ send_bend (pb_min , pb_default , pb_return_rate , 1 )
230+
231+ if key is tempo_up_button :
232+ send_bend (pb_max , pb_default , pb_return_rate , 0 )
0 commit comments