mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-10 16:43:33 +02:00
Update portmidi to SVN r227.
This commit is contained in:
3
lib-src/portmidi/pm_python/pyportmidi/__init__.py
Normal file
3
lib-src/portmidi/pm_python/pyportmidi/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
from .midi import *
|
||||
|
5663
lib-src/portmidi/pm_python/pyportmidi/_pyportmidi.c
Normal file
5663
lib-src/portmidi/pm_python/pyportmidi/_pyportmidi.c
Normal file
File diff suppressed because it is too large
Load Diff
537
lib-src/portmidi/pm_python/pyportmidi/_pyportmidi.pyx
Normal file
537
lib-src/portmidi/pm_python/pyportmidi/_pyportmidi.pyx
Normal file
@@ -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
|
566
lib-src/portmidi/pm_python/pyportmidi/midi.py
Normal file
566
lib-src/portmidi/pm_python/pyportmidi/midi.py
Normal file
@@ -0,0 +1,566 @@
|
||||
"""
|
||||
Module for interacting with midi input and output.
|
||||
|
||||
The midi module can send output to midi devices, and get input
|
||||
from midi devices. It can also list midi devices on the system.
|
||||
|
||||
Including real midi devices, and virtual ones.
|
||||
|
||||
It uses the portmidi library. Is portable to which ever platforms
|
||||
portmidi supports (currently windows, OSX, and linux).
|
||||
"""
|
||||
|
||||
import atexit
|
||||
|
||||
|
||||
|
||||
_init = False
|
||||
_pypm = None
|
||||
|
||||
|
||||
__all__ = [ "Input",
|
||||
"MidiException",
|
||||
"Output",
|
||||
"get_count",
|
||||
"get_default_input_id",
|
||||
"get_default_output_id",
|
||||
"get_device_info",
|
||||
"init",
|
||||
"quit",
|
||||
"time",
|
||||
]
|
||||
|
||||
__theclasses__ = ["Input", "Output"]
|
||||
|
||||
|
||||
def init():
|
||||
"""initialize the midi module
|
||||
pyportmidi.init(): return None
|
||||
|
||||
Call the initialisation function before using the midi module.
|
||||
|
||||
It is safe to call this more than once.
|
||||
"""
|
||||
global _init, _pypm
|
||||
if not _init:
|
||||
import pyportmidi._pyportmidi
|
||||
_pypm = pyportmidi._pyportmidi
|
||||
|
||||
_pypm.Initialize()
|
||||
_init = True
|
||||
atexit.register(quit)
|
||||
|
||||
|
||||
def quit():
|
||||
"""uninitialize the midi module
|
||||
pyportmidi.quit(): return None
|
||||
|
||||
|
||||
Called automatically atexit if you don't call it.
|
||||
|
||||
It is safe to call this function more than once.
|
||||
"""
|
||||
global _init, _pypm
|
||||
if _init:
|
||||
# TODO: find all Input and Output classes and close them first?
|
||||
_pypm.Terminate()
|
||||
_init = False
|
||||
del _pypm
|
||||
|
||||
def _check_init():
|
||||
if not _init:
|
||||
raise RuntimeError("pyportmidi not initialised.")
|
||||
|
||||
def get_count():
|
||||
"""gets the number of devices.
|
||||
pyportmidi.get_count(): return num_devices
|
||||
|
||||
|
||||
Device ids range from 0 to get_count() -1
|
||||
"""
|
||||
_check_init()
|
||||
return _pypm.CountDevices()
|
||||
|
||||
|
||||
|
||||
|
||||
def get_default_input_id():
|
||||
"""gets default input device number
|
||||
pyportmidi.get_default_input_id(): return default_id
|
||||
|
||||
|
||||
Return the default device ID or -1 if there are no devices.
|
||||
The result can be passed to the Input()/Ouput() class.
|
||||
|
||||
On the PC, the user can specify a default device by
|
||||
setting an environment variable. For example, to use device #1.
|
||||
|
||||
set PM_RECOMMENDED_INPUT_DEVICE=1
|
||||
|
||||
The user should first determine the available device ID by using
|
||||
the supplied application "testin" or "testout".
|
||||
|
||||
In general, the registry is a better place for this kind of info,
|
||||
and with USB devices that can come and go, using integers is not
|
||||
very reliable for device identification. Under Windows, if
|
||||
PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is
|
||||
*NOT* found in the environment, then the default device is obtained
|
||||
by looking for a string in the registry under:
|
||||
HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
|
||||
and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
|
||||
for a string. The number of the first device with a substring that
|
||||
matches the string exactly is returned. For example, if the string
|
||||
in the registry is "USB", and device 1 is named
|
||||
"In USB MidiSport 1x1", then that will be the default
|
||||
input because it contains the string "USB".
|
||||
|
||||
In addition to the name, get_device_info() returns "interf", which
|
||||
is the interface name. (The "interface" is the underlying software
|
||||
system or API used by PortMidi to access devices. Examples are
|
||||
MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.)
|
||||
At present, the only Win32 interface is "MMSystem", the only Linux
|
||||
interface is "ALSA", and the only Max OS X interface is "CoreMIDI".
|
||||
To specify both the interface and the device name in the registry,
|
||||
separate the two with a comma and a space, e.g.:
|
||||
MMSystem, In USB MidiSport 1x1
|
||||
In this case, the string before the comma must be a substring of
|
||||
the "interf" string, and the string after the space must be a
|
||||
substring of the "name" name string in order to match the device.
|
||||
|
||||
Note: in the current release, the default is simply the first device
|
||||
(the input or output device with the lowest PmDeviceID).
|
||||
"""
|
||||
return _pypm.GetDefaultInputDeviceID()
|
||||
|
||||
|
||||
|
||||
|
||||
def get_default_output_id():
|
||||
"""gets default output device number
|
||||
pyportmidi.get_default_output_id(): return default_id
|
||||
|
||||
|
||||
Return the default device ID or -1 if there are no devices.
|
||||
The result can be passed to the Input()/Ouput() class.
|
||||
|
||||
On the PC, the user can specify a default device by
|
||||
setting an environment variable. For example, to use device #1.
|
||||
|
||||
set PM_RECOMMENDED_OUTPUT_DEVICE=1
|
||||
|
||||
The user should first determine the available device ID by using
|
||||
the supplied application "testin" or "testout".
|
||||
|
||||
In general, the registry is a better place for this kind of info,
|
||||
and with USB devices that can come and go, using integers is not
|
||||
very reliable for device identification. Under Windows, if
|
||||
PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is
|
||||
*NOT* found in the environment, then the default device is obtained
|
||||
by looking for a string in the registry under:
|
||||
HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
|
||||
and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
|
||||
for a string. The number of the first device with a substring that
|
||||
matches the string exactly is returned. For example, if the string
|
||||
in the registry is "USB", and device 1 is named
|
||||
"In USB MidiSport 1x1", then that will be the default
|
||||
input because it contains the string "USB".
|
||||
|
||||
In addition to the name, get_device_info() returns "interf", which
|
||||
is the interface name. (The "interface" is the underlying software
|
||||
system or API used by PortMidi to access devices. Examples are
|
||||
MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.)
|
||||
At present, the only Win32 interface is "MMSystem", the only Linux
|
||||
interface is "ALSA", and the only Max OS X interface is "CoreMIDI".
|
||||
To specify both the interface and the device name in the registry,
|
||||
separate the two with a comma and a space, e.g.:
|
||||
MMSystem, In USB MidiSport 1x1
|
||||
In this case, the string before the comma must be a substring of
|
||||
the "interf" string, and the string after the space must be a
|
||||
substring of the "name" name string in order to match the device.
|
||||
|
||||
Note: in the current release, the default is simply the first device
|
||||
(the input or output device with the lowest PmDeviceID).
|
||||
"""
|
||||
_check_init()
|
||||
return _pypm.GetDefaultOutputDeviceID()
|
||||
|
||||
|
||||
def get_device_info(an_id):
|
||||
""" returns information about a midi device
|
||||
pyportmidi.get_device_info(an_id): return (interf, name, input, output, opened)
|
||||
|
||||
interf - a text string describing the device interface, eg 'ALSA'.
|
||||
name - a text string for the name of the device, eg 'Midi Through Port-0'
|
||||
input - 0, or 1 if the device is an input device.
|
||||
output - 0, or 1 if the device is an output device.
|
||||
opened - 0, or 1 if the device is opened.
|
||||
|
||||
If the id is out of range, the function returns None.
|
||||
"""
|
||||
_check_init()
|
||||
return _pypm.GetDeviceInfo(an_id)
|
||||
|
||||
|
||||
class Input(object):
|
||||
"""Input is used to get midi input from midi devices.
|
||||
Input(device_id)
|
||||
Input(device_id, buffer_size)
|
||||
|
||||
buffer_size -the number of input events to be buffered waiting to
|
||||
be read using Input.read()
|
||||
"""
|
||||
|
||||
def __init__(self, device_id, buffer_size=4096):
|
||||
"""
|
||||
The buffer_size specifies the number of input events to be buffered
|
||||
waiting to be read using Input.read().
|
||||
"""
|
||||
_check_init()
|
||||
|
||||
if device_id == -1:
|
||||
raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.")
|
||||
|
||||
try:
|
||||
r = get_device_info(device_id)
|
||||
except TypeError:
|
||||
raise TypeError("an integer is required")
|
||||
except OverflowError:
|
||||
raise OverflowError("long int too large to convert to int")
|
||||
|
||||
# and now some nasty looking error checking, to provide nice error
|
||||
# messages to the kind, lovely, midi using people of whereever.
|
||||
if r:
|
||||
interf, name, input, output, opened = r
|
||||
if input:
|
||||
try:
|
||||
self._input = _pypm.Input(device_id, buffer_size)
|
||||
except TypeError:
|
||||
raise TypeError("an integer is required")
|
||||
self.device_id = device_id
|
||||
|
||||
elif output:
|
||||
raise MidiException("Device id given is not a valid input id, it is an output id.")
|
||||
else:
|
||||
raise MidiException("Device id given is not a valid input id.")
|
||||
else:
|
||||
raise MidiException("Device id invalid, out of range.")
|
||||
|
||||
|
||||
|
||||
|
||||
def _check_open(self):
|
||||
if self._input is None:
|
||||
raise MidiException("midi not open.")
|
||||
|
||||
|
||||
|
||||
def close(self):
|
||||
""" closes a midi stream, flushing any pending buffers.
|
||||
Input.close(): return None
|
||||
|
||||
PortMidi attempts to close open streams when the application
|
||||
exits -- this is particularly difficult under Windows.
|
||||
"""
|
||||
_check_init()
|
||||
if not (self._input is None):
|
||||
self._input.Close()
|
||||
self._input = None
|
||||
|
||||
|
||||
|
||||
def read(self, num_events):
|
||||
"""reads num_events midi events from the buffer.
|
||||
Input.read(num_events): return midi_event_list
|
||||
|
||||
Reads from the Input buffer and gives back midi events.
|
||||
[[[status,data1,data2,data3],timestamp],
|
||||
[[status,data1,data2,data3],timestamp],...]
|
||||
"""
|
||||
_check_init()
|
||||
self._check_open()
|
||||
return self._input.Read(num_events)
|
||||
|
||||
|
||||
def poll(self):
|
||||
"""returns true if there's data, or false if not.
|
||||
Input.poll(): return Bool
|
||||
|
||||
raises a MidiException on error.
|
||||
"""
|
||||
_check_init()
|
||||
self._check_open()
|
||||
|
||||
r = self._input.Poll()
|
||||
if r == _pypm.TRUE:
|
||||
return True
|
||||
elif r == _pypm.FALSE:
|
||||
return False
|
||||
else:
|
||||
err_text = GetErrorText(r)
|
||||
raise MidiException( (r, err_text) )
|
||||
|
||||
|
||||
|
||||
|
||||
class Output(object):
|
||||
"""Output is used to send midi to an output device
|
||||
Output(device_id)
|
||||
Output(device_id, latency = 0)
|
||||
Output(device_id, buffer_size = 4096)
|
||||
Output(device_id, latency, buffer_size)
|
||||
|
||||
The buffer_size specifies the number of output events to be
|
||||
buffered waiting for output. (In some cases -- see below --
|
||||
PortMidi does not buffer output at all and merely passes data
|
||||
to a lower-level API, in which case buffersize is ignored.)
|
||||
|
||||
latency is the delay in milliseconds applied to timestamps to determine
|
||||
when the output should actually occur. (If latency is < 0, 0 is
|
||||
assumed.)
|
||||
|
||||
If latency is zero, timestamps are ignored and all output is delivered
|
||||
immediately. If latency is greater than zero, output is delayed until
|
||||
the message timestamp plus the latency. (NOTE: time is measured
|
||||
relative to the time source indicated by time_proc. Timestamps are
|
||||
absolute, not relative delays or offsets.) In some cases, PortMidi
|
||||
can obtain better timing than your application by passing timestamps
|
||||
along to the device driver or hardware. Latency may also help you
|
||||
to synchronize midi data to audio data by matching midi latency to
|
||||
the audio buffer latency.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, device_id, latency = 0, buffer_size = 4096):
|
||||
"""Output(device_id)
|
||||
Output(device_id, latency = 0)
|
||||
Output(device_id, buffer_size = 4096)
|
||||
Output(device_id, latency, buffer_size)
|
||||
|
||||
The buffer_size specifies the number of output events to be
|
||||
buffered waiting for output. (In some cases -- see below --
|
||||
PortMidi does not buffer output at all and merely passes data
|
||||
to a lower-level API, in which case buffersize is ignored.)
|
||||
|
||||
latency is the delay in milliseconds applied to timestamps to determine
|
||||
when the output should actually occur. (If latency is < 0, 0 is
|
||||
assumed.)
|
||||
|
||||
If latency is zero, timestamps are ignored and all output is delivered
|
||||
immediately. If latency is greater than zero, output is delayed until
|
||||
the message timestamp plus the latency. (NOTE: time is measured
|
||||
relative to the time source indicated by time_proc. Timestamps are
|
||||
absolute, not relative delays or offsets.) In some cases, PortMidi
|
||||
can obtain better timing than your application by passing timestamps
|
||||
along to the device driver or hardware. Latency may also help you
|
||||
to synchronize midi data to audio data by matching midi latency to
|
||||
the audio buffer latency.
|
||||
"""
|
||||
|
||||
_check_init()
|
||||
self._aborted = 0
|
||||
|
||||
if device_id == -1:
|
||||
raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.")
|
||||
|
||||
try:
|
||||
r = get_device_info(device_id)
|
||||
except TypeError:
|
||||
raise TypeError("an integer is required")
|
||||
except OverflowError:
|
||||
raise OverflowError("long int too large to convert to int")
|
||||
|
||||
# and now some nasty looking error checking, to provide nice error
|
||||
# messages to the kind, lovely, midi using people of whereever.
|
||||
if r:
|
||||
interf, name, input, output, opened = r
|
||||
if output:
|
||||
try:
|
||||
self._output = _pypm.Output(device_id, latency)
|
||||
except TypeError:
|
||||
raise TypeError("an integer is required")
|
||||
self.device_id = device_id
|
||||
|
||||
elif input:
|
||||
raise MidiException("Device id given is not a valid output id, it is an input id.")
|
||||
else:
|
||||
raise MidiException("Device id given is not a valid output id.")
|
||||
else:
|
||||
raise MidiException("Device id invalid, out of range.")
|
||||
|
||||
def _check_open(self):
|
||||
if self._output is None:
|
||||
raise MidiException("midi not open.")
|
||||
|
||||
if self._aborted:
|
||||
raise MidiException("midi aborted.")
|
||||
|
||||
|
||||
def close(self):
|
||||
""" closes a midi stream, flushing any pending buffers.
|
||||
Output.close(): return None
|
||||
|
||||
PortMidi attempts to close open streams when the application
|
||||
exits -- this is particularly difficult under Windows.
|
||||
"""
|
||||
_check_init()
|
||||
if not (self._output is None):
|
||||
self._output.Close()
|
||||
self._output = None
|
||||
|
||||
def abort(self):
|
||||
"""terminates outgoing messages immediately
|
||||
Output.abort(): return None
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
_check_init()
|
||||
if self._output:
|
||||
self._output.Abort()
|
||||
self._aborted = 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def write(self, data):
|
||||
"""writes a list of midi data to the Output
|
||||
Output.write(data)
|
||||
|
||||
writes 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]])
|
||||
|
||||
Can send up to 1024 elements in your data list, otherwise an
|
||||
IndexError exception is raised.
|
||||
"""
|
||||
_check_init()
|
||||
self._check_open()
|
||||
|
||||
self._output.Write(data)
|
||||
|
||||
|
||||
def write_short(self, status, data1 = 0, data2 = 0):
|
||||
"""write_short(status <, data1><, data2>)
|
||||
Output.write_short(status)
|
||||
Output.write_short(status, data1 = 0, data2 = 0)
|
||||
|
||||
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
|
||||
write_short(0x90,65,100)
|
||||
"""
|
||||
_check_init()
|
||||
self._check_open()
|
||||
self._output.WriteShort(status, data1, data2)
|
||||
|
||||
|
||||
def write_sys_ex(self, when, msg):
|
||||
"""writes a timestamped system-exclusive midi message.
|
||||
Output.write_sys_ex(when, msg)
|
||||
|
||||
msg - can be a *list* or a *string*
|
||||
when - a timestamp in miliseconds
|
||||
example:
|
||||
(assuming o is an onput MIDI stream)
|
||||
o.write_sys_ex(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7')
|
||||
is equivalent to
|
||||
o.write_sys_ex(pyportmidi.time(),
|
||||
[0xF0,0x7D,0x10,0x11,0x12,0x13,0xF7])
|
||||
"""
|
||||
_check_init()
|
||||
self._check_open()
|
||||
self._output.WriteSysEx(when, msg)
|
||||
|
||||
|
||||
def note_on(self, note, velocity=None, channel = 0):
|
||||
"""turns a midi note on. Note must be off.
|
||||
Output.note_on(note, velocity=None, channel = 0)
|
||||
|
||||
Turn a note on in the output stream. The note must already
|
||||
be off for this to work correctly.
|
||||
"""
|
||||
if velocity is None:
|
||||
velocity = 0
|
||||
|
||||
if not (0 <= channel <= 15):
|
||||
raise ValueError("Channel not between 0 and 15.")
|
||||
|
||||
self.write_short(0x90+channel, note, velocity)
|
||||
|
||||
def note_off(self, note, velocity=None, channel = 0):
|
||||
"""turns a midi note off. Note must be on.
|
||||
Output.note_off(note, velocity=None, channel = 0)
|
||||
|
||||
Turn a note off in the output stream. The note must already
|
||||
be on for this to work correctly.
|
||||
"""
|
||||
if velocity is None:
|
||||
velocity = 0
|
||||
|
||||
if not (0 <= channel <= 15):
|
||||
raise ValueError("Channel not between 0 and 15.")
|
||||
|
||||
self.write_short(0x80 + channel, note, velocity)
|
||||
|
||||
|
||||
def set_instrument(self, instrument_id, channel = 0):
|
||||
"""select an instrument, with a value between 0 and 127
|
||||
Output.set_instrument(instrument_id, channel = 0)
|
||||
|
||||
"""
|
||||
if not (0 <= instrument_id <= 127):
|
||||
raise ValueError("Undefined instrument id: %d" % instrument_id)
|
||||
|
||||
if not (0 <= channel <= 15):
|
||||
raise ValueError("Channel not between 0 and 15.")
|
||||
|
||||
self.write_short(0xc0+channel, instrument_id)
|
||||
|
||||
|
||||
|
||||
def time():
|
||||
"""returns the current time in ms of the PortMidi timer
|
||||
pyportmidi.time(): return time
|
||||
|
||||
The time is reset to 0, when the module is inited.
|
||||
"""
|
||||
return _pypm.Time()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MidiException(Exception):
|
||||
"""MidiException(errno) that can be raised.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
def __str__(self):
|
||||
return repr(self.parameter)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user