From ab9a7eec7418be881d31fd8b2bfef02315d85db8 Mon Sep 17 00:00:00 2001 From: BlitzCityDIY Date: Tue, 22 Feb 2022 14:05:50 -0500 Subject: [PATCH] Adding code for MIDI for Makers guide Adding six examples for the MIDI for Makers guide. Three MIDI out (basic keyboard, BLE sequencer, CC pots) and three MIDI in (UART to USB, display MIDI messages and control motors). --- MIDI_for_Makers/BLE_MIDI_Sequencer/code.py | 96 +++++++++++++++++++ MIDI_for_Makers/Basic_MIDI_Keyboard/code.py | 55 +++++++++++ .../Control_Motors_with_MIDI/code.py | 35 +++++++ .../MIDI_CC_Control_with_Pots/code.py | 82 ++++++++++++++++ .../code.py | 47 +++++++++ .../Receive_and_Display_MIDI_Messages/code.py | 76 +++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 MIDI_for_Makers/BLE_MIDI_Sequencer/code.py create mode 100644 MIDI_for_Makers/Basic_MIDI_Keyboard/code.py create mode 100644 MIDI_for_Makers/Control_Motors_with_MIDI/code.py create mode 100644 MIDI_for_Makers/MIDI_CC_Control_with_Pots/code.py create mode 100644 MIDI_for_Makers/Receive_MIDI_Over_UART_and_Send_Over_USB/code.py create mode 100644 MIDI_for_Makers/Receive_and_Display_MIDI_Messages/code.py diff --git a/MIDI_for_Makers/BLE_MIDI_Sequencer/code.py b/MIDI_for_Makers/BLE_MIDI_Sequencer/code.py new file mode 100644 index 000000000..7998b4ca3 --- /dev/null +++ b/MIDI_for_Makers/BLE_MIDI_Sequencer/code.py @@ -0,0 +1,96 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time +import adafruit_ble +import touchio +import board +import adafruit_midi +import adafruit_ble_midi +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_midi.note_on import NoteOn +from adafruit_midi.note_off import NoteOff + +# CLUE cap touch setup +c_touch = touchio.TouchIn(board.D0) +f_touch = touchio.TouchIn(board.D1) +g_touch = touchio.TouchIn(board.D2) + +# array of touch pads +pads = [c_touch, f_touch, g_touch] + +# BLE MIDI setup +midi_service = adafruit_ble_midi.MIDIService() +advertisement = ProvideServicesAdvertisement(midi_service) + +ble = adafruit_ble.BLERadio() +if ble.connected: + for c in ble.connections: + c.disconnect() + +# midi setup +midi = adafruit_midi.MIDI(midi_out=midi_service, out_channel=0) + +print("advertising") +ble.start_advertising(advertisement) + +# MIDI note numbers for C, F and G major triads +c_triad = (60, 64, 67) +f_triad = (65, 69, 72) +g_triad = (67, 71, 74) + +# array of triads +triads = [c_triad, f_triad, g_triad] + +# touch debounce states +c_pressed = False +f_pressed = False +g_pressed = False + +# array of debounce states +triad_states = [c_pressed, f_pressed, g_pressed] + +# beginning triad +active_triad = c_triad +# variable for triad index +z = 0 + +while True: + # BLE connection + print("Waiting for connection") + while not ble.connected: + pass + print("Connected") + time.sleep(1.0) + + # while BLE is connected... + while ble.connected: + # iterate through the touch inputs + for i in range(3): + inputs = pads[i] + # if a touch input is detected... + if inputs.value and triad_states[i] is False: + # debounce state activated + triad_states[i] = True + # update triad + active_triad = triads[i] + print(active_triad) + # after touch input... + if not inputs.value and triad_states[i] is True: + # reset debounce state + triad_states[i] = False + # send triad arpeggios out with half second delay + midi.send(NoteOn(active_triad[z])) + time.sleep(0.5) + midi.send(NoteOff(active_triad[z])) + time.sleep(0.5) + # increase index by 1 + z += 1 + # reset index at end of triad + if z > 2: + z = 0 + + # BLE connection + print("Disconnected") + print() + ble.start_advertising(advertisement) diff --git a/MIDI_for_Makers/Basic_MIDI_Keyboard/code.py b/MIDI_for_Makers/Basic_MIDI_Keyboard/code.py new file mode 100644 index 000000000..83a155aab --- /dev/null +++ b/MIDI_for_Makers/Basic_MIDI_Keyboard/code.py @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import digitalio +import usb_midi +import adafruit_midi +from adafruit_midi.note_on import NoteOn +from adafruit_midi.note_off import NoteOff + +# midi setup +midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0) + +# midi note numbers +midi_notes = [60, 64, 67, 72] + +# digital pins for the buttons +key_pins = [board.D5, board.D6, board.D9, board.D10] + +# array for buttons +keys = [] + +# setup buttons as inputs +for key in key_pins: + key_pin = digitalio.DigitalInOut(key) + key_pin.direction = digitalio.Direction.INPUT + key_pin.pull = digitalio.Pull.UP + keys.append(key_pin) + +# states for buttons +key0_pressed = False +key1_pressed = False +key2_pressed = False +key3_pressed = False + +# array for button states +key_states = [key0_pressed, key1_pressed, key2_pressed, key3_pressed] + +while True: + + # iterate through 4 buttons + for i in range(4): + inputs = keys[i] + # if button is pressed... + if not inputs.value and key_states[i] is False: + # update button state + key_states[i] = True + # send NoteOn for corresponding MIDI note + midi.send(NoteOn(midi_notes[i], 120)) + + # if the button is released... + if inputs.value and key_states[i] is True: + # send NoteOff for corresponding MIDI note + midi.send(NoteOff(midi_notes[i], 120)) + key_states[i] = False diff --git a/MIDI_for_Makers/Control_Motors_with_MIDI/code.py b/MIDI_for_Makers/Control_Motors_with_MIDI/code.py new file mode 100644 index 000000000..18046f14a --- /dev/null +++ b/MIDI_for_Makers/Control_Motors_with_MIDI/code.py @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import pwmio +import usb_midi +import adafruit_midi +from adafruit_midi.note_off import NoteOff +from adafruit_midi.note_on import NoteOn +from adafruit_motor import servo + +# pwm setup for servo +pwm = pwmio.PWMOut(board.D2, duty_cycle=2 ** 15, frequency=50) + +# servo setup +motor = servo.Servo(pwm) + +# midi setup +midi = adafruit_midi.MIDI( + midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0 +) + +while True: + # receive midi input + msg = midi.receive() + + if msg is not None: + # if a NoteOn message is received... + if isinstance(msg, NoteOn): + # servo set to 180 degrees + motor.angle = 180 + # if a NoteOff message is received... + if isinstance(msg, NoteOff): + # servo set to 0 degrees + motor.angle = 0 diff --git a/MIDI_for_Makers/MIDI_CC_Control_with_Pots/code.py b/MIDI_for_Makers/MIDI_CC_Control_with_Pots/code.py new file mode 100644 index 000000000..a33bcc567 --- /dev/null +++ b/MIDI_for_Makers/MIDI_CC_Control_with_Pots/code.py @@ -0,0 +1,82 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import usb_midi +import adafruit_midi +import simpleio +from analogio import AnalogIn +from adafruit_midi.control_change import ControlChange +from adafruit_midi.pitch_bend import PitchBend + +# midi setup +midi = adafruit_midi.MIDI( + midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0 +) + +# potentiometer setup +mod_pot = AnalogIn(board.A0) +pitchDown_pot = AnalogIn(board.A1) +pitchUp_pot = AnalogIn(board.A2) +sus_pot = AnalogIn(board.A3) + +# function to read analog input +def val(pin): + return pin.value + +# variables for last read value +# defaults to 0 +# no pitchbend is 8192 +mod_val2 = 0 +pitchDown_val2 = 8192 +pitchUp_val2 = 8192 +sus_val2 = 0 + +while True: + + # map range of analog input to midi values + # pitchbend range is 0 to 16383 with 8192 centered or no pitchbend + mod_val1 = round(simpleio.map_range(val(mod_pot), 0, 65535, 0, 127)) + pitchDown_val1 = round(simpleio.map_range(val(pitchDown_pot), 0, 65535, 0, 8192)) + pitchUp_val1 = round(simpleio.map_range(val(pitchUp_pot), 0, 65535, 8192, 16383)) + sus_val1 = round(simpleio.map_range(val(sus_pot), 0, 65535, 0, 127)) + + # if modulation value is updated... + if abs(mod_val1 - mod_val2) > 2: + # update mod_val2 + mod_val2 = mod_val1 + # create integer + modulation = int(mod_val2) + # create CC message + modWheel = ControlChange(1, modulation) + # send CC message + midi.send(modWheel) + + # pitchbend down value is updated... + if abs(pitchDown_val1 - pitchDown_val2) > 75: + # update pitchDown_val2 + pitchDown_val2 = pitchDown_val1 + # create PitchBend message + pitchDown = PitchBend(int(pitchDown_val2)) + # send PitchBend message + midi.send(pitchDown) + + # pitchbend up value is updated... + if abs(pitchUp_val1 - pitchUp_val2) > 75: + # updated pitchUp_val2 + pitchUp_val2 = pitchUp_val1 + # create PitchBend message + pitchUp = PitchBend(int(pitchUp_val2)) + # send PitchBend message + midi.send(pitchUp) + + # sustain value is updated... + if abs(sus_val1 - sus_val2) > 2: + # update sus_val2 + sus_val2 = sus_val1 + # create integer + sustain = int(sus_val2) + # create CC message + sustainPedal = ControlChange(64, sustain) + # send CC message + midi.send(sustainPedal) diff --git a/MIDI_for_Makers/Receive_MIDI_Over_UART_and_Send_Over_USB/code.py b/MIDI_for_Makers/Receive_MIDI_Over_UART_and_Send_Over_USB/code.py new file mode 100644 index 000000000..0b27830ec --- /dev/null +++ b/MIDI_for_Makers/Receive_MIDI_Over_UART_and_Send_Over_USB/code.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import busio +import adafruit_midi +import usb_midi +from adafruit_midi.control_change import ControlChange +from adafruit_midi.pitch_bend import PitchBend +from adafruit_midi.note_off import NoteOff +from adafruit_midi.note_on import NoteOn + +# uart setup +uart = busio.UART(board.TX, board.RX, baudrate=31250) +# midi channel setup +midi_in_channel = 1 +midi_out_channel = 1 +# midi setup +# UART is setup as the input +# USB is setup as the output +midi = adafruit_midi.MIDI( + midi_in=uart, + midi_out=usb_midi.ports[1], + in_channel=(midi_in_channel - 1), + out_channel=(midi_out_channel - 1), + debug=False, +) + +print("MIDI UART In/USB Out") +print("Default output channel:", midi.out_channel + 1) + +# array of message types +messages = (NoteOn, NoteOff, PitchBend, ControlChange) + +while True: + # receive MIDI input from UART + msg = midi.receive() + + # if the input is a recognized message... + if msg is not None: + for i in range(0, 3): + # iterate through message types + # makes it so that you aren't sending any unnecessary messages + if isinstance(msg, messages[i]): + # send the input out via USB + midi.send(msg) + print(msg) diff --git a/MIDI_for_Makers/Receive_and_Display_MIDI_Messages/code.py b/MIDI_for_Makers/Receive_and_Display_MIDI_Messages/code.py new file mode 100644 index 000000000..03c6ab764 --- /dev/null +++ b/MIDI_for_Makers/Receive_and_Display_MIDI_Messages/code.py @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import busio +import usb_midi +import adafruit_midi +import displayio +import terminalio +from adafruit_display_text import label +import adafruit_displayio_ssd1306 +from adafruit_midi.control_change import ControlChange +from adafruit_midi.note_off import NoteOff +from adafruit_midi.note_on import NoteOn +from adafruit_midi.pitch_bend import PitchBend + +displayio.release_displays() + +oled_reset = board.D1 + +# I2C setup for display +i2c = busio.I2C(board.SCL1, board.SDA1) +display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=oled_reset) + +# midi setup +print(usb_midi.ports) +midi = adafruit_midi.MIDI( + midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0 +) + +msg = midi.receive() + +# display width and height setup +WIDTH = 128 +HEIGHT = 32 +BORDER = 5 + +# display setup +display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT) + +splash = displayio.Group() +display.show(splash) + +# text area setup +text = "MIDI Messages" +text_area = label.Label( + terminalio.FONT, text=text, color=0xFFFFFF, x=28, y=HEIGHT // 2+1) +splash.append(text_area) + +while True: + # receive midi messages + msg = midi.receive() + + if msg is not None: + # if a NoteOn message... + if isinstance(msg, NoteOn): + string_msg = 'NoteOn' + # get note number + string_val = str(msg.note) + # if a NoteOff message... + if isinstance(msg, NoteOff): + string_msg = 'NoteOff' + # get note number + string_val = str(msg.note) + # if a PitchBend message... + if isinstance(msg, PitchBend): + string_msg = 'PitchBend' + # get value of pitchbend + string_val = str(msg.pitch_bend) + # if a CC message... + if isinstance(msg, ControlChange): + string_msg = 'ControlChange' + # get CC message number + string_val = str(msg.control) + # update text area with message type and value of message as strings + text_area.text = (string_msg + " " + string_val)