MIDI extensions (WIN32, Mac)?

Graham Breed graham at nospam.microtonal.co.uk
Thu Feb 15 00:25:00 CET 2001


Below is some code that actually works.  I don't think it's fully 
compliant with the MIDI standard, but you can check that.  There's no 
obvious problem with speed.

Note: do not use input.read() without an argument.  It will block 
indefinitely.

I have written code in C to use the MIDI ports in Windows.  I don't think 
it can be done directly in Python, but I would be interested in working on 
a C module.

I have no idea about Macintosh.  Hopefully, OSX will behave like Linux in 
this respect.


import string

#messages
noteOff = 0x80
noteOn = 0x90
polyAftertouch = 0xA0
controlChange = 0xB0
programChange = 0xC0
monoAftertouch = 0xD0
pitchBend = 0xE0
beginSysEx = 0xF0
songPositionPointer = 0xF2
songSelect = 0xF3
tuneRequest = 0xF6
endSysEx = 0xF7
timingClock = 0xF8
start = 0xFA
cont = 0xFB
stop = 0xFC
activeSensing = 0xFE
systemReset = 0xFF

messagesWithNotes = (noteOn, noteOff, polyAftertouch)

messageNames = {
    noteOff: 'note off',
    noteOn: 'note on',
    polyAftertouch: 'poly aftertouch',
    controlChange: 'control change',
    programChange: 'program change',
    monoAftertouch: 'mono aftertouch',
    pitchBend: 'pitch bend',
    beginSysEx: 'begin SysEx',
    endSysEx: 'end SysEx',
    songPositionPointer: 'song position pointer',
    songSelect: 'song select',
    tuneRequest: 'tune request',
    endSysEx: 'end SysEx',
    timingClock: 'timing clock',
    start: 'start',
    cont: 'continue',
    stop: 'stop',
    activeSensing: 'active sensing',
    systemReset: 'system reset'}

nDataBytes = {
  noteOff: 2,
  noteOn: 2,
  polyAftertouch: 2,
  controlChange: 2,
  programChange: 1,
  monoAftertouch: 1,
  pitchBend: 2}

noteNames = ('C','C#','D','Eb','E','F','F#',
    'G','GA','A','Bb','B')

def isData(byte):
  return byte<0x80;

def isChannelMessage(byte):
  return 0x80<=byte<0xF0;

def formatNote(noteNum):
  return noteNames[noteNum%12] + str(noteNum/12 - 1)

def sendMessage(output, message):
  output.write(string.join(map(chr, message),''))
  try:
    output.flush()
  except:
    print "couldn't flush"
  


midiIn = open('/dev/midi00', 'r')
midiOut = open('/dev/midi1','w')

messageNumber = ''
data = []
dataLength = 0
channel=-1
while 1:
  inputData = midiIn.read(1)
  if len(inputData)>1 :
    print 'read to much '+map(hex, map(ord, inputData))
  else:
    datum = ord(inputData)
    if isData(datum):
      data.append(datum)
      if len(data)==dataLength:
        print messageNames[messageNumber], 'channel', channel, 
        if messageNumber in messagesWithNotes:
          print formatNote(data[0]), data[1]
        else:
          print string.join(map(hex, data))
        sendMessage(midiOut, [messageNumber+channel]+data)
        data = []
    elif isChannelMessage(datum):
      channel = datum%16
      messageNumber = datum-channel
      dataLength = nDataBytes[messageNumber]
      if data:
        print 'residual data '+string.join(map(hex, data))
        data = []
    elif datum==beginSysEx:
      messageNumber=beginSysEx
      dataLength = -1
      data = [] # should this be different to the normal data?
    elif datum==endSysEx:
      if messageNumber != beginSysEx:
        print 'end with no beginning'
      formattedData = []
      for byte in data:
        formattedData.append(hex(byte)[-2:])
      print 'SysEx', string.replace(string.join(
          formattedData,''),'x','0')
      sendMessage(midiOut, [beginSysEx]+data+[endSysEx])
      data = []
    else:
      print messageNames.get(datum, 'undefined')
      sendMessage(midiOut, datum)
      



More information about the Python-list mailing list