picadae calibration programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MidiDevice.py 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. ##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org>
  2. ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. import rtmidi
  4. import rtmidi.midiutil
  5. from result import Result
  6. class MidiDevice(object):
  7. def __init__(self, **kwargs ):
  8. self.mip = None
  9. self.mop = None
  10. self.inMonitorFl = False
  11. self.outMonitorFl = False
  12. self.throughFl = False
  13. self.inPortLabel = None
  14. self.outPortLabel = None
  15. self.setup(**kwargs)
  16. def setup( self, **kwargs ):
  17. res = Result()
  18. if kwargs is None:
  19. return res
  20. if 'inPortLabel' in kwargs:
  21. res += self.select_port( True, kwargs['inPortLabel'] )
  22. if 'outPortLabel' in kwargs:
  23. res += self.select_port( False, kwargs['outPortLabel'] )
  24. if 'inMonitorFl' in kwargs:
  25. self.enable_monitor( True, kwargs['inMonitorFl'] )
  26. if 'outMonitorFl' in kwargs:
  27. self.enable_monitor( True, kwargs['outMonitorFl'] )
  28. if 'throughFl' in kwargs:
  29. self.enable_through( kwargs['throughFl'] )
  30. return res
  31. def _clean_port_label( self, portLabel ):
  32. return ' '.join(portLabel.split(' ')[:-1])
  33. def _get_port_list( self, inDirFl ):
  34. dev = rtmidi.MidiIn() if inDirFl else rtmidi.MidiOut()
  35. # get port list and drop the numeric id at the end of the port label
  36. return [ self._clean_port_label(p) for p in dev.get_ports() ]
  37. def get_port_list( self, inDirFl ):
  38. return { 'type':'midi',
  39. 'dir': 'in' if inDirFl else 'out',
  40. 'op': 'list',
  41. 'listL': self._get_port_list( inDirFl )
  42. }
  43. def get_in_port_list( self ):
  44. return self.get_port_list( True )
  45. def get_out_port_list( self ):
  46. return self.get_port_list( False )
  47. def select_port( self, inDirFl, portLabel ):
  48. res = Result()
  49. if portLabel:
  50. dirLabel = "input" if inDirFl else "output"
  51. portL = self._get_port_list( inDirFl )
  52. if portLabel not in portL:
  53. res.set_error("The port '%s' is not an available %s port." % (portLabel,dirLabel))
  54. else:
  55. port_idx = portL.index(portLabel) # TODO error check
  56. if inDirFl:
  57. self.mip,self.inPortLabel = rtmidi.midiutil.open_midiinput(port=port_idx)
  58. self.inPortLabel = self._clean_port_label(self.inPortLabel)
  59. else:
  60. self.mop,self.outPortLabel = rtmidi.midiutil.open_midioutput(port=port_idx)
  61. self.outPortLabel = self._clean_port_label(self.outPortLabel)
  62. return res
  63. def select_in_port( self, portLabel ):
  64. return self.select_port( True, portLabel )
  65. def select_out_port( self, portLabel ):
  66. return self.select_port( False, portLabel )
  67. def enable_through( self, throughFl ):
  68. self.throughFl = throughFl
  69. def enable_monitor( self, inDirFl, monitorFl ):
  70. if inDirFl:
  71. self.inMonitorFl = monitorFl
  72. else:
  73. self.outMonitorFl = monitorFl
  74. def enable_in_monitor( self, monitorFl):
  75. self.enable_monitor( True, monitorFl )
  76. def enable_out_monitor( self, monitorFl):
  77. self.enable_monitor( False, monitorFl )
  78. def port_name( self, inDirFl ):
  79. return inPortLabel if inDirFl else outPortLabel
  80. def in_port_name( self ):
  81. return self.port_name(True)
  82. def out_port_name( self ):
  83. return self.port_name(False)
  84. def _midi_data_to_text_msg( self, inFl, midi_data ):
  85. text = ""
  86. if len(midi_data) > 0:
  87. text += "{:02x}".format(midi_data[0])
  88. if len(midi_data) > 1:
  89. text += " {:3d}".format(midi_data[1])
  90. if len(midi_data) > 2:
  91. text += " {:3d}".format(midi_data[2])
  92. text = ("in: " if inFl else "out: ") + text
  93. return { 'type':'midi', 'dir':inFl, 'op':'monitor', 'value':text }
  94. def get_input( self ):
  95. o_msgL = []
  96. if self.mip is not None:
  97. while True:
  98. midi_msg = self.mip.get_message()
  99. if not midi_msg or not midi_msg[0]:
  100. break;
  101. if self.inMonitorFl:
  102. o_msgL.append( self._midi_data_to_text_msg(True,midi_msg[0]) )
  103. if self.throughFl and self.mop is not None:
  104. self.mop.send_message(midi_msg[0])
  105. o_msgL.append( { 'type':'midi', 'op':'data', 'dir':'in', 'value':midi_msg[0] } )
  106. return o_msgL
  107. def send_output( self, m ):
  108. o_msgL = []
  109. if self.mop is not None:
  110. self.mop.send_message(m)
  111. if self.outMonitorFl:
  112. o_msgL += [self._midi_data_to_text_msg( False, m )]
  113. return o_msgL
  114. def send_note_on( self, pitch, vel, ch=0 ):
  115. return self.send_output( [ 0x90+ch, pitch, vel ] )
  116. def send_note_off( self, pitch, ch=0 ):
  117. return self.send_note_on( 0, ch )
  118. def send_pgm_change( self, pgm, ch=0 ):
  119. return self.send_output( [ 0xc0+ch, pgm ] )
  120. def send_pbend( self, val, ch=0 ):
  121. assert( val < 8192 )
  122. ival = int(val)
  123. lsb = ival & 0x7f
  124. msb = (ival >> 7) & 0x7f
  125. return self.send_output( [ 0xe0+ch, lsb, msb ] )
  126. def send_controller( self, num, value, ch=0 ):
  127. return self.send_output( [0xb0+ch, num, value ] )
  128. def send_all_notes_off(self, ch=0 ):
  129. return self.send_controller( 123, 1, ch=ch )
  130. def get_state( self ):
  131. return {
  132. "inMonitorFl":self.inMonitorFl,
  133. "outMonitorFl":self.outMonitorFl,
  134. "throughFl":self.throughFl,
  135. "inPortLabel":self.inPortLabel,
  136. "outPortLabel":self.outPortLabel,
  137. "inPortL":self.get_in_port_list(),
  138. "outPortL":self.get_out_port_list()
  139. }
  140. def on_command( self, m, ms ):
  141. errL = []
  142. if m.type == 'midi':
  143. if m.op == 'sel':
  144. errL.append(self.select_port( m.dir=='in', m.value ))
  145. elif m.op == 'through':
  146. self.enable_through( m.value )
  147. elif m.op == 'monitor':
  148. self.enable_monitor( m.dir=='in', m.value )
  149. return errL
  150. if __name__ == "__main__":
  151. md = MidiDevice()
  152. print(md.get_port_list( True ))
  153. print(md.get_port_list( False ))