Skip to content

Commit 10ebd2a

Browse files
committed
Add option to use virtual midi output port to replicate push messages
1 parent 105d6dd commit 10ebd2a

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

push2_python/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Push2(object):
4747
simulator_controller = None
4848

4949

50-
def __init__(self, use_user_midi_port=False, run_simulator=False, simulator_port=6128):
50+
def __init__(self, use_user_midi_port=False, run_simulator=False, simulator_port=6128, simulator_use_virtual_midi_out=False):
5151
"""Initializes object to interface with Ableton's Push2.
5252
This function will set up USB and MIDI connections with the hardware device.
5353
By default, MIDI connection will use LIVE MIDI port instead of USER MIDI port.
@@ -105,11 +105,13 @@ def check_active_sensing(f_stop):
105105

106106
# Initialize simulator (if requested)
107107
if run_simulator:
108-
self.simulator_controller = start_simulator(self, port=simulator_port)
108+
self.simulator_controller = start_simulator(self, port=simulator_port, use_virtual_midi_out=simulator_use_virtual_midi_out)
109+
109110

110111
def stop_active_sensing_thread(self):
111112
self.f_stop.set()
112113

114+
113115
def set_push2_reconnect_call_interval(self, new_interval):
114116
self.function_call_interval_limit_overwrite = new_interval
115117
self.display.function_call_interval_limit_overwrite = new_interval

push2_python/simulator/simulator.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import numpy
1212
import queue
1313
import logging
14+
import mido
1415

1516
app = Flask(__name__)
1617
sim_app = SocketIO(app)
@@ -20,6 +21,9 @@
2021
push_object = None
2122
client_connected = False
2223

24+
midi_out = None
25+
26+
2327
default_color_palette = {
2428
0: [(0, 0, 0), (0 ,0 ,0)],
2529
3: [(265, 165, 0), None],
@@ -39,9 +43,9 @@
3943

4044
def make_midi_message_from_midi_trigger(midi_trigger, releasing=False, velocity=127, value=127):
4145
if midi_trigger.startswith('nn'):
42-
return mido.Message('note_on' if not releasing else 'note_off', note=int(midi_trigger.replace('nn', '')), velocity=velocity)
43-
if midi_trigger.startswith('cc'):
44-
return mido.Message('control_change', control=int(midi_trigger.replace('cc', '')), value=value if not releasing else 0)
46+
return mido.Message('note_on' if not releasing else 'note_off', note=int(midi_trigger.replace('nn', '')), velocity=velocity, channel=0)
47+
elif midi_trigger.startswith('cc'):
48+
return mido.Message('control_change', control=int(midi_trigger.replace('cc', '')), value=value if not releasing else 0, channel=0)
4549
return None
4650

4751

@@ -54,7 +58,8 @@ class SimulatorController(object):
5458
color_palette = default_color_palette
5559
ws_message_queue = queue.Queue()
5660

57-
def __init__(self, *args, **kwargs):
61+
def __init__(self):
62+
5863
# Generate black frame to be used if display is not updated
5964
colors = ['{b:05b}{g:06b}{r:05b}'.format(r=0, g=0, b=0),
6065
'{b:05b}{g:06b}{r:05b}'.format(r=0, g=0, b=0),
@@ -184,42 +189,56 @@ def get_ws_messages_from_queue():
184189
@sim_app.on('padPressed')
185190
def pad_pressed(midiTrigger):
186191
msg = make_midi_message_from_midi_trigger(midiTrigger)
192+
if midi_out is not None:
193+
midi_out.send(msg)
187194
push_object.pads.on_midi_message(msg)
188195

189196

190197
@sim_app.on('padReleased')
191198
def pad_released(midiTrigger):
192199
msg = make_midi_message_from_midi_trigger(midiTrigger, releasing=True)
200+
if midi_out is not None:
201+
midi_out.send(msg)
193202
push_object.pads.on_midi_message(msg)
194203

195204

196205
@sim_app.on('buttonPressed')
197206
def button_pressed(midiTrigger):
198207
msg = make_midi_message_from_midi_trigger(midiTrigger)
208+
if midi_out is not None:
209+
midi_out.send(msg)
199210
push_object.buttons.on_midi_message(msg)
200211

201212

202213
@sim_app.on('buttonReleased')
203214
def button_released(midiTrigger):
204215
msg = make_midi_message_from_midi_trigger(midiTrigger, releasing=True)
216+
if midi_out is not None:
217+
midi_out.send(msg)
205218
push_object.buttons.on_midi_message(msg)
206219

207220

208221
@sim_app.on('encdoerTouched')
209222
def encoder_pressed(midiTrigger):
210223
msg = make_midi_message_from_midi_trigger(midiTrigger, velocity=127)
224+
if midi_out is not None:
225+
midi_out.send(msg)
211226
push_object.encoders.on_midi_message(msg)
212227

213228

214229
@sim_app.on('encdoerReleased')
215230
def encoder_released(midiTrigger):
216231
msg = make_midi_message_from_midi_trigger(midiTrigger, velocity=0)
232+
if midi_out is not None:
233+
midi_out.send(msg)
217234
push_object.encoders.on_midi_message(msg)
218235

219236

220237
@sim_app.on('encdoerRotated')
221238
def encoder_rotated(midiTrigger, value):
222239
msg = make_midi_message_from_midi_trigger(midiTrigger, value=value)
240+
if midi_out is not None:
241+
midi_out.send(msg)
223242
push_object.encoders.on_midi_message(msg)
224243

225244

@@ -235,9 +254,13 @@ def run_simulator_in_thread(port):
235254
sim_app.run(app, port=port)
236255

237256

238-
def start_simulator(_push_object, port):
239-
global push_object
257+
def start_simulator(_push_object, port, use_virtual_midi_out):
258+
global push_object, midi_out
240259
push_object = _push_object
241-
thread = Thread(target=run_simulator_in_thread, args=(port,))
260+
if use_virtual_midi_out:
261+
name = 'Push2Simulator'
262+
logging.info('Sending Push2 simulated messages to "{}" virtual midi output'.format(name))
263+
midi_out = mido.open_output(name, virtual=True)
264+
thread = Thread(target=run_simulator_in_thread, args=(port, ))
242265
thread.start()
243266
return SimulatorController()

0 commit comments

Comments
 (0)