@@ -12,7 +12,11 @@ def _get_filter(item):
1212class MIDI :
1313 """"""
1414 def __init__ (self , in_ports = None , out_ports = None , verbose = True ):
15- """"""
15+ """
16+ Args:
17+ in_ports: list of input devices (uses all by default)
18+ out_ports: list of output devices (uses all by default)
19+ """
1620 self .verbose = verbose
1721 # type -> list[Optional[set[port], Optional[set[channel]], function]
1822 self .handlers = defaultdict (list )
@@ -39,6 +43,11 @@ def __init__(self, in_ports=None, out_ports=None, verbose=True):
3943
4044 self .handle = MIDIHandlers (self )
4145
46+ # TODO: filtering needs to work for all message types...
47+ # maybe there should be just one decorator
48+ # which can accept any filter, including on type?
49+ # and which just takes a mido message?
50+
4251 def get_midi_callback (self ):
4352 """callback for mido MIDI handling"""
4453 # close over `self``
@@ -82,22 +91,54 @@ def decorator(f):
8291
8392 return decorator if f is None else decorator (f )
8493
85- # TODO: MIDI send
86- def note_on (self , pitch , velocity = 64 , channel = 0 ):
87- pass
94+ # send on all ports by default
95+ def _send (self , port , * a , ** kw ):
96+ ports = self .out_ports .values () if port is None else [self .out_ports [port ]]
97+ for p in ports :
98+ p .send (mido .Message (* a , ** kw ))
99+
100+ # see https://mido.readthedocs.io/en/latest/message_types.html
101+
102+ def note_on (self , note , velocity = 64 , channel = 0 , port = None ):
103+ self ._send (port , 'note_on' , channel = channel , note = note , velocity = velocity )
104+
105+ def note_off (self , note , velocity = 64 , channel = 0 , port = None ):
106+ self ._send (port , 'note_off' , channel = channel , note = note , velocity = velocity )
107+
108+ def control_change (self , control , value , channel = 0 , port = None ):
109+ self ._send (port , 'control_change' , channel = channel , control = control , value = value )
110+
111+ def cc (self , * a , ** kw ):
112+ self .control_change (* a , ** kw )
113+
114+ def program_change (self , program , channel = 0 , port = None ):
115+ self ._send (port , 'program_change' , channel = channel , program = program )
116+
117+ def polytouch (self , note , value = 64 , channel = 0 , port = None ):
118+ self ._send (port , 'polytouch' , channel = channel , note = note , value = value )
119+
120+ def aftertouch (self , value = 64 , channel = 0 , port = None ):
121+ self ._send (port , 'aftertouch' , channel = channel , value = value )
122+
123+ def pitchwheel (self , pitch = 0 , port = None ):
124+ self ._send (port , 'pitchwheel' , pitch = pitch )
125+
126+ def pitchbend (self , * a , ** kw ):
127+ self .pitchwheel (* a , ** kw )
128+
88129
89- # this is effectively part of MIDI,
130+ # this is effectively part of MIDI class ,
90131# it is only a separate class for naming aesthetics
91132class MIDIHandlers :
92133 """specific MIDI handler decorators"""
93134 def __init__ (self , midi ):
94135 self .midi = midi
95136
96- def note_on (self , ports = None , channels = None , pitches = None , velocities = None ):
97- return self .midi ._decorator ('note_on' , ports , channels , pitches , velocities )
137+ def note_on (self , ports = None , channels = None , notes = None , velocities = None ):
138+ return self .midi ._decorator ('note_on' , ports , channels , notes , velocities )
98139
99- def note_off (self , ports = None , channels = None , pitches = None , velocities = None ):
100- return self .midi ._decorator ('note_off' , ports , channels , pitches , velocities )
140+ def note_off (self , ports = None , channels = None , notes = None , velocities = None ):
141+ return self .midi ._decorator ('note_off' , ports , channels , notes , velocities )
101142
102143 def control_change (self , ports = None , channels = None , controls = None , values = None ):
103144 return self .midi ._decorator ('control_change' , ports , channels , controls , values )
0 commit comments