115 lines
4.0 KiB
Python
115 lines
4.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import rtmidi
|
|
import random
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class Midi:
|
|
InvalidType = 0x00 # For notifying errors
|
|
NoteOff = 0x80 # Note Off
|
|
NoteOn = 0x90 # Note On
|
|
AfterTouchPoly = 0xA0 # Polyphonic AfterTouch
|
|
ControlChange = 0xB0 # Control Change / Channel Mode
|
|
ProgramChange = 0xC0 # Program Change
|
|
AfterTouchChannel = 0xD0 # Channel (monophonic) AfterTouch
|
|
PitchBend = 0xE0 # Pitch Bend
|
|
SystemExclusive = 0xF0 # System Exclusive
|
|
TimeCodeQuarterFrame = 0xF1 # System Common - MIDI Time Code Quarter Frame
|
|
SongPosition = 0xF2 # System Common - Song Position Pointer
|
|
SongSelect = 0xF3 # System Common - Song Select
|
|
TuneRequest = 0xF6 # System Common - Tune Request
|
|
Clock = 0xF8 # System Real Time - Timing Clock
|
|
Start = 0xFA # System Real Time - Start
|
|
Continue = 0xFB # System Real Time - Continue
|
|
Stop = 0xFC # System Real Time - Stop
|
|
ActiveSensing = 0xFE # System Real Time - Active Sensing
|
|
SystemReset = 0xFF # System Real Time - System Reset
|
|
|
|
@staticmethod
|
|
def getChannel(statusByte):
|
|
return statusByte & 0x0f;
|
|
|
|
@staticmethod
|
|
def getType(statusByte):
|
|
if statusByte >= 0xf0:
|
|
# System messages
|
|
return statusByte
|
|
else:
|
|
# Channel messages
|
|
return statusByte & 0xf0;
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class MidiInterface:
|
|
def __init__(self, listenerCallback = None):
|
|
self.input = rtmidi.MidiIn()
|
|
self.output = rtmidi.MidiOut()
|
|
self.listenerCallback = listenerCallback
|
|
self.ports = self.getAvailablePorts()
|
|
self.port = self.connect(self.choosePorts())
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
def handleMidiInput(self, message, timestamp):
|
|
midiData = message[0]
|
|
if self.listenerCallback:
|
|
self.listenerCallback(midiData)
|
|
|
|
def send(self, message):
|
|
print('Sending', message)
|
|
self.output.send_message(message)
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
def getAvailablePorts(self):
|
|
return {
|
|
'input' : self.input.get_ports(),
|
|
'output': self.output.get_ports(),
|
|
}
|
|
|
|
def choosePorts(self):
|
|
return {
|
|
'input' : self.choosePort(self.ports['input'], 'input'),
|
|
'output': self.choosePort(self.ports['output'], 'output')
|
|
}
|
|
|
|
def choosePort(self, ports, direction):
|
|
if not ports:
|
|
print('No MIDI ports available, bailing out.')
|
|
return None
|
|
|
|
if len(ports) == 1:
|
|
return {
|
|
'id': 0,
|
|
'name': ports[0]
|
|
}
|
|
|
|
else:
|
|
# Give a choice
|
|
print('Multiple %s ports available, please make a choice:' % direction)
|
|
choices = dict()
|
|
for port, i in zip(ports, range(0, len(ports))):
|
|
choices[i] = port
|
|
print(' [%d]' % i, port)
|
|
choiceIndex = int(input('-> '))
|
|
return {
|
|
'id': choiceIndex,
|
|
'name': choices[choiceIndex]
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
def connect(self, ports):
|
|
if not ports:
|
|
return None
|
|
|
|
print('Connecting input to %s' % ports['input']['name'])
|
|
print('Connecting output to %s' % ports['output']['name'])
|
|
|
|
self.input.set_callback(self.handleMidiInput)
|
|
self.input.open_port(ports['input']['id'])
|
|
self.output.open_port(ports['output']['id'])
|
|
return ports
|