gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

midi.py (19419B)


      1 """
      2 Module for interacting with midi input and output.
      3 
      4 The midi module can send output to midi devices, and get input
      5 from midi devices.  It can also list midi devices on the system.
      6 
      7 Including real midi devices, and virtual ones.
      8 
      9 It uses the portmidi library.  Is portable to which ever platforms
     10 portmidi supports (currently windows, OSX, and linux).
     11 """
     12 
     13 import atexit
     14 
     15 
     16 
     17 _init = False
     18 _pypm = None
     19 
     20 
     21 __all__ = [ "Input",
     22             "MidiException",
     23             "Output",
     24             "get_count",
     25             "get_default_input_id",
     26             "get_default_output_id",
     27             "get_device_info",
     28             "init",
     29             "quit",
     30             "time",
     31            ]
     32 
     33 __theclasses__ = ["Input", "Output"]
     34 
     35 
     36 def init():
     37     """initialize the midi module
     38     pyportmidi.init(): return None
     39     
     40     Call the initialisation function before using the midi module.
     41     
     42     It is safe to call this more than once.
     43     """
     44     global _init, _pypm
     45     if not _init:
     46         import pyportmidi._pyportmidi
     47         _pypm = pyportmidi._pyportmidi
     48 
     49         _pypm.Initialize()
     50         _init = True
     51         atexit.register(quit)
     52 
     53 
     54 def quit():
     55     """uninitialize the midi module
     56     pyportmidi.quit(): return None
     57 
     58 
     59     Called automatically atexit if you don't call it.
     60 
     61     It is safe to call this function more than once.
     62     """
     63     global _init, _pypm
     64     if _init:
     65         # TODO: find all Input and Output classes and close them first?
     66         _pypm.Terminate()
     67         _init = False
     68         del _pypm
     69 
     70 def _check_init():
     71     if not _init:
     72         raise RuntimeError("pyportmidi not initialised.")
     73 
     74 def get_count():
     75     """gets the number of devices.
     76     pyportmidi.get_count(): return num_devices
     77 
     78 
     79     Device ids range from 0 to get_count() -1
     80     """
     81     _check_init()
     82     return _pypm.CountDevices()
     83 
     84 
     85 
     86 
     87 def get_default_input_id():
     88     """gets default input device number
     89     pyportmidi.get_default_input_id(): return default_id
     90     
     91     
     92     Return the default device ID or -1 if there are no devices.
     93     The result can be passed to the Input()/Ouput() class.
     94     
     95     On the PC, the user can specify a default device by
     96     setting an environment variable. For example, to use device #1.
     97     
     98         set PM_RECOMMENDED_INPUT_DEVICE=1
     99     
    100     The user should first determine the available device ID by using
    101     the supplied application "testin" or "testout".
    102     
    103     In general, the registry is a better place for this kind of info,
    104     and with USB devices that can come and go, using integers is not
    105     very reliable for device identification. Under Windows, if
    106     PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is
    107     *NOT* found in the environment, then the default device is obtained
    108     by looking for a string in the registry under:
    109         HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
    110     and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
    111     for a string. The number of the first device with a substring that
    112     matches the string exactly is returned. For example, if the string
    113     in the registry is "USB", and device 1 is named
    114     "In USB MidiSport 1x1", then that will be the default
    115     input because it contains the string "USB".
    116     
    117     In addition to the name, get_device_info() returns "interf", which
    118     is the interface name. (The "interface" is the underlying software
    119     system or API used by PortMidi to access devices. Examples are
    120     MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.)
    121     At present, the only Win32 interface is "MMSystem", the only Linux
    122     interface is "ALSA", and the only Max OS X interface is "CoreMIDI".
    123     To specify both the interface and the device name in the registry,
    124     separate the two with a comma and a space, e.g.:
    125         MMSystem, In USB MidiSport 1x1
    126     In this case, the string before the comma must be a substring of
    127     the "interf" string, and the string after the space must be a
    128     substring of the "name" name string in order to match the device.
    129     
    130     Note: in the current release, the default is simply the first device
    131     (the input or output device with the lowest PmDeviceID).
    132     """
    133     return _pypm.GetDefaultInputDeviceID()
    134 
    135 
    136 
    137 
    138 def get_default_output_id():
    139     """gets default output device number
    140     pyportmidi.get_default_output_id(): return default_id
    141     
    142     
    143     Return the default device ID or -1 if there are no devices.
    144     The result can be passed to the Input()/Ouput() class.
    145     
    146     On the PC, the user can specify a default device by
    147     setting an environment variable. For example, to use device #1.
    148     
    149         set PM_RECOMMENDED_OUTPUT_DEVICE=1
    150     
    151     The user should first determine the available device ID by using
    152     the supplied application "testin" or "testout".
    153     
    154     In general, the registry is a better place for this kind of info,
    155     and with USB devices that can come and go, using integers is not
    156     very reliable for device identification. Under Windows, if
    157     PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is
    158     *NOT* found in the environment, then the default device is obtained
    159     by looking for a string in the registry under:
    160         HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
    161     and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
    162     for a string. The number of the first device with a substring that
    163     matches the string exactly is returned. For example, if the string
    164     in the registry is "USB", and device 1 is named
    165     "In USB MidiSport 1x1", then that will be the default
    166     input because it contains the string "USB".
    167     
    168     In addition to the name, get_device_info() returns "interf", which
    169     is the interface name. (The "interface" is the underlying software
    170     system or API used by PortMidi to access devices. Examples are
    171     MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.)
    172     At present, the only Win32 interface is "MMSystem", the only Linux
    173     interface is "ALSA", and the only Max OS X interface is "CoreMIDI".
    174     To specify both the interface and the device name in the registry,
    175     separate the two with a comma and a space, e.g.:
    176         MMSystem, In USB MidiSport 1x1
    177     In this case, the string before the comma must be a substring of
    178     the "interf" string, and the string after the space must be a
    179     substring of the "name" name string in order to match the device.
    180     
    181     Note: in the current release, the default is simply the first device
    182     (the input or output device with the lowest PmDeviceID).
    183     """
    184     _check_init()
    185     return _pypm.GetDefaultOutputDeviceID()
    186 
    187 
    188 def get_device_info(an_id):
    189     """ returns information about a midi device
    190     pyportmidi.get_device_info(an_id): return (interf, name, input, output, opened) 
    191 
    192     interf - a text string describing the device interface, eg 'ALSA'.
    193     name - a text string for the name of the device, eg 'Midi Through Port-0'
    194     input - 0, or 1 if the device is an input device.
    195     output - 0, or 1 if the device is an output device.
    196     opened - 0, or 1 if the device is opened.
    197 
    198     If the id is out of range, the function returns None.
    199     """
    200     _check_init()
    201     return _pypm.GetDeviceInfo(an_id) 
    202 
    203 
    204 class Input(object):
    205     """Input is used to get midi input from midi devices.
    206     Input(device_id)
    207     Input(device_id, buffer_size)
    208 
    209     buffer_size -the number of input events to be buffered waiting to 
    210       be read using Input.read() 
    211     """
    212 
    213     def __init__(self, device_id, buffer_size=4096):
    214         """
    215         The buffer_size specifies the number of input events to be buffered 
    216         waiting to be read using Input.read().
    217         """
    218         _check_init()
    219  
    220         if device_id == -1:
    221             raise MidiException("Device id is -1, not a valid output id.  -1 usually means there were no default Output devices.")
    222             
    223         try:
    224             r = get_device_info(device_id)
    225         except TypeError:
    226             raise TypeError("an integer is required")
    227         except OverflowError:
    228             raise OverflowError("long int too large to convert to int")
    229 
    230         # and now some nasty looking error checking, to provide nice error 
    231         #   messages to the kind, lovely, midi using people of whereever.
    232         if r:
    233             interf, name, input, output, opened = r
    234             if input:
    235                 try:
    236                     self._input = _pypm.Input(device_id, buffer_size)
    237                 except TypeError:
    238                     raise TypeError("an integer is required")
    239                 self.device_id = device_id
    240 
    241             elif output:
    242                 raise MidiException("Device id given is not a valid input id, it is an output id.")
    243             else:
    244                 raise MidiException("Device id given is not a valid input id.")
    245         else:
    246             raise MidiException("Device id invalid, out of range.")
    247 
    248 
    249 
    250 
    251     def _check_open(self):
    252         if self._input is None:
    253             raise MidiException("midi not open.")
    254 
    255 
    256 
    257     def close(self):
    258         """ closes a midi stream, flushing any pending buffers.
    259         Input.close(): return None
    260 
    261         PortMidi attempts to close open streams when the application
    262         exits -- this is particularly difficult under Windows.
    263         """
    264         _check_init()
    265         if not (self._input is None):
    266             self._input.Close()
    267         self._input = None
    268 
    269 
    270 
    271     def read(self, num_events):
    272         """reads num_events midi events from the buffer.
    273         Input.read(num_events): return midi_event_list
    274 
    275         Reads from the Input buffer and gives back midi events.
    276         [[[status,data1,data2,data3],timestamp],
    277          [[status,data1,data2,data3],timestamp],...]
    278         """
    279         _check_init()
    280         self._check_open()
    281         return self._input.Read(num_events)
    282 
    283 
    284     def poll(self):
    285         """returns true if there's data, or false if not.
    286         Input.poll(): return Bool
    287 
    288         raises a MidiException on error.
    289         """
    290         _check_init()
    291         self._check_open()
    292 
    293         r = self._input.Poll()
    294         if r == _pypm.TRUE:
    295             return True
    296         elif r == _pypm.FALSE:
    297             return False
    298         else:
    299             err_text = GetErrorText(r)
    300             raise MidiException( (r, err_text) )
    301 
    302 
    303 
    304 
    305 class Output(object):
    306     """Output is used to send midi to an output device
    307     Output(device_id)
    308     Output(device_id, latency = 0)
    309     Output(device_id, buffer_size = 4096)
    310     Output(device_id, latency, buffer_size)
    311 
    312     The buffer_size specifies the number of output events to be 
    313     buffered waiting for output.  (In some cases -- see below -- 
    314     PortMidi does not buffer output at all and merely passes data 
    315     to a lower-level API, in which case buffersize is ignored.)
    316 
    317     latency is the delay in milliseconds applied to timestamps to determine
    318     when the output should actually occur. (If latency is < 0, 0 is 
    319     assumed.)
    320 
    321     If latency is zero, timestamps are ignored and all output is delivered
    322     immediately. If latency is greater than zero, output is delayed until
    323     the message timestamp plus the latency. (NOTE: time is measured 
    324     relative to the time source indicated by time_proc. Timestamps are 
    325     absolute, not relative delays or offsets.) In some cases, PortMidi 
    326     can obtain better timing than your application by passing timestamps 
    327     along to the device driver or hardware. Latency may also help you 
    328     to synchronize midi data to audio data by matching midi latency to 
    329     the audio buffer latency.
    330 
    331     """
    332 
    333     def __init__(self, device_id, latency = 0, buffer_size = 4096):
    334         """Output(device_id)
    335         Output(device_id, latency = 0)
    336         Output(device_id, buffer_size = 4096)
    337         Output(device_id, latency, buffer_size)
    338 
    339         The buffer_size specifies the number of output events to be 
    340         buffered waiting for output.  (In some cases -- see below -- 
    341         PortMidi does not buffer output at all and merely passes data 
    342         to a lower-level API, in which case buffersize is ignored.)
    343 
    344         latency is the delay in milliseconds applied to timestamps to determine
    345         when the output should actually occur. (If latency is < 0, 0 is 
    346         assumed.)
    347 
    348         If latency is zero, timestamps are ignored and all output is delivered
    349         immediately. If latency is greater than zero, output is delayed until
    350         the message timestamp plus the latency. (NOTE: time is measured 
    351         relative to the time source indicated by time_proc. Timestamps are 
    352         absolute, not relative delays or offsets.) In some cases, PortMidi 
    353         can obtain better timing than your application by passing timestamps 
    354         along to the device driver or hardware. Latency may also help you 
    355         to synchronize midi data to audio data by matching midi latency to 
    356         the audio buffer latency.
    357         """
    358      
    359         _check_init()
    360         self._aborted = 0
    361 
    362         if device_id == -1:
    363             raise MidiException("Device id is -1, not a valid output id.  -1 usually means there were no default Output devices.")
    364             
    365         try:
    366             r = get_device_info(device_id)
    367         except TypeError:
    368             raise TypeError("an integer is required")
    369         except OverflowError:
    370             raise OverflowError("long int too large to convert to int")
    371 
    372         # and now some nasty looking error checking, to provide nice error 
    373         #   messages to the kind, lovely, midi using people of whereever.
    374         if r:
    375             interf, name, input, output, opened = r
    376             if output:
    377                 try:
    378                     self._output = _pypm.Output(device_id, latency)
    379                 except TypeError:
    380                     raise TypeError("an integer is required")
    381                 self.device_id = device_id
    382 
    383             elif input:
    384                 raise MidiException("Device id given is not a valid output id, it is an input id.")
    385             else:
    386                 raise MidiException("Device id given is not a valid output id.")
    387         else:
    388             raise MidiException("Device id invalid, out of range.")
    389 
    390     def _check_open(self):
    391         if self._output is None:
    392             raise MidiException("midi not open.")
    393 
    394         if self._aborted:
    395             raise MidiException("midi aborted.")
    396 
    397 
    398     def close(self):
    399         """ closes a midi stream, flushing any pending buffers.
    400         Output.close(): return None
    401 
    402         PortMidi attempts to close open streams when the application
    403         exits -- this is particularly difficult under Windows.
    404         """
    405         _check_init()
    406         if not (self._output is None):
    407             self._output.Close()
    408         self._output = None
    409 
    410     def abort(self):
    411         """terminates outgoing messages immediately
    412         Output.abort(): return None
    413 
    414         The caller should immediately close the output port;
    415         this call may result in transmission of a partial midi message.
    416         There is no abort for Midi input because the user can simply
    417         ignore messages in the buffer and close an input device at
    418         any time.
    419         """
    420 
    421         _check_init()
    422         if self._output:
    423             self._output.Abort()
    424         self._aborted = 1
    425 
    426 
    427 
    428 
    429 
    430     def write(self, data):
    431         """writes a list of midi data to the Output
    432         Output.write(data)
    433 
    434         writes series of MIDI information in the form of a list:
    435              write([[[status <,data1><,data2><,data3>],timestamp],
    436                     [[status <,data1><,data2><,data3>],timestamp],...])
    437         <data> fields are optional
    438         example: choose program change 1 at time 20000 and
    439         send note 65 with velocity 100 500 ms later.
    440              write([[[0xc0,0,0],20000],[[0x90,60,100],20500]])
    441         notes:
    442           1. timestamps will be ignored if latency = 0.
    443           2. To get a note to play immediately, send MIDI info with
    444              timestamp read from function Time.
    445           3. understanding optional data fields:
    446                write([[[0xc0,0,0],20000]]) is equivalent to
    447                write([[[0xc0],20000]])
    448 
    449         Can send up to 1024 elements in your data list, otherwise an 
    450          IndexError exception is raised.
    451         """
    452         _check_init()
    453         self._check_open()
    454 
    455         self._output.Write(data)
    456 
    457 
    458     def write_short(self, status, data1 = 0, data2 = 0):
    459         """write_short(status <, data1><, data2>)
    460         Output.write_short(status)
    461         Output.write_short(status, data1 = 0, data2 = 0)
    462 
    463         output MIDI information of 3 bytes or less.
    464         data fields are optional
    465         status byte could be:
    466              0xc0 = program change
    467              0x90 = note on
    468              etc.
    469              data bytes are optional and assumed 0 if omitted
    470         example: note 65 on with velocity 100
    471              write_short(0x90,65,100)
    472         """
    473         _check_init()
    474         self._check_open()
    475         self._output.WriteShort(status, data1, data2)
    476 
    477 
    478     def write_sys_ex(self, when, msg):
    479         """writes a timestamped system-exclusive midi message.
    480         Output.write_sys_ex(when, msg)
    481 
    482         msg - can be a *list* or a *string*
    483         when - a timestamp in miliseconds
    484         example:
    485           (assuming o is an onput MIDI stream)
    486             o.write_sys_ex(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7')
    487           is equivalent to
    488             o.write_sys_ex(pyportmidi.time(),
    489                            [0xF0,0x7D,0x10,0x11,0x12,0x13,0xF7])
    490         """
    491         _check_init()
    492         self._check_open()
    493         self._output.WriteSysEx(when, msg)
    494 
    495 
    496     def note_on(self, note, velocity=None, channel = 0):
    497         """turns a midi note on.  Note must be off.
    498         Output.note_on(note, velocity=None, channel = 0)
    499 
    500         Turn a note on in the output stream.  The note must already
    501         be off for this to work correctly.
    502         """
    503         if velocity is None:
    504             velocity = 0
    505 
    506         if not (0 <= channel <= 15):
    507             raise ValueError("Channel not between 0 and 15.")
    508 
    509         self.write_short(0x90+channel, note, velocity)
    510 
    511     def note_off(self, note, velocity=None, channel = 0):
    512         """turns a midi note off.  Note must be on.
    513         Output.note_off(note, velocity=None, channel = 0)
    514 
    515         Turn a note off in the output stream.  The note must already
    516         be on for this to work correctly.
    517         """
    518         if velocity is None:
    519             velocity = 0
    520 
    521         if not (0 <= channel <= 15):
    522             raise ValueError("Channel not between 0 and 15.")
    523 
    524         self.write_short(0x80 + channel, note, velocity)
    525 
    526 
    527     def set_instrument(self, instrument_id, channel = 0):
    528         """select an instrument, with a value between 0 and 127
    529         Output.set_instrument(instrument_id, channel = 0)
    530 
    531         """
    532         if not (0 <= instrument_id <= 127):
    533             raise ValueError("Undefined instrument id: %d" % instrument_id)
    534 
    535         if not (0 <= channel <= 15):
    536             raise ValueError("Channel not between 0 and 15.")
    537 
    538         self.write_short(0xc0+channel, instrument_id)
    539 
    540 
    541 
    542 def time():
    543     """returns the current time in ms of the PortMidi timer
    544     pyportmidi.time(): return time
    545 
    546     The time is reset to 0, when the module is inited.
    547     """
    548     return _pypm.Time()
    549 
    550 
    551 
    552 
    553 
    554 
    555 
    556 
    557 class MidiException(Exception):
    558     """MidiException(errno) that can be raised.
    559     """
    560     def __init__(self, value):
    561         self.parameter = value
    562     def __str__(self):
    563         return repr(self.parameter)
    564 
    565 
    566