import busio import microcontroller import time import random import _gbio import adafruit_midi # TimingClock is worth importing first if present as it # will make parsing more efficient for this high frequency event # Only importing what is used will save a little bit of memory from adafruit_midi.timing_clock import TimingClock # from adafruit_midi.channel_pressure import ChannelPressure 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_change import PitchBendChange # from adafruit_midi.polyphonic_key_pressure import PolyphonicKeyPressure # from adafruit_midi.program_change import ProgramChange # from adafruit_midi.start import Start # from adafruit_midi.stop import Stop # from adafruit_midi.system_exclusive import SystemExclusive from adafruit_midi.midi_message import MIDIUnknownEvent _gbio.reset_gameboy() midi_out = busio.UART(microcontroller.pin.PB31, None, baudrate=31250) midi_in = busio.UART(None, microcontroller.pin.PA00, baudrate=31250, timeout=0) # for i in range(10): # midi_out.write(str(i).encode("utf-8")) # print(midi_in.read()) # time.sleep(0.5) # midi_inoutdemo - demonstrates receiving and sending MIDI events midi = adafruit_midi.MIDI(midi_in=midi_in, midi_out=midi_out, in_channel=(0,1,2,3), out_channel=0) print("Midi Demo in and out") # Convert channel numbers at the presentation layer to the ones musicians use print("Default output channel:", midi.out_channel + 1) print("Listening on input channels:", tuple([c + 1 for c in midi.in_channel])) import array pitch_bits = array.array("B", [0]) * (128 * 2) for i in range(128): f = 440 * 2 ** ((i-60+16) / 12) gb = 2048 - 131072 / f gb = max(0, int(round(gb))) pitch_bits[i * 2] = gb >> 8 pitch_bits[i * 2 + 1] = gb & 0xff on = ( b"\x00\x0e\x13" # Load 0x13 into C Increment fails for some reason #b"\x0c" # increment C b"\x3e\x83" # Load 0x83 into A b"\xe2" # Loads A into ff00 + C (0x13) b"\x0c" # increment C b"\x3e\x87" # Load 0x87 into A b"\xe2") # Loads A into ff00 + C (0x14) current_note_bits = [0, 0] def note_on(midi_note, *, voice=0): instructions = bytearray(on) # volume is slow to respond per note # instructions[7] = 0x03 | volume << 4 if voice == 1: instructions[2] = 0x18 instructions[4] = pitch_bits[midi_note * 2 + 1] instructions[8] = 0x80 | pitch_bits[midi_note * 2] current_note_bits[voice] = pitch_bits[midi_note * 2] gbio.queue_commands(instructions) def note_off(midi_note, *, voice=0): instructions = bytearray(b"\x00\x0e\x12" # Load 0x13 into C Increment fails for some reason b"\x3e\x00" # Load 0x83 into A b"\xe2") # Loads A into ff00 + C (0x13) if voice == 1: instructions[2] = 0x19 #instructions[4] |= current_note_bits[voice] #current_note_bits[voice] = 0 gbio.queue_commands(instructions) voice_settings = ( b"\x00\x0e\x11" # Load 0x11 into C Increment fails for some reason b"\x3e\x5f" # Load 0x3f into A b"\xe2" # Loads A into ff00 + C (0x16) b"\x0c" # increment C b"\x3e\x00" # Load 0x87 into A b"\xe2" # Loads A into ff00 + C (0x17) # Voice 2 b"\x0e\x16" # Load 0x16 into C Increment fails for some reason b"\x3e\x5f" # Load 0x3f into A b"\xe2" # Loads A into ff00 + C (0x16) b"\x0c" # increment C b"\x3e\xf0" # Load 0x87 into A b"\xe2") # Loads A into ff00 + C (0x17) _gbio.queue_commands(voice_settings) voice_state = [0, 0] while True: message = midi.receive() # non-blocking read if not message: continue print(message) # For a Note On or Note Off play a major chord # For any other known event just forward it if isinstance(msg_in, NoteOn) and msg_in.velocity != 0: # print("On", msg_in.note, # "from channel", channel_in + 1) if 0 in voice_state: free_voice = voice_state.index(0) note_on(msg_in.note, voice=free_voice) voice_state[free_voice] = msg_in.note else: # Busy, pass it on. midi.send(msg_in) elif (isinstance(msg_in, NoteOff) or isinstance(msg_in, NoteOn) and msg_in.velocity == 0): # print("Off", msg_in.note, # "from channel", channel_in + 1) if msg_in.note in voice_state: active_voice = voice_state.index(msg_in.note) note_off(msg_in.note, voice=active_voice) voice_state[active_voice] = 0 else: # We're not playing it, pass it on. midi.send(msg_in) # print(voice_state) elif isinstance(msg_in, MIDIUnknownEvent): # Message are only known if they are imported print("Unknown MIDI event status", msg_in.status) elif isinstance(msg_in, ControlChange): # Message are only known if they are imported print("Control change", msg_in.control, msg_in.value) elif isinstance(msg_in, TimingClock): pass elif msg_in is not None: print(msg_in)