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