/ MIDI_CLUE_BLE_Glove / code.py
code.py
1 # SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 """ 6 CLUE BLE MIDI 7 Sends MIDI CC values based on accelerometer x & y and proximity sensor 8 Touch #0 switches Bank/Preset patches 9 Touch #1 picks among the three CC lines w A&B buttons adjusting CC numbers 10 Touch #2 starts/stops sending CC messages (still allows Program Change) 11 """ 12 import time 13 from adafruit_clue import clue 14 import adafruit_ble 15 from adafruit_ble.advertising.standard import ProvideServicesAdvertisement 16 import adafruit_ble_midi 17 import adafruit_midi 18 from adafruit_midi.control_change import ControlChange 19 from adafruit_midi.program_change import ProgramChange 20 21 # from adafruit_midi.note_on import NoteOn 22 # from adafruit_midi.pitch_bend import PitchBend 23 import simpleio 24 import displayio 25 import terminalio 26 from adafruit_display_text import label 27 from adafruit_display_shapes.rect import Rect 28 29 # --- Pick your midi out channel here --- 30 midi_channel = 1 31 # --- Pick your MIDI CC numbers here --- 32 cc_x_num = 71 33 cc_y_num = 72 34 cc_prox_num = 73 35 # --- Pick Bank & Preset pairs here --- 36 touch_patch = [ # first number is the Bank, second is the Preset 37 (4, 16), # minimoog: Leads > Original MINI 38 (5, 8), # Pads > Intergalactic Pass 39 (0, 13), # Bass > Kraft Bass 40 (6, 9), # Percussion > Space Hat 41 ] 42 43 patch_count = len(touch_patch) 44 patch_index = ( 45 patch_count - 1 46 ) # start on the last one so first time it is pressed it goes to first 47 48 cc_x = 0 49 cc_y = 0 50 cc_prox = 0 51 52 # Use default HID descriptor 53 midi_service = adafruit_ble_midi.MIDIService() 54 advertisement = ProvideServicesAdvertisement(midi_service) 55 56 ble = adafruit_ble.BLERadio() 57 if ble.connected: 58 for c in ble.connections: 59 c.disconnect() 60 61 midi = adafruit_midi.MIDI(midi_out=midi_service, out_channel=midi_channel - 1) 62 63 print("advertising") 64 ble.name = "CLUE BLE MIDI" 65 ble.start_advertising(advertisement) 66 67 clue.display.brightness = 1.0 68 clue.pixel.brightness = 0.2 69 screen = displayio.Group() 70 71 ORANGE = 0xCE6136 72 GRAY = 0x080808 73 BLACK = 0x121212 74 BLUE = 0x668190 75 SILVER = 0xAAAAAA 76 BROWN = 0x805D40 77 78 # --- Setup screen --- 79 # BG 80 color_bitmap = displayio.Bitmap(240, 240, 1) 81 color_palette = displayio.Palette(1) 82 color_palette[0] = GRAY 83 bg_sprite = displayio.TileGrid(color_bitmap, x=0, y=0, pixel_shader=color_palette) 84 screen.append(bg_sprite) 85 column_a = 20 86 column_b = 168 87 # positions that are distributed relative to cc_x and cc_prox y positions 88 row_a = 80 89 row_c = 170 90 row_b = int(row_a + ((row_c - row_a) / 2)) 91 line_row_a = int(row_a + ((row_b - row_a) / 2)) 92 line_row_b = int(row_b + ((row_c - row_b) / 2)) 93 picker_box_row = [row_a, row_b, row_c] 94 95 # trim 96 top_trim_box = Rect(0, 0, 240, 8, fill=BROWN, outline=None) 97 screen.append(top_trim_box) 98 bottom_trim_box = Rect(0, 232, 240, 8, fill=BROWN, outline=None) 99 screen.append(bottom_trim_box) 100 101 # title text 102 title_label = label.Label(terminalio.FONT, text="MIDI CLUE", scale=4, color=SILVER) 103 title_label.x = 14 104 title_label.y = 27 105 screen.append(title_label) 106 107 # title box 108 title_box = Rect(0, 54, 240, 8, fill=BROWN, outline=None) 109 screen.append(title_box) 110 111 # cc x num 112 cc_x_num_label = label.Label( 113 terminalio.FONT, 114 text=("CC {}".format(cc_x_num)), 115 scale=3, 116 color=ORANGE, 117 ) 118 cc_x_num_label.x = column_a 119 cc_x_num_label.y = row_a 120 screen.append(cc_x_num_label) 121 122 # cc x value 123 cc_x_label = label.Label(terminalio.FONT, text=str(cc_x), scale=3, color=ORANGE) 124 cc_x_label.x = column_b 125 cc_x_label.y = row_a 126 screen.append(cc_x_label) 127 128 # picker box 129 picker_box = Rect(3, row_a, 6, 6, fill=ORANGE, outline=None) 130 screen.append(picker_box) 131 132 # mid line 133 mid_line_a = Rect(0, line_row_a, 240, 2, fill=SILVER, outline=None) 134 screen.append(mid_line_a) 135 136 # cc y num 137 cc_y_num_label = label.Label( 138 terminalio.FONT, text=("CC {}".format(cc_y_num)), scale=3, color=BLUE 139 ) 140 cc_y_num_label.x = column_a 141 cc_y_num_label.y = row_b 142 screen.append(cc_y_num_label) 143 144 # cc y value text 145 cc_y_label = label.Label(terminalio.FONT, text=str(cc_y), scale=3, color=BLUE) 146 cc_y_label.x = column_b 147 cc_y_label.y = row_b 148 screen.append(cc_y_label) 149 150 # mid line 151 mid_line_b = Rect(0, line_row_b, 240, 2, fill=SILVER, outline=None) 152 screen.append(mid_line_b) 153 154 # cc prox num text 155 cc_prox_num_label = label.Label( 156 terminalio.FONT, 157 text=("CC {}".format(cc_prox_num)), 158 scale=3, 159 color=SILVER, 160 ) 161 cc_prox_num_label.x = column_a 162 cc_prox_num_label.y = row_c 163 screen.append(cc_prox_num_label) 164 165 # cc prox value text 166 cc_prox_label = label.Label(terminalio.FONT, text=str(cc_prox), scale=3, color=SILVER) 167 cc_prox_label.x = column_b 168 cc_prox_label.y = row_c 169 screen.append(cc_prox_label) 170 171 # footer line 172 footer_line = Rect(0, 192, 240, 2, fill=SILVER, outline=None) 173 screen.append(footer_line) 174 175 176 # patch label 177 patch_label = label.Label(terminalio.FONT, text="Patch _", scale=2, color=BLUE) 178 patch_label.x = 4 179 patch_label.y = 216 180 screen.append(patch_label) 181 182 # footer label 183 footer_label = label.Label(terminalio.FONT, text="connect BLE", scale=2, color=ORANGE) 184 footer_label.x = 102 185 footer_label.y = 216 186 screen.append(footer_label) 187 188 # show the screen 189 clue.display.show(screen) 190 191 cc_num_pick_toggle = 0 # which cc to adjust w buttons 192 cc_send_toggle = True # to start and stop sending cc 193 194 debug = False # set debug mode True to test raw values, set False to run BLE MIDI 195 196 while True: 197 if debug: 198 accel_data = clue.acceleration # get accelerometer reading 199 accel_x = accel_data[0] 200 accel_y = accel_data[1] 201 prox_data = clue.proximity 202 print("x:{} y:{}".format(accel_x, accel_y,)) 203 print("proximity: {}".format(clue.proximity)) 204 time.sleep(0.2) 205 206 else: 207 print("Waiting for connection") 208 while not ble.connected: 209 pass 210 print("Connected") 211 footer_label.x = 80 212 footer_label.color = BLUE 213 footer_label.text = "BLE Connected" 214 time.sleep(2) 215 footer_label.x = 110 216 footer_label.color = SILVER 217 footer_label.text = "sending CC" 218 219 while ble.connected: 220 # Clue sensor readings to CC 221 accel_data = clue.acceleration # get accelerometer reading 222 accel_x = accel_data[0] 223 accel_y = accel_data[1] 224 prox_data = clue.proximity 225 226 # Remap analog readings to cc range 227 cc_x = int(simpleio.map_range(accel_x, -9, 9, 0, 127)) 228 cc_y = int(simpleio.map_range(accel_y, 0, 9, 0, 127)) 229 cc_prox = int(simpleio.map_range(prox_data, 0, 255, 0, 127)) 230 231 # send all the midi messages in a list 232 if cc_send_toggle: 233 midi.send( 234 [ 235 ControlChange(cc_x_num, cc_x), 236 ControlChange(cc_y_num, cc_y), 237 ControlChange(cc_prox_num, cc_prox), 238 ] 239 ) 240 cc_x_label.text = str(cc_x) 241 cc_y_label.text = str(cc_y) 242 cc_prox_label.text = str(cc_prox) 243 244 # If you want to send NoteOn or Pitch Bend, here are examples: 245 # midi.send(NoteOn(44, 1column_a)) # G sharp 2nd octave 246 # a_pitch_bend = PitchBend(random.randint(0, 16383)) 247 # midi.send(a_pitch_bend) 248 249 if clue.button_a: 250 if cc_num_pick_toggle == 0: 251 cc_x_num = cc_x_num - 1 252 cc_x_num_label.text = "CC {}".format(cc_x_num) 253 time.sleep(0.05) # Debounce 254 elif cc_num_pick_toggle == 1: 255 cc_y_num = cc_y_num - 1 256 cc_y_num_label.text = "CC {}".format(cc_y_num) 257 time.sleep(0.05) 258 else: 259 cc_prox_num = cc_prox_num - 1 260 cc_prox_num_label.text = "CC {}".format(cc_prox_num) 261 time.sleep(0.05) 262 263 if clue.button_b: 264 if cc_num_pick_toggle == 0: 265 cc_x_num = cc_x_num + 1 266 cc_x_num_label.text = "CC {}".format(cc_x_num) 267 time.sleep(0.05) 268 elif cc_num_pick_toggle == 1: 269 cc_y_num = cc_y_num + 1 270 cc_y_num_label.text = "CC {}".format(cc_y_num) 271 time.sleep(0.05) 272 else: 273 cc_prox_num = cc_prox_num + 1 274 cc_prox_num_label.text = "CC {}".format(cc_prox_num) 275 time.sleep(0.05) 276 277 if clue.touch_0: 278 patch_index = (patch_index + 1) % patch_count 279 midi.send( # Bank select 280 [ 281 ControlChange(0, 0), # MSB 282 ControlChange(32, touch_patch[patch_index][0]), # LSB 283 ] 284 ) 285 midi.send(ProgramChange(touch_patch[patch_index][1])) # Program Change 286 patch_label.text = "Patch {}".format(patch_index + 1) 287 time.sleep(0.2) 288 289 if clue.touch_1: 290 cc_num_pick_toggle = (cc_num_pick_toggle + 1) % 3 291 picker_box.y = picker_box_row[cc_num_pick_toggle] 292 time.sleep(0.1) 293 294 if clue.touch_2: 295 cc_send_toggle = not cc_send_toggle 296 if cc_send_toggle: 297 footer_label.x = 110 298 footer_label.color = SILVER 299 footer_label.text = "sending CC" 300 else: 301 footer_label.x = 114 302 footer_label.color = ORANGE 303 footer_label.text = "CC paused" 304 time.sleep(0.1) 305 306 print("Disconnected") 307 print() 308 ble.start_advertising(advertisement)