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
9import 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
34cdef 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
96cdef 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
108FILT_ACTIVE=0x1
109FILT_SYSEX=0x2
110FILT_CLOCK=0x4
111FILT_PLAY=0x8
112FILT_F9=0x10
113FILT_TICK=0x10
114FILT_FD=0x20
115FILT_UNDEFINED=0x30
116FILT_RESET=0x40
117FILT_REALTIME=0x7F
118FILT_NOTE=0x80
119FILT_CHANNEL_AFTERTOUCH=0x100
120FILT_POLY_AFTERTOUCH=0x200
121FILT_AFTERTOUCH=0x300
122FILT_PROGRAM=0x400
123FILT_CONTROL=0x800
124FILT_PITCHBEND=0x1000
125FILT_MTC=0x2000
126FILT_SONG_POSITION=0x4000
127FILT_SONG_SELECT=0x8000
128FILT_TUNE=0x10000
129FALSE=0
130TRUE=1
131
132def Initialize():
133    """
134Initialize: call this first
135    """
136    Pm_Initialize()
137    Pt_Start(1, NULL, NULL) # /* equiv to TIME_START: start timer w/ ms accuracy */
138
139def Terminate():
140    """
141Terminate: call this to clean up Midi streams when done.
142If you do not call this on Windows machines when you are
143done with MIDI, your system may crash.
144    """
145    Pm_Terminate()
146
147def GetDefaultInputDeviceID():
148    return Pm_GetDefaultInputDeviceID()
149
150def GetDefaultOutputDeviceID():
151    return Pm_GetDefaultOutputDeviceID()
152
153def CountDevices():
154    return Pm_CountDevices()
155
156def GetDeviceInfo(i):
157    """
158GetDeviceInfo(<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
173def Time():
174    """
175Time() returns the current time in ms
176of the PortMidi timer
177    """
178    return Pt_Time()
179
180def GetErrorText(err):
181    """
182GetErrorText(<err num>) returns human-readable error
183messages translated from error numbers
184    """
185    return Pm_GetErrorText(err)
186
187def Channel(chan):
188    """
189Channel(<chan>) is used with ChannelMask on input MIDI streams.
190Example: to receive input on channels 1 and 10 on a MIDI
191         stream called MidiIn:
192MidiIn.SetChannelMask(pypm.Channel(1) | pypm.Channel(10))
193
194note: 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
201cdef class Output:
202    """
203class 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        """
260Close()
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        """
276Abort() 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        """
295Write(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        """
335WriteShort(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
394cdef class Input:
395    """
396class 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        """
432Close()
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        """
516Read(length): returns up to <length> midi events stored in
517the buffer and returns them as a list:
518[[[status,data1,data2,data3],timestamp],
519 [[status,data1,data2,data3],timestamp],...]
520example: 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
538