diff options
author | 2015-01-10 16:58:11 +0100 | |
---|---|---|
committer | 2015-01-10 16:58:11 +0100 | |
commit | 1273e97e73d7856a668b4419aae27065c81b06be (patch) | |
tree | 49ef6f3811122b79ae93bad45756ab0a77f3a04e /3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx | |
parent | d103debef2830bd0e28b54348c21492f89c269c4 (diff) |
Placed full code of portmidi in 3rdparty folder (nw)
Diffstat (limited to '3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx')
-rw-r--r-- | 3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx b/3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx new file mode 100644 index 00000000000..e43bd2b28b4 --- /dev/null +++ b/3rdparty/portmidi/pm_python/pyportmidi/_pyportmidi.pyx @@ -0,0 +1,537 @@ +# pyPortMidi +# Python bindings for PortMidi +# John Harrison +# http://sound.media.mit.edu/~harrison +# harrison@media.mit.edu +# written in Pyrex +__version__="0.07" + +import array + +# CHANGES: + +# 0.0.5: (June 1st, 2009) +# Output no longer calls abort when it deallocates. +# Added abort and close methods. +# Need to call Abort() explicityly if you want that to happen. + + +# +# 0.0.3: (March 15, 2005) +# changed everything from tuples to lists +# return 4 values for PmRead instead of 3 (for SysEx) +# minor fixes for flexibility and error checking +# flushed out DistUtils package and added Mac and Linux compile support +# Markus Pfaff: added ability for WriteSysEx to accept lists as well +# as strings + +# 0.0.2: +# fixed pointer to function calls to avoid necessity of pyport library + +# 0.0.1: +# initial release + +cdef extern from "portmidi.h": + ctypedef enum PmError: + pmNoError = 0, + pmHostError = -10000, + pmInvalidDeviceId, #/* out of range or output device when input is requested or vice versa */ + pmInsufficientMemory, + pmBufferTooSmall, + pmBufferOverflow, + pmBadPtr, + pmBadData, #/* illegal midi data, e.g. missing EOX */ + pmInternalError, + pmBufferMaxSize, #/* buffer is already as large as it can be */ + PmError Pm_Initialize() + PmError Pm_Terminate() + ctypedef void PortMidiStream + ctypedef PortMidiStream PmStream # CHECK THIS! + ctypedef int PmDeviceID + int Pm_HasHostError( PortMidiStream * stream ) + char *Pm_GetErrorText( PmError errnum ) + Pm_GetHostErrorText(char * msg, unsigned int len) + ctypedef struct PmDeviceInfo: + int structVersion + char *interf #/* underlying MIDI API, e.g. MMSystem or DirectX */ + char *name #/* device name, e.g. USB MidiSport 1x1 */ + int input #/* true iff input is available */ + int output #/* true iff output is available */ + int opened #/* used by generic PortMidi code to do error checking on arguments */ + int Pm_CountDevices() + PmDeviceID Pm_GetDefaultInputDeviceID() + PmDeviceID Pm_GetDefaultOutputDeviceID() + ctypedef long PmTimestamp + ctypedef PmTimestamp (*PmTimeProcPtr)(void *time_info) + #PmBefore is not defined... + PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) + PmError Pm_OpenInput( PortMidiStream** stream, + PmDeviceID inputDevice, + void *inputDriverInfo, + long bufferSize, + long (*PmPtr) (), # long = PtTimestamp + void *time_info ) + PmError Pm_OpenOutput( PortMidiStream** stream, + PmDeviceID outputDevice, + void *outputDriverInfo, + long bufferSize, + #long (*PmPtr) (), # long = PtTimestamp + PmTimeProcPtr time_proc, # long = PtTimestamp + void *time_info, + long latency ) + PmError Pm_SetFilter( PortMidiStream* stream, long filters ) + PmError Pm_Abort( PortMidiStream* stream ) + PmError Pm_Close( PortMidiStream* stream ) + ctypedef long PmMessage + ctypedef struct PmEvent: + PmMessage message + PmTimestamp timestamp + PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ) + PmError Pm_Poll( PortMidiStream *stream) + int Pm_Channel(int channel) + PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) + PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ) + PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg) + +cdef extern from "porttime.h": + ctypedef enum PtError: + ptNoError = 0, + ptHostError = -10000, + ptAlreadyStarted, + ptAlreadyStopped, + ptInsufficientMemory + ctypedef long PtTimestamp + ctypedef void (* PtCallback)( PtTimestamp timestamp, void *userData ) + PtError Pt_Start(int resolution, PtCallback *callback, void *userData) + PtTimestamp Pt_Time() + +FILT_ACTIVE=0x1 +FILT_SYSEX=0x2 +FILT_CLOCK=0x4 +FILT_PLAY=0x8 +FILT_F9=0x10 +FILT_TICK=0x10 +FILT_FD=0x20 +FILT_UNDEFINED=0x30 +FILT_RESET=0x40 +FILT_REALTIME=0x7F +FILT_NOTE=0x80 +FILT_CHANNEL_AFTERTOUCH=0x100 +FILT_POLY_AFTERTOUCH=0x200 +FILT_AFTERTOUCH=0x300 +FILT_PROGRAM=0x400 +FILT_CONTROL=0x800 +FILT_PITCHBEND=0x1000 +FILT_MTC=0x2000 +FILT_SONG_POSITION=0x4000 +FILT_SONG_SELECT=0x8000 +FILT_TUNE=0x10000 +FALSE=0 +TRUE=1 + +def Initialize(): + """ +Initialize: call this first + """ + Pm_Initialize() + Pt_Start(1, NULL, NULL) # /* equiv to TIME_START: start timer w/ ms accuracy */ + +def Terminate(): + """ +Terminate: call this to clean up Midi streams when done. +If you do not call this on Windows machines when you are +done with MIDI, your system may crash. + """ + Pm_Terminate() + +def GetDefaultInputDeviceID(): + return Pm_GetDefaultInputDeviceID() + +def GetDefaultOutputDeviceID(): + return Pm_GetDefaultOutputDeviceID() + +def CountDevices(): + return Pm_CountDevices() + +def GetDeviceInfo(i): + """ +GetDeviceInfo(<device number>): returns 5 parameters + - underlying MIDI API + - device name + - TRUE iff input is available + - TRUE iff output is available + - TRUE iff device stream is already open + """ + cdef PmDeviceInfo *info + + # disregarding the constness from Pm_GetDeviceInfo, since pyrex doesn't do const. + info = <PmDeviceInfo *>Pm_GetDeviceInfo(i) + + if info <> NULL: return info.interf, info.name, info.input, info.output, info.opened + else: return + +def Time(): + """ +Time() returns the current time in ms +of the PortMidi timer + """ + return Pt_Time() + +def GetErrorText(err): + """ +GetErrorText(<err num>) returns human-readable error +messages translated from error numbers + """ + return Pm_GetErrorText(err) + +def Channel(chan): + """ +Channel(<chan>) is used with ChannelMask on input MIDI streams. +Example: to receive input on channels 1 and 10 on a MIDI + stream called MidiIn: +MidiIn.SetChannelMask(pypm.Channel(1) | pypm.Channel(10)) + +note: PyPortMidi Channel function has been altered from + the original PortMidi c call to correct for what + seems to be a bug --- i.e. channel filters were + all numbered from 0 to 15 instead of 1 to 16. + """ + return Pm_Channel(chan-1) + +cdef class Output: + """ +class Output: + define an output MIDI stream. Takes the form: + x = pypm.Output(MidiOutputDevice, latency) + latency is in ms. + If latency = 0 then timestamps for output are ignored. + """ + cdef int i + cdef PmStream *midi + cdef int debug + cdef int _aborted + + def __init__(self, OutputDevice, latency=0): + + cdef PmError err + #cdef PtTimestamp (*PmPtr) () + cdef PmTimeProcPtr PmPtr + + self.i = OutputDevice + self.debug = 0 + self._aborted = 0 + + if latency == 0: + PmPtr = NULL + else: + PmPtr = <PmTimeProcPtr>&Pt_Time + if self.debug: print "Opening Midi Output" + # Why is bufferSize 0 here? + err = Pm_OpenOutput(&(self.midi), self.i, NULL, 0, PmPtr, NULL, latency) + if err < 0: + s = Pm_GetErrorText(err) + # Something's amiss here - if we try to throw an Exception + # here, we crash. + if not err == -10000: + raise Exception,s + else: + print "Unable to open Midi OutputDevice=",OutputDevice," err=",s + + def __dealloc__(self): + if self.debug: print "Closing MIDI output stream and destroying instance" + #err = Pm_Abort(self.midi) + #if err < 0: raise Exception, Pm_GetErrorText(err) + err = Pm_Close(self.midi) + if err < 0: raise Exception, Pm_GetErrorText(err) + + + def _check_open(self): + """ checks to see if the midi is open, and if not, raises an error. + """ + + if self.midi == NULL: + raise Exception, "midi Output not open." + + if self._aborted: + raise Exception, "midi Output aborted. Need to call Close after Abort." + + def Close(self): + """ +Close() + closes a midi stream, flushing any pending buffers. + (PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows.) + """ + #if not self.midi: + # return + + err = Pm_Close(self.midi) + if err < 0: + raise Exception, Pm_GetErrorText(err) + #self.midi = NULL + + + def Abort(self): + """ +Abort() terminates outgoing messages immediately + The caller should immediately close the output port; + this call may result in transmission of a partial midi message. + There is no abort for Midi input because the user can simply + ignore messages in the buffer and close an input device at + any time. + """ + #if not self.midi: + # return + + err = Pm_Abort(self.midi) + if err < 0: + raise Exception, Pm_GetErrorText(err) + + self._aborted = 1 + + + def Write(self, data): + """ +Write(data) + output a series of MIDI information in the form of a list: + Write([[[status <,data1><,data2><,data3>],timestamp], + [[status <,data1><,data2><,data3>],timestamp],...]) + <data> fields are optional + example: choose program change 1 at time 20000 and + send note 65 with velocity 100 500 ms later. + Write([[[0xc0,0,0],20000],[[0x90,60,100],20500]]) + notes: + 1. timestamps will be ignored if latency = 0. + 2. To get a note to play immediately, send MIDI info with + timestamp read from function Time. + 3. understanding optional data fields: + Write([[[0xc0,0,0],20000]]) is equivalent to + Write([[[0xc0],20000]]) + """ + cdef PmEvent buffer[1024] + cdef PmError err + cdef int i + + self._check_open() + + + if len(data) > 1024: raise IndexError, 'maximum list length is 1024' + else: + for loop1 in range(len(data)): + if ((len(data[loop1][0]) > 4) | + (len(data[loop1][0]) < 1)): + raise IndexError, str(len(data[loop1][0]))+' arguments in event list' + buffer[loop1].message = 0 + for i in range(len(data[loop1][0])): + buffer[loop1].message = buffer[loop1].message + ((data[loop1][0][i]&0xFF) << (8*i)) + buffer[loop1].timestamp = data[loop1][1] + if self.debug: print loop1," : ",buffer[loop1].message," : ",buffer[loop1].timestamp + if self.debug: print "writing to midi buffer" + err= Pm_Write(self.midi, buffer, len(data)) + if err < 0: raise Exception, Pm_GetErrorText(err) + + def WriteShort(self, status, data1 = 0, data2 = 0): + """ +WriteShort(status <, data1><, data2>) + output MIDI information of 3 bytes or less. + data fields are optional + status byte could be: + 0xc0 = program change + 0x90 = note on + etc. + data bytes are optional and assumed 0 if omitted + example: note 65 on with velocity 100 + WriteShort(0x90,65,100) + """ + cdef PmEvent buffer[1] + cdef PmError err + self._check_open() + + buffer[0].timestamp = Pt_Time() + buffer[0].message = ((((data2) << 16) & 0xFF0000) | (((data1) << 8) & 0xFF00) | ((status) & 0xFF)) + if self.debug: print "Writing to MIDI buffer" + err = Pm_Write(self.midi, buffer, 1) # stream, buffer, length + if err < 0 : raise Exception, Pm_GetErrorText(err) + + def WriteSysEx(self, when, msg): + """ + WriteSysEx(<timestamp>,<msg>) + writes a timestamped system-exclusive midi message. + <msg> can be a *list* or a *string* + example: + (assuming y is an input MIDI stream) + y.WriteSysEx(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7') + is equivalent to + y.WriteSysEx(pypm.Time, + [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) + """ + cdef PmError err + cdef char *cmsg + cdef PtTimestamp CurTime + + self._check_open() + + if type(msg) is list: + msg = array.array('B',msg).tostring() # Markus Pfaff contribution + cmsg = msg + + CurTime = Pt_Time() + err = Pm_WriteSysEx(self.midi, when, <unsigned char *> cmsg) + if err < 0 : raise Exception, Pm_GetErrorText(err) + while Pt_Time() == CurTime: # wait for SysEx to go thru or...my + pass # win32 machine crashes w/ multiple SysEx + + + + + + + + + + + +cdef class Input: + """ +class Input: + define an input MIDI stream. Takes the form: + x = pypm.Input(MidiInputDevice) + """ + cdef PmStream *midi + cdef int debug + cdef int i + + def __init__(self, InputDevice, buffersize=4096): + cdef PmError err + self.i = InputDevice + self.debug = 0 + err= Pm_OpenInput(&(self.midi),self.i,NULL,buffersize,&Pt_Time,NULL) + if err < 0: raise Exception, Pm_GetErrorText(err) + if self.debug: print "MIDI input opened." + + def __dealloc__(self): + cdef PmError err + if self.debug: print "Closing MIDI input stream and destroying instance" + + err = Pm_Close(self.midi) + if err < 0: + raise Exception, Pm_GetErrorText(err) + + + + def _check_open(self): + """ checks to see if the midi is open, and if not, raises an error. + """ + + if self.midi == NULL: + raise Exception, "midi Input not open." + + + def Close(self): + """ +Close() + closes a midi stream, flushing any pending buffers. + (PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows.) + """ + #if not self.midi: + # return + + err = Pm_Close(self.midi) + if err < 0: + raise Exception, Pm_GetErrorText(err) + #self.midi = NULL + + + + def SetFilter(self, filters): + """ + SetFilter(<filters>) sets filters on an open input stream + to drop selected input types. By default, only active sensing + messages are filtered. To prohibit, say, active sensing and + sysex messages, call + SetFilter(stream, FILT_ACTIVE | FILT_SYSEX); + + Filtering is useful when midi routing or midi thru functionality + is being provided by the user application. + For example, you may want to exclude timing messages + (clock, MTC, start/stop/continue), while allowing note-related + messages to pass. Or you may be using a sequencer or drum-machine + for MIDI clock information but want to exclude any notes + it may play. + + Note: SetFilter empties the buffer after setting the filter, + just in case anything got through. + """ + cdef PmEvent buffer[1] + cdef PmError err + + self._check_open() + + + err = Pm_SetFilter(self.midi, filters) + + if err < 0: raise Exception, Pm_GetErrorText(err) + + while(Pm_Poll(self.midi) != pmNoError): + + err = Pm_Read(self.midi,buffer,1) + if err < 0: raise Exception, Pm_GetErrorText(err) + + def SetChannelMask(self, mask): + """ + SetChannelMask(<mask>) filters incoming messages based on channel. + The mask is a 16-bit bitfield corresponding to appropriate channels + Channel(<channel>) can assist in calling this function. + i.e. to set receive only input on channel 1, call with + SetChannelMask(Channel(1)) + Multiple channels should be OR'd together, like + SetChannelMask(Channel(10) | Channel(11)) + note: PyPortMidi Channel function has been altered from + the original PortMidi c call to correct for what + seems to be a bug --- i.e. channel filters were + all numbered from 0 to 15 instead of 1 to 16. + """ + cdef PmError err + + self._check_open() + + err = Pm_SetChannelMask(self.midi,mask) + if err < 0: raise Exception, Pm_GetErrorText(err) + + def Poll(self): + """ + Poll tests whether input is available, + returning TRUE, FALSE, or an error value. + """ + cdef PmError err + self._check_open() + + err = Pm_Poll(self.midi) + if err < 0: raise Exception, Pm_GetErrorText(err) + return err + + def Read(self,length): + """ +Read(length): returns up to <length> midi events stored in +the buffer and returns them as a list: +[[[status,data1,data2,data3],timestamp], + [[status,data1,data2,data3],timestamp],...] +example: Read(50) returns all the events in the buffer, + up to 50 events. + """ + cdef PmEvent buffer[1024] + + self._check_open() + + x = [] + + if length > 1024: raise IndexError, 'maximum buffer length is 1024' + if length < 1: raise IndexError, 'minimum buffer length is 1' + NumEvents = Pm_Read(self.midi,buffer,length) + if NumEvents < 0: raise Exception, Pm_GetErrorText(NumEvents) + x=[] + if NumEvents >= 1: + for loop in range(NumEvents): + x.append([[buffer[loop].message & 0xff, (buffer[loop].message >> 8) & 0xFF, (buffer[loop].message >> 16) & 0xFF, (buffer[loop].message >> 24) & 0xFF], buffer[loop].timestamp]) + return x |