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
8# cython: language_level=2
9
10__version__ = "0.0.6"
11
12import array
13import sys
14
15# CHANGES:
16
17# 0.0.6: (Feb 25, 2011) christopher arndt <chris@chrisarndt.de>
18#   Do not try to close device in Input/Output.__dealloc__ if not open
19#   Major code layout clean up
20
21# 0.0.5: (June 1st, 2009)
22#   Output no longer calls abort when it deallocates.
23#   Added abort and close methods.
24#   Need to call Abort() explicityly if you want that to happen.
25
26
27#
28# 0.0.3: (March 15, 2005)
29#   changed everything from tuples to lists
30#   return 4 values for PmRead instead of 3 (for SysEx)
31#   minor fixes for flexibility and error checking
32#   flushed out DistUtils package and added Mac and Linux compile support
33#   Markus Pfaff: added ability for WriteSysEx to accept lists as well
34#                 as strings
35
36# 0.0.2:
37#   fixed pointer to function calls to avoid necessity of pyport library
38
39# 0.0.1:
40#   initial release
41
42
43FILT_ACTIVE = 0x1
44FILT_SYSEX = 0x2
45FILT_CLOCK = 0x4
46FILT_PLAY = 0x8
47FILT_F9 = 0x10
48FILT_TICK = 0x10
49FILT_FD = 0x20
50FILT_UNDEFINED = 0x30
51FILT_RESET = 0x40
52FILT_REALTIME = 0x7F
53FILT_NOTE = 0x80
54FILT_CHANNEL_AFTERTOUCH = 0x100
55FILT_POLY_AFTERTOUCH = 0x200
56FILT_AFTERTOUCH = 0x300
57FILT_PROGRAM = 0x400
58FILT_CONTROL = 0x800
59FILT_PITCHBEND = 0x1000
60FILT_MTC = 0x2000
61FILT_SONG_POSITION = 0x4000
62FILT_SONG_SELECT = 0x8000
63FILT_TUNE = 0x10000
64FALSE = 0
65TRUE = 1
66
67cdef extern from "portmidi.h":
68    ctypedef enum PmError:
69        pmNoError = 0,
70        pmHostError = -10000,
71        pmInvalidDeviceId, # out of range or output device when input is requested or vice versa
72        pmInsufficientMemory,
73        pmBufferTooSmall,
74        pmBufferOverflow,
75        pmBadPtr,
76        pmBadData, # illegal midi data, e.g. missing EOX
77        pmInternalError,
78        pmBufferMaxSize, # buffer is already as large as it can be
79
80    PmError Pm_Initialize()
81    PmError Pm_Terminate()
82    ctypedef void PortMidiStream
83    ctypedef PortMidiStream PmStream # CHECK THIS!
84    ctypedef int PmDeviceID
85    int Pm_HasHostError(PortMidiStream * stream)
86    char *Pm_GetErrorText(PmError errnum)
87    Pm_GetHostErrorText(char * msg, unsigned int len)
88
89    ctypedef struct PmDeviceInfo:
90        int structVersion
91        char *interf # underlying MIDI API, e.g. MMSystem or DirectX
92        char *name   # device name, e.g. USB MidiSport 1x1
93        int input    # true iff input is available
94        int output   # true iff output is available
95        int opened   # used by generic PortMidi code to do error checking on arguments
96
97    int Pm_CountDevices()
98    PmDeviceID Pm_GetDefaultInputDeviceID()
99    PmDeviceID Pm_GetDefaultOutputDeviceID()
100    ctypedef long PmTimestamp
101    ctypedef PmTimestamp(*PmTimeProcPtr)(void *time_info)
102    # PmBefore is not defined...
103    PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id)
104
105    PmError Pm_OpenInput(PortMidiStream** stream,
106                         PmDeviceID inputDevice,
107                         void *inputDriverInfo,
108                         long bufferSize,
109                         long (*PmPtr) (), # long = PtTimestamp
110                         void *time_info)
111
112    PmError Pm_OpenOutput(PortMidiStream** stream,
113                          PmDeviceID outputDevice,
114                          void *outputDriverInfo,
115                          long bufferSize,
116                          #long (*PmPtr) (), # long = PtTimestamp
117                          PmTimeProcPtr time_proc, # long = PtTimestamp
118                          void *time_info,
119                          long latency)
120
121    PmError Pm_SetFilter(PortMidiStream* stream, long filters)
122    PmError Pm_Abort(PortMidiStream* stream)
123    PmError Pm_Close(PortMidiStream* stream)
124    ctypedef long PmMessage
125
126    ctypedef struct PmEvent:
127        PmMessage message
128        PmTimestamp timestamp
129
130    PmError Pm_Read(PortMidiStream *stream, PmEvent *buffer, long length)
131    PmError Pm_Poll(PortMidiStream *stream)
132    int Pm_Channel(int channel)
133    PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
134    PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, long length)
135    PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when,
136                          unsigned char *msg)
137
138
139cdef extern from "porttime.h":
140    ctypedef enum PtError:
141        ptNoError = 0,
142        ptHostError = -10000,
143        ptAlreadyStarted,
144        ptAlreadyStopped,
145        ptInsufficientMemory
146
147    ctypedef long PtTimestamp
148    ctypedef void (* PtCallback)(PtTimestamp timestamp, void *userData)
149    PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
150    PtTimestamp Pt_Time()
151
152cdef long _pypm_initialized
153
154def Initialize():
155    """Initialize PortMidi library.
156
157    This function must be called once before any other function or class from
158    this module can be used.
159
160    """
161    Pm_Initialize()
162    # equiv to TIME_START: start timer w/ ms accuracy
163    Pt_Start(1, NULL, NULL)
164    _pypm_initialized = 1
165
166def Terminate():
167    """Terminate use of PortMidi library.
168
169    Call this to clean up Midi streams when done.
170
171    If you do not call this on Windows machines when you are done with MIDI,
172    your system may crash.
173
174    """
175    Pm_Terminate()
176    _pypm_initialized = 0
177
178
179def GetDefaultInputDeviceID():
180    """Return the number of the default MIDI input device.
181
182    See the PortMidi documentation on how the default device is set and
183    determined.
184
185    """
186    return Pm_GetDefaultInputDeviceID()
187
188def GetDefaultOutputDeviceID():
189    """Return the number of the default MIDI output device.
190
191    See the PortMidi documentation on how the default device is set and
192    determined.
193
194    """
195    return Pm_GetDefaultOutputDeviceID()
196
197def CountDevices():
198    """Return number of available MIDI (input and output) devices."""
199
200    return Pm_CountDevices()
201
202def GetDeviceInfo(device_no):
203    """Return device info tuple for MIDI device given by device_no.
204
205    The returned tuple has the following five items:
206
207    * underlying MIDI API (string)
208    * device name (string)
209    * whether device can be opened as input (1) or not (0)
210    * whether device can be opened as output (1) or not (0)
211    * whether device is currently opened (1) or not (0)
212
213    """
214    cdef PmDeviceInfo *info
215
216    # disregarding the constness from Pm_GetDeviceInfo,
217    # since pyrex doesn't do const.
218    info = <PmDeviceInfo *>Pm_GetDeviceInfo(device_no)
219
220    if info != NULL:
221        return info.interf, info.name, info.input, info.output, info.opened
222    # return None
223
224def Time():
225    """Return the current time in ms of the PortMidi timer."""
226
227    return Pt_Time()
228
229def GetErrorText(err):
230    """Return human-readable error message translated from error number."""
231
232    return Pm_GetErrorText(err)
233
234def Channel(chan):
235    """Return Channel object for given MIDI channel number 1 - 16.
236
237    Channel(<chan>) is used with ChannelMask on input MIDI streams.
238
239    Example:
240
241    To receive input on channels 1 and 10 on a MIDI stream called
242    MidiIn::
243
244        MidiIn.SetChannelMask(pypm.Channel(1) | pypm.Channel(10))
245
246    .. note::
247        PyPortMidi Channel function has been altered from
248        the original PortMidi c call to correct for what
249        seems to be a bug --- i.e. channel filters were
250        all numbered from 0 to 15 instead of 1 to 16.
251
252    """
253    return Pm_Channel(chan - 1)
254
255
256cdef class Output:
257    """Represents an output MIDI stream device.
258
259    Takes the form::
260
261        output = pypm.Output(output_device, latency)
262
263    latency is in ms. If latency == 0 then timestamps for output are ignored.
264
265    """
266    cdef int device
267    cdef PmStream *midi
268    cdef int debug
269    cdef int _aborted
270
271    def __init__(self, output_device, latency=0, buffer_size=256):
272        """Instantiate MIDI output stream object."""
273
274        cdef PmError err
275        #cdef PtTimestamp (*PmPtr) ()
276        cdef PmTimeProcPtr PmPtr
277        cdef const char * errmsg
278
279        self.device = output_device
280        self.debug = 0
281        self._aborted = 0
282
283        if latency == 0:
284            PmPtr = NULL
285        else:
286            PmPtr = <PmTimeProcPtr>&Pt_Time
287
288        if self.debug:
289            print "Opening Midi Output", output_device
290
291        err = Pm_OpenOutput(&(self.midi), output_device, NULL, buffer_size,
292                            PmPtr, NULL, latency)
293        if self.debug:
294            print "Pm_OpenOutput err", err
295
296        if err < 0:
297            errmsg = Pm_GetErrorText(err)
298            # Something's amiss here - if we try to throw an Exception
299            # here, we crash.
300            if not err == -10000:
301                raise Exception(errmsg)
302            else:
303                print "Unable to open Midi OutputDevice=%i: %s" % (
304                    output_device, errmsg)
305
306    def __dealloc__(self):
307        """Close midi device if still open when the instance is destroyed."""
308
309        cdef PmError err
310
311        if self.debug:
312            print "Closing MIDI output stream and destroying instance."
313
314        if self.midi and _pypm_initialized:
315            err = Pm_Close(self.midi)
316            if err < 0:
317                raise Exception(Pm_GetErrorText(err))
318
319    def _check_open(self):
320        """Check whether midi device is open, and if not, raises an error.
321
322        Internal method, should be used only by other methods of this class.
323
324        """
325
326        if self.midi == NULL:
327            raise Exception("midi Output not open.")
328
329        if self._aborted:
330            raise Exception(
331                "midi Output aborted. Need to call Close after Abort.")
332
333    def Close(self):
334        """Close the midi output device, flushing any pending buffers.
335
336        PortMidi attempts to close open streams when the application exits --
337        this is particularly difficult under Windows, so it is best to take
338        care to close all devices explicitly.
339
340        """
341        cdef PmError err
342
343        if not self.midi or not _pypm_initialized:
344            return
345
346        err = Pm_Close(self.midi)
347        if err < 0:
348            raise Exception(Pm_GetErrorText(err))
349
350        self.midi = NULL
351
352    def Abort(self):
353        """Terminate outgoing messages immediately.
354
355        The caller should immediately close the output port after calling this
356        method. This call may result in transmission of a partial midi message.
357        There is no abort for Midi input because the user can simply ignore
358        messages in the buffer and close an input device at any time.
359
360        """
361        cdef PmError err
362
363        if not self.midi:
364            return
365
366        err = Pm_Abort(self.midi)
367        if err < 0:
368            raise Exception(Pm_GetErrorText(err))
369
370        self._aborted = 1
371
372    def Write(self, data):
373        """Output a series of MIDI events given by data list n this device.
374
375        Usage::
376
377            Write([
378                [[status, data1, data2, data3], timestamp],
379                [[status, data1, data2, data3], timestamp],
380                ...
381            ])
382
383        The data1/2/3 items in each event are optional::
384
385           Write([[[0xc0, 0, 0], 20000]])
386
387        is equivalent to::
388
389           Write([[[0xc0], 20000]])
390
391        Example:
392
393        Send program change 1 at time 20000 and send note 65 with velocity 100
394        at 500 ms later::
395
396             Write([[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]])
397
398        .. notes::
399            1. Timestamps will be ignored if latency == 0.
400
401            2. To get a note to play immediately, send the note on event with
402               the result from the Time() function as the timestamp.
403
404        """
405        cdef PmEvent buffer[1024]
406        cdef PmError err
407        cdef int item
408        cdef int ev_no
409
410        self._check_open()
411
412        if len(data) > 1024:
413            raise IndexError('Maximum event list length is 1024.')
414        else:
415            for ev_no, event in enumerate(data):
416                if not event[0]:
417                    raise ValueError('No data in event no. %i.' % ev_no)
418                if len(event[0]) > 4:
419                    raise ValueError('Too many data bytes (%i) in event no. %i.'
420                        % (len(event[0]), ev_no))
421
422                buffer[ev_no].message = 0
423
424                for item in range(len(event[0])):
425                    buffer[ev_no].message += (
426                        (event[0][item] & 0xFF) << (8 * item))
427
428                buffer[ev_no].timestamp = event[1]
429
430                if self.debug:
431                    print "%i : %r : %s" % (
432                        ev_no, buffer[ev_no].message, buffer[ev_no].timestamp)
433
434        if self.debug:
435            print "Writing to midi buffer."
436        err = Pm_Write(self.midi, buffer, len(data))
437        if err < 0:
438            raise Exception(Pm_GetErrorText(err))
439
440    def WriteShort(self, status, data1=0, data2=0):
441        """Output MIDI event of three bytes or less immediately on this device.
442
443        Usage::
444
445            WriteShort(status, data1, data2)
446
447        status must be a valid MIDI status byte, for example:
448
449        0xCx = Program Change
450        0xBx = Controller Change
451        0x9x = Note On
452
453        where x is the MIDI channel number 0 - 0xF.
454
455        The data1 and data2 arguments are optional and assumed to be 0 if
456        omitted.
457
458        Example:
459
460        Send note 65 on with velocity 100::
461
462             WriteShort(0x90, 65, 100)
463
464        """
465        cdef PmEvent buffer[1]
466        cdef PmError err
467
468        self._check_open()
469
470        buffer[0].timestamp = Pt_Time()
471        buffer[0].message = (((data2 << 16) & 0xFF0000) |
472            ((data1 << 8) & 0xFF00) | (status & 0xFF))
473
474        if self.debug:
475            print "Writing to MIDI buffer."
476        err = Pm_Write(self.midi, buffer, 1) # stream, buffer, length
477        if err < 0:
478            raise Exception(Pm_GetErrorText(err))
479
480    def WriteSysEx(self, when, msg):
481        """Output a timestamped system-exclusive MIDI message on this device.
482
483        Usage::
484
485            WriteSysEx(<timestamp>, <msg>)
486
487        <msg> can be a *list* or a *string*
488
489        Example (assuming 'out' is an output MIDI stream):
490
491            out.WriteSysEx(0, '\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7')
492
493        This is equivalent to::
494
495            out.WriteSysEx(pypm.Time(),
496                [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7])
497
498        """
499        cdef PmError err
500        cdef char *cmsg
501        cdef PtTimestamp cur_time
502
503        self._check_open()
504
505        if type(msg) is list:
506            # Markus Pfaff contribution
507            arr = array.array('B', msg)
508            if sys.version_info[0] == 3:
509                msg = arr.tobytes()
510            else:
511                msg = arr.tostring()
512        cmsg = msg
513
514        cur_time = Pt_Time()
515        err = Pm_WriteSysEx(self.midi, when, <unsigned char *> cmsg)
516        if err < 0:
517            raise Exception(Pm_GetErrorText(err))
518
519        # wait for SysEx to go thru or...
520        # my win32 machine crashes w/ multiple SysEx
521        while Pt_Time() == cur_time:
522            pass
523
524
525cdef class Input:
526    """Represents an input MIDI stream device.
527
528    Takes the form::
529
530        input = pypm.Input(input_device)
531
532    """
533    cdef int device
534    cdef PmStream *midi
535    cdef int debug
536
537    def __init__(self, input_device, buffersize=4096):
538        """Instantiate MIDI input stream object."""
539
540        cdef PmError err
541        self.device = input_device
542        self.debug = 0
543
544        err = Pm_OpenInput(&(self.midi), input_device, NULL, buffersize,
545                           &Pt_Time, NULL)
546        if err < 0:
547            raise Exception(Pm_GetErrorText(err))
548
549        if self.debug:
550            print "MIDI input opened.", input_device
551
552    def __dealloc__(self):
553        """Close midi device if still open when the instance is destroyed."""
554
555        cdef PmError err
556        if not _pypm_initialized:
557            return
558
559        if self.debug:
560            print "Closing MIDI input stream and destroying instance"
561
562        if self.midi:
563            err = Pm_Close(self.midi)
564            if err < 0:
565                raise Exception(Pm_GetErrorText(err))
566
567    def _check_open(self):
568        """Check whether midi device is open, and if not, raises an error.
569
570        Internal method, should be used only by other methods of this class.
571
572        """
573        if self.midi == NULL:
574            raise Exception("midi Input not open.")
575
576    def Close(self):
577        """Close the midi input device.
578
579        PortMidi attempts to close open streams when the application exits --
580        this is particularly difficult under Windows, so it is best to take
581        care to close all devices explicitly.
582
583        """
584        cdef PmError err
585        if not _pypm_initialized:
586            return
587
588        if not self.midi:
589            return
590
591        if self.midi:
592            err = Pm_Close(self.midi)
593            if err < 0:
594                raise Exception(Pm_GetErrorText(err))
595
596        self.midi = NULL
597
598
599    def SetFilter(self, filters):
600        """Set filters on an open input stream.
601
602        Usage::
603
604            input.SetFilter(filters)
605
606        Filters are used to drop selected input event types. By default, only
607        active sensing messages are filtered. To prohibit, say, active sensing
608        and sysex messages, call
609
610        ::
611
612            input.SetFilter(FILT_ACTIVE | FILT_SYSEX);
613
614        Filtering is useful when midi routing or midi thru functionality is
615        being provided by the user application. For example, you may want to
616        exclude timing messages (clock, MTC, start/stop/continue), while
617        allowing note-related messages to pass. Or you may be using a sequencer
618        or drum-machine for MIDI clock information but want to exclude any
619        notes it may play.
620
621        .. note::
622            SetFilter empties the buffer after setting the filter,
623            just in case anything got through.
624
625        """
626        cdef PmEvent buffer[1]
627        cdef PmError err
628
629        self._check_open()
630
631        err = Pm_SetFilter(self.midi, filters)
632
633        if err < 0:
634            raise Exception(Pm_GetErrorText(err))
635
636        while(Pm_Poll(self.midi) != pmNoError):
637            err = Pm_Read(self.midi, buffer, 1)
638            if err < 0:
639                raise Exception(Pm_GetErrorText(err))
640
641    def SetChannelMask(self, mask):
642        """Set channel mask to filter incoming messages based on channel.
643
644        The mask is a 16-bit bitfield corresponding to appropriate channels
645        Channel(<channel>) can assist in calling this function, i.e. to
646        receive only input on channel 1, call this method like this::
647
648            SetChannelMask(Channel(1))
649
650        Multiple channels should be OR'd together::
651
652            SetChannelMask(Channel(10) | Channel(11))
653
654        .. note::
655            The PyPortMidi Channel function has been altered from the original
656            PortMidi C call to correct for what seems to be a bug --- i.e.
657            channel filters were all numbered from 0 to 15 instead of 1 to 16.
658
659        """
660        cdef PmError err
661
662        self._check_open()
663
664        err = Pm_SetChannelMask(self.midi, mask)
665        if err < 0:
666            raise Exception(Pm_GetErrorText(err))
667
668    def Poll(self):
669        """Test whether input is available.
670
671        Returns TRUE if input can be read, FALSE otherwise, or an error value.
672
673        """
674        cdef PmError err
675
676        self._check_open()
677
678        err = Pm_Poll(self.midi)
679        if err < 0:
680            raise Exception(Pm_GetErrorText(err))
681
682        return err
683
684    def Read(self, max_events):
685        """Read and return up to max_events events from input.
686
687        Reads up to max_events midi events stored in the input buffer and
688        returns them as a list in the following form::
689
690            [
691                [[status, data1, data2, data3], timestamp],
692                [[status, data1, data2, data3], timestamp],
693                ...
694            ]
695
696        """
697        cdef PmEvent buffer[1024]
698        cdef PmError num_events
699
700        self._check_open()
701
702        if max_events > 1024:
703            raise ValueError('Maximum buffer length is 1024.')
704        if not max_events:
705            raise ValueError('Minimum buffer length is 1.')
706
707        num_events = Pm_Read(self.midi, buffer, max_events)
708        if num_events < 0:
709            raise Exception(Pm_GetErrorText(num_events))
710
711        events = []
712        if num_events >= 1:
713            for ev_no in range(<int>num_events):
714                events.append(
715                    [
716                        [
717                            buffer[ev_no].message & 0xFF,
718                            (buffer[ev_no].message >> 8) & 0xFF,
719                            (buffer[ev_no].message >> 16) & 0xFF,
720                            (buffer[ev_no].message >> 24) & 0xFF
721                        ],
722                        buffer[ev_no].timestamp
723                    ]
724                )
725
726        return events
727