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

_pyportmidi.pyx (17368B)


      1 # pyPortMidi
      2 # Python bindings for PortMidi
      3 # John Harrison
      4 # http://sound.media.mit.edu/~harrison
      5 # harrison@media.mit.edu
      6 # written in Pyrex
      7 __version__="0.07"
      8 
      9 import array
     10 
     11 # CHANGES:
     12 
     13 # 0.0.5: (June 1st, 2009)
     14 #   Output no longer calls abort when it deallocates.
     15 #   Added abort and close methods.
     16 #   Need to call Abort() explicityly if you want that to happen.
     17 
     18 
     19 #
     20 # 0.0.3: (March 15, 2005)
     21 #   changed everything from tuples to lists
     22 #   return 4 values for PmRead instead of 3 (for SysEx)
     23 #   minor fixes for flexibility and error checking
     24 #   flushed out DistUtils package and added Mac and Linux compile support
     25 #   Markus Pfaff: added ability for WriteSysEx to accept lists as well
     26 #                 as strings
     27 
     28 # 0.0.2:
     29 #   fixed pointer to function calls to avoid necessity of pyport library
     30 
     31 # 0.0.1:
     32 #   initial release
     33 
     34 cdef extern from "portmidi.h":
     35     ctypedef enum PmError:
     36         pmNoError = 0,
     37         pmHostError = -10000,
     38         pmInvalidDeviceId, #/* out of range or output device when input is requested or vice versa */
     39         pmInsufficientMemory,
     40         pmBufferTooSmall,
     41         pmBufferOverflow,
     42         pmBadPtr,
     43         pmBadData, #/* illegal midi data, e.g. missing EOX */
     44         pmInternalError,
     45         pmBufferMaxSize, #/* buffer is already as large as it can be */
     46     PmError Pm_Initialize()
     47     PmError Pm_Terminate()
     48     ctypedef void PortMidiStream
     49     ctypedef PortMidiStream PmStream # CHECK THIS!
     50     ctypedef int PmDeviceID
     51     int Pm_HasHostError( PortMidiStream * stream )  
     52     char *Pm_GetErrorText( PmError errnum )
     53     Pm_GetHostErrorText(char * msg, unsigned int len)
     54     ctypedef struct PmDeviceInfo:
     55         int structVersion
     56         char *interf #/* underlying MIDI API, e.g. MMSystem or DirectX */
     57         char *name   #/* device name, e.g. USB MidiSport 1x1 */
     58         int input    #/* true iff input is available */
     59         int output   #/* true iff output is available */
     60         int opened   #/* used by generic PortMidi code to do error checking on arguments */
     61     int Pm_CountDevices()
     62     PmDeviceID Pm_GetDefaultInputDeviceID()
     63     PmDeviceID Pm_GetDefaultOutputDeviceID()
     64     ctypedef long PmTimestamp
     65     ctypedef PmTimestamp (*PmTimeProcPtr)(void *time_info)
     66     #PmBefore is not defined...
     67     PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id )
     68     PmError Pm_OpenInput( PortMidiStream** stream,
     69                           PmDeviceID inputDevice,
     70                           void *inputDriverInfo,
     71                           long bufferSize,
     72                           long (*PmPtr) (), # long = PtTimestamp
     73                           void *time_info )
     74     PmError Pm_OpenOutput( PortMidiStream** stream,
     75                            PmDeviceID outputDevice,
     76                            void *outputDriverInfo,
     77                            long bufferSize,
     78                            #long (*PmPtr) (), # long = PtTimestamp
     79                            PmTimeProcPtr time_proc, # long = PtTimestamp
     80                            void *time_info,
     81                            long latency )
     82     PmError Pm_SetFilter( PortMidiStream* stream, long filters )
     83     PmError Pm_Abort( PortMidiStream* stream )
     84     PmError Pm_Close( PortMidiStream* stream )
     85     ctypedef long PmMessage
     86     ctypedef struct PmEvent:
     87         PmMessage message
     88         PmTimestamp timestamp
     89     PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length )
     90     PmError Pm_Poll( PortMidiStream *stream)
     91     int Pm_Channel(int channel)
     92     PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
     93     PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length )
     94     PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg)
     95 
     96 cdef extern from "porttime.h":
     97     ctypedef enum PtError:
     98         ptNoError = 0,
     99         ptHostError = -10000,
    100         ptAlreadyStarted,
    101         ptAlreadyStopped,
    102         ptInsufficientMemory
    103     ctypedef long PtTimestamp
    104     ctypedef void (* PtCallback)( PtTimestamp timestamp, void *userData )
    105     PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
    106     PtTimestamp Pt_Time()
    107 
    108 FILT_ACTIVE=0x1
    109 FILT_SYSEX=0x2
    110 FILT_CLOCK=0x4
    111 FILT_PLAY=0x8
    112 FILT_F9=0x10
    113 FILT_TICK=0x10
    114 FILT_FD=0x20
    115 FILT_UNDEFINED=0x30
    116 FILT_RESET=0x40
    117 FILT_REALTIME=0x7F
    118 FILT_NOTE=0x80
    119 FILT_CHANNEL_AFTERTOUCH=0x100
    120 FILT_POLY_AFTERTOUCH=0x200
    121 FILT_AFTERTOUCH=0x300
    122 FILT_PROGRAM=0x400
    123 FILT_CONTROL=0x800
    124 FILT_PITCHBEND=0x1000
    125 FILT_MTC=0x2000
    126 FILT_SONG_POSITION=0x4000
    127 FILT_SONG_SELECT=0x8000
    128 FILT_TUNE=0x10000
    129 FALSE=0
    130 TRUE=1
    131 
    132 def Initialize():
    133     """
    134 Initialize: call this first
    135     """
    136     Pm_Initialize()
    137     Pt_Start(1, NULL, NULL) # /* equiv to TIME_START: start timer w/ ms accuracy */
    138 
    139 def Terminate():
    140     """
    141 Terminate: call this to clean up Midi streams when done.
    142 If you do not call this on Windows machines when you are
    143 done with MIDI, your system may crash.
    144     """
    145     Pm_Terminate()
    146 
    147 def GetDefaultInputDeviceID():
    148     return Pm_GetDefaultInputDeviceID()
    149 
    150 def GetDefaultOutputDeviceID():
    151     return Pm_GetDefaultOutputDeviceID()
    152 
    153 def CountDevices():
    154     return Pm_CountDevices()
    155 
    156 def GetDeviceInfo(i):
    157     """
    158 GetDeviceInfo(<device number>): returns 5 parameters
    159   - underlying MIDI API
    160   - device name
    161   - TRUE iff input is available
    162   - TRUE iff output is available
    163   - TRUE iff device stream is already open
    164     """
    165     cdef PmDeviceInfo *info
    166 
    167     # disregarding the constness from Pm_GetDeviceInfo, since pyrex doesn't do const.
    168     info = <PmDeviceInfo *>Pm_GetDeviceInfo(i)
    169 
    170     if info <> NULL: return info.interf, info.name, info.input, info.output, info.opened
    171     else: return 
    172 
    173 def Time():
    174     """
    175 Time() returns the current time in ms
    176 of the PortMidi timer
    177     """
    178     return Pt_Time()
    179 
    180 def GetErrorText(err):
    181     """
    182 GetErrorText(<err num>) returns human-readable error
    183 messages translated from error numbers
    184     """
    185     return Pm_GetErrorText(err)
    186 
    187 def Channel(chan):
    188     """
    189 Channel(<chan>) is used with ChannelMask on input MIDI streams.
    190 Example: to receive input on channels 1 and 10 on a MIDI
    191          stream called MidiIn:
    192 MidiIn.SetChannelMask(pypm.Channel(1) | pypm.Channel(10))
    193 
    194 note: PyPortMidi Channel function has been altered from
    195       the original PortMidi c call to correct for what
    196       seems to be a bug --- i.e. channel filters were
    197       all numbered from 0 to 15 instead of 1 to 16.
    198     """
    199     return Pm_Channel(chan-1)
    200 
    201 cdef class Output:
    202     """
    203 class Output:
    204     define an output MIDI stream. Takes the form:
    205         x = pypm.Output(MidiOutputDevice, latency)
    206     latency is in ms.
    207     If latency = 0 then timestamps for output are ignored.
    208     """
    209     cdef int i
    210     cdef PmStream *midi
    211     cdef int debug
    212     cdef int _aborted
    213 
    214     def __init__(self, OutputDevice, latency=0):
    215         
    216         cdef PmError err
    217         #cdef PtTimestamp (*PmPtr) ()
    218         cdef PmTimeProcPtr PmPtr
    219 
    220         self.i = OutputDevice
    221         self.debug = 0
    222         self._aborted = 0
    223         
    224         if latency == 0:
    225             PmPtr = NULL
    226         else:
    227             PmPtr = <PmTimeProcPtr>&Pt_Time
    228         if self.debug: print "Opening Midi Output"
    229 	# Why is bufferSize 0 here?
    230         err = Pm_OpenOutput(&(self.midi), self.i, NULL, 0, PmPtr, NULL, latency)
    231         if err < 0:
    232                 s = Pm_GetErrorText(err)
    233                 # Something's amiss here - if we try to throw an Exception
    234                	# here, we crash.
    235                 if not err == -10000:
    236                         raise Exception,s
    237                 else:
    238                         print "Unable to open Midi OutputDevice=",OutputDevice," err=",s
    239 
    240     def __dealloc__(self):
    241         if self.debug: print "Closing MIDI output stream and destroying instance"
    242         #err = Pm_Abort(self.midi)
    243         #if err < 0: raise Exception, Pm_GetErrorText(err)
    244         err = Pm_Close(self.midi)
    245         if err < 0: raise Exception, Pm_GetErrorText(err) 
    246 
    247 
    248     def _check_open(self):
    249         """ checks to see if the midi is open, and if not, raises an error.
    250         """
    251 
    252         if self.midi == NULL:
    253             raise Exception, "midi Output not open."
    254 
    255         if self._aborted:
    256             raise Exception, "midi Output aborted.  Need to call Close after Abort."
    257 
    258     def Close(self):
    259         """
    260 Close()
    261     closes a midi stream, flushing any pending buffers.
    262     (PortMidi attempts to close open streams when the application
    263     exits -- this is particularly difficult under Windows.)
    264         """
    265         #if not self.midi:
    266         #    return
    267 
    268         err = Pm_Close(self.midi)
    269         if err < 0:
    270             raise Exception, Pm_GetErrorText(err)
    271         #self.midi = NULL
    272 
    273 
    274     def Abort(self):
    275         """
    276 Abort() terminates outgoing messages immediately
    277     The caller should immediately close the output port;
    278     this call may result in transmission of a partial midi message.
    279     There is no abort for Midi input because the user can simply
    280     ignore messages in the buffer and close an input device at
    281     any time.
    282         """
    283         #if not self.midi:
    284         #    return
    285 
    286         err = Pm_Abort(self.midi)
    287         if err < 0:
    288             raise Exception, Pm_GetErrorText(err)
    289 
    290         self._aborted = 1
    291 
    292 
    293     def Write(self, data):
    294         """
    295 Write(data)
    296     output a series of MIDI information in the form of a list:
    297          Write([[[status <,data1><,data2><,data3>],timestamp],
    298                 [[status <,data1><,data2><,data3>],timestamp],...])
    299     <data> fields are optional
    300     example: choose program change 1 at time 20000 and
    301     send note 65 with velocity 100 500 ms later.
    302          Write([[[0xc0,0,0],20000],[[0x90,60,100],20500]])
    303     notes:
    304       1. timestamps will be ignored if latency = 0.
    305       2. To get a note to play immediately, send MIDI info with
    306          timestamp read from function Time.
    307       3. understanding optional data fields:
    308            Write([[[0xc0,0,0],20000]]) is equivalent to
    309            Write([[[0xc0],20000]])
    310         """
    311         cdef PmEvent buffer[1024]
    312         cdef PmError err
    313         cdef int i
    314 
    315         self._check_open()
    316 
    317 
    318         if len(data) > 1024: raise IndexError, 'maximum list length is 1024'
    319         else:
    320             for loop1 in range(len(data)):
    321                 if ((len(data[loop1][0]) > 4) |
    322                     (len(data[loop1][0]) < 1)):
    323                     raise IndexError, str(len(data[loop1][0]))+' arguments in event list'
    324                 buffer[loop1].message = 0
    325                 for i in range(len(data[loop1][0])):
    326                     buffer[loop1].message = buffer[loop1].message + ((data[loop1][0][i]&0xFF) << (8*i))
    327                 buffer[loop1].timestamp = data[loop1][1]
    328                 if self.debug: print loop1," : ",buffer[loop1].message," : ",buffer[loop1].timestamp
    329         if self.debug: print "writing to midi buffer"
    330         err= Pm_Write(self.midi, buffer, len(data))
    331         if err < 0: raise Exception, Pm_GetErrorText(err)
    332         
    333     def WriteShort(self, status, data1 = 0, data2 = 0):
    334         """
    335 WriteShort(status <, data1><, data2>)
    336      output MIDI information of 3 bytes or less.
    337      data fields are optional
    338      status byte could be:
    339           0xc0 = program change
    340           0x90 = note on
    341           etc.
    342           data bytes are optional and assumed 0 if omitted
    343      example: note 65 on with velocity 100
    344           WriteShort(0x90,65,100)
    345         """
    346         cdef PmEvent buffer[1]
    347         cdef PmError err
    348         self._check_open()
    349 
    350         buffer[0].timestamp = Pt_Time()
    351         buffer[0].message = ((((data2) << 16) & 0xFF0000) | (((data1) << 8) & 0xFF00) | ((status) & 0xFF))
    352         if self.debug: print "Writing to MIDI buffer"
    353         err = Pm_Write(self.midi, buffer, 1) # stream, buffer, length
    354         if err < 0 : raise Exception, Pm_GetErrorText(err)
    355 
    356     def WriteSysEx(self, when, msg):
    357         """
    358         WriteSysEx(<timestamp>,<msg>)
    359         writes a timestamped system-exclusive midi message.
    360         <msg> can be a *list* or a *string*
    361         example:
    362             (assuming y is an input MIDI stream)
    363             y.WriteSysEx(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7')
    364                               is equivalent to
    365             y.WriteSysEx(pypm.Time,
    366             [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7])
    367         """
    368         cdef PmError err
    369         cdef char *cmsg
    370         cdef PtTimestamp CurTime
    371 
    372         self._check_open()
    373 
    374         if type(msg) is list:
    375             msg = array.array('B',msg).tostring() # Markus Pfaff contribution
    376         cmsg = msg
    377 
    378         CurTime = Pt_Time()
    379         err = Pm_WriteSysEx(self.midi, when, <unsigned char *> cmsg)
    380         if err < 0 : raise Exception, Pm_GetErrorText(err)
    381         while Pt_Time() == CurTime: # wait for SysEx to go thru or...my
    382             pass                    # win32 machine crashes w/ multiple SysEx
    383 
    384 
    385 
    386 
    387 
    388 
    389 
    390 
    391 
    392 
    393 
    394 cdef class Input:
    395     """
    396 class Input:
    397     define an input MIDI stream. Takes the form:
    398         x = pypm.Input(MidiInputDevice)
    399     """
    400     cdef PmStream *midi
    401     cdef int debug
    402     cdef int i
    403 
    404     def __init__(self, InputDevice, buffersize=4096):
    405         cdef PmError err
    406         self.i = InputDevice
    407         self.debug = 0
    408         err= Pm_OpenInput(&(self.midi),self.i,NULL,buffersize,&Pt_Time,NULL)
    409         if err < 0: raise Exception, Pm_GetErrorText(err)
    410         if self.debug: print "MIDI input opened."
    411 
    412     def __dealloc__(self):
    413         cdef PmError err
    414         if self.debug: print "Closing MIDI input stream and destroying instance"
    415 
    416         err = Pm_Close(self.midi)
    417         if err < 0:
    418             raise Exception, Pm_GetErrorText(err)
    419 
    420 
    421 
    422     def _check_open(self):
    423         """ checks to see if the midi is open, and if not, raises an error.
    424         """
    425 
    426         if self.midi == NULL:
    427             raise Exception, "midi Input not open."
    428 
    429 
    430     def Close(self):
    431         """
    432 Close()
    433     closes a midi stream, flushing any pending buffers.
    434     (PortMidi attempts to close open streams when the application
    435     exits -- this is particularly difficult under Windows.)
    436         """
    437         #if not self.midi:
    438         #    return
    439 
    440         err = Pm_Close(self.midi)
    441         if err < 0:
    442             raise Exception, Pm_GetErrorText(err)
    443         #self.midi = NULL
    444 
    445 
    446 
    447     def SetFilter(self, filters):
    448         """
    449     SetFilter(<filters>) sets filters on an open input stream
    450     to drop selected input types. By default, only active sensing
    451     messages are filtered. To prohibit, say, active sensing and
    452     sysex messages, call
    453     SetFilter(stream, FILT_ACTIVE | FILT_SYSEX);
    454 
    455     Filtering is useful when midi routing or midi thru functionality
    456     is being provided by the user application.
    457     For example, you may want to exclude timing messages
    458     (clock, MTC, start/stop/continue), while allowing note-related
    459     messages to pass. Or you may be using a sequencer or drum-machine
    460     for MIDI clock information but want to exclude any notes
    461     it may play.
    462 
    463     Note: SetFilter empties the buffer after setting the filter,
    464     just in case anything got through.
    465         """
    466         cdef PmEvent buffer[1]
    467         cdef PmError err
    468 
    469         self._check_open()
    470 
    471 
    472         err = Pm_SetFilter(self.midi, filters)
    473 
    474         if err < 0: raise Exception, Pm_GetErrorText(err)
    475 
    476         while(Pm_Poll(self.midi) != pmNoError):
    477 
    478             err = Pm_Read(self.midi,buffer,1)
    479             if err < 0: raise Exception, Pm_GetErrorText(err)
    480 
    481     def SetChannelMask(self, mask):
    482         """
    483     SetChannelMask(<mask>) filters incoming messages based on channel.
    484     The mask is a 16-bit bitfield corresponding to appropriate channels
    485     Channel(<channel>) can assist in calling this function.
    486     i.e. to set receive only input on channel 1, call with
    487     SetChannelMask(Channel(1))
    488     Multiple channels should be OR'd together, like
    489     SetChannelMask(Channel(10) | Channel(11))
    490     note: PyPortMidi Channel function has been altered from
    491           the original PortMidi c call to correct for what
    492           seems to be a bug --- i.e. channel filters were
    493           all numbered from 0 to 15 instead of 1 to 16.
    494         """
    495         cdef PmError err
    496 
    497         self._check_open()
    498 
    499         err = Pm_SetChannelMask(self.midi,mask)
    500         if err < 0: raise Exception, Pm_GetErrorText(err)
    501         
    502     def Poll(self):
    503         """
    504     Poll tests whether input is available,
    505     returning TRUE, FALSE, or an error value.
    506         """
    507         cdef PmError err
    508         self._check_open()
    509 
    510         err = Pm_Poll(self.midi)
    511         if err < 0: raise Exception, Pm_GetErrorText(err)
    512         return err
    513     
    514     def Read(self,length):
    515         """
    516 Read(length): returns up to <length> midi events stored in
    517 the buffer and returns them as a list:
    518 [[[status,data1,data2,data3],timestamp],
    519  [[status,data1,data2,data3],timestamp],...]
    520 example: Read(50) returns all the events in the buffer,
    521          up to 50 events.
    522         """
    523         cdef PmEvent buffer[1024]
    524         
    525         self._check_open()
    526 
    527         x = []
    528         
    529         if length > 1024: raise IndexError, 'maximum buffer length is 1024'
    530         if length < 1: raise IndexError, 'minimum buffer length is 1'
    531         NumEvents = Pm_Read(self.midi,buffer,length)
    532         if NumEvents < 0: raise Exception, Pm_GetErrorText(NumEvents)
    533         x=[]
    534         if NumEvents >= 1:
    535             for loop in range(NumEvents):
    536                  x.append([[buffer[loop].message & 0xff, (buffer[loop].message >> 8) & 0xFF, (buffer[loop].message >> 16) & 0xFF, (buffer[loop].message >> 24) & 0xFF], buffer[loop].timestamp])
    537         return x