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