Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacOS compatibility #72

Open
wants to merge 12 commits into
base: dev
Choose a base branch
from
175 changes: 98 additions & 77 deletions BeatStep_Q.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
from .QControlComponent import QControlComponent
from .QSetup import QSetup

from functools import partial
from threading import Thread
from time import sleep



ENCODER_MSG_IDS = (10, 74, 71, 76, 77, 93, 73, 75, 114, 18, 19, 16, 17, 91, 79, 72)
PAD_MSG_IDS = list(range(44, 52)) + list(range(36, 44))

Expand All @@ -23,30 +29,30 @@
# the used memory-slot to store the configurations
MEMORY_SLOT = 8

SETUP_HARDWARE_DELAY = 2.1
SETUP_HARDWARE_DELAY = 8
INDIVIDUAL_MESSAGE_DELAY = 0.001

def split_list(l, size):
for i in range(0, len(l), size):
yield l[i:i + size]



class BeatStep_Q(ControlSurface):
def __init__(self, *a, **k):
super(BeatStep_Q, self).__init__(*a, **k)
self._messages_to_send = []

self.QS = QSetup()
self.control_layer_active = False

with self.component_guard():
self._setup_hardware_task = self._tasks.add(
Task.sequence(
Task.wait(SETUP_HARDWARE_DELAY), Task.run(self._setup_hardware)
)
)
self._setup_hardware_task.kill()
self._start_hardware_setup()

self._create_controls()
self._create_Q_control()

self._create_device()


def receive_midi(self, midi_bytes):
# self.show_message(str(midi_bytes))
super(BeatStep_Q, self).receive_midi(midi_bytes)
Expand All @@ -56,131 +62,146 @@ def handle_sysex(self, midi_bytes):
super(BeatStep_Q, self).handle_sysex(midi_bytes)

def port_settings_changed(self):
super(BeatStep_Q, self).port_settings_changed()
self._start_hardware_setup()
#super(BeatStep_Q, self).port_settings_changed()
# self._start_hardware_setup()
self._set_init_color_sequence()
self._setup_hardware(delay=.5, start_delay=SETUP_HARDWARE_DELAY)

def _start_hardware_setup(self):
# kill already running setup tasks:
self._setup_hardware_task.kill()
self._messages_to_send = []
self._setup_hardware_task.restart()
self._collect_setup_messages()
self._setup_hardware(start_delay=SETUP_HARDWARE_DELAY)

def _B_color_callback(self, b, c):
# do this to ensure callback-name closure
def f():
self._send_midi(self.QS.set_B_color(b, c))
def _setup_hardware(self, delay=None, start_delay=0.):
if delay is None:
delay = INDIVIDUAL_MESSAGE_DELAY

return f
# add individual message delays
i = 1
with self.component_guard():
while len(self._messages_to_send) > 0:
msg = self._messages_to_send.pop(0)
self.schedule_message(i * delay + start_delay,
partial(self._send_midi, msg))
i += 1

def _collect_setup_messages(self, layer="init"):
if layer == "init":
self._setup_buttons_and_encoders()
self._setup_control_buttons_and_encoders()

# set pad velocity to 0 (e.g. linear) on startup
self._messages_to_send.append(self.QS.set_B_velocity(0))
# set encoder acceleration to "slow" on startup
self._messages_to_send.append(self.QS.set_E_acceleration(0))

def _init_color_sequence(self):
for i in range(1, 9):
self.schedule_message(i, self._B_color_callback(i, 1))
self.schedule_message(17 - i, self._B_color_callback(i, 16))
for i in range(1, 17):
self.schedule_message(20, self._B_color_callback(i, 0))

def _setup_hardware(self):
self._init_color_sequence()
self._setup_control_buttons_and_encoders()
self._setup_buttons_and_encoders()
def _set_init_color_sequence(self):

# set pad velocity to 0 (e.g. linear) on startup
self._send_midi(self.QS.set_B_velocity(0))
# set encoder acceleration to "slow" on startup
self._send_midi(self.QS.set_E_acceleration(0))
offmsgs = [self.QS.set_B_color(i, 0) for i in range(1, 17)]
rmsgs = [self.QS.set_B_color(i, 1) for i in range(1, 17)]
bmsgs = [self.QS.set_B_color(i, 16) for i in range(1, 17)]
mmsgs = [self.QS.set_B_color(i, 17) for i in range(1, 17)]

for msg in offmsgs + rmsgs + bmsgs + mmsgs + offmsgs:
self._messages_to_send.append(msg)

def _setup_control_buttons_and_encoders(self):
"""
this function is only called once when the BeatStep is plugged in to
ensure correct assignments of function-buttons and transpose encoder
"""
# set shift button to note-mode
self._send_midi(self.QS.set_B_mode("shift", 8))
self._send_midi(self.QS.set_B_channel("shift", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("shift", 1))
self._messages_to_send.append(self.QS.set_B_mode("shift", 8))
self._messages_to_send.append(self.QS.set_B_channel("shift", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("shift", 1))

# set stop button to note-mode
self._send_midi(self.QS.set_B_mode("stop", 8))
self._send_midi(self.QS.set_B_channel("stop", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("stop", 1))
self._messages_to_send.append(self.QS.set_B_mode("stop", 8))
self._messages_to_send.append(self.QS.set_B_channel("stop", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("stop", 1))

# set play button to note-mode
self._send_midi(self.QS.set_B_mode("play", 8))
self._send_midi(self.QS.set_B_channel("play", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("play", 1))
self._messages_to_send.append(self.QS.set_B_mode("play", 8))
self._messages_to_send.append(self.QS.set_B_channel("play", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("play", 1))

# set cntrl/seq button to note-mode
self._send_midi(self.QS.set_B_mode("cntrl", 8))
self._send_midi(self.QS.set_B_channel("cntrl", CHANNEL))
self._messages_to_send.append(self.QS.set_B_mode("cntrl", 8))
self._messages_to_send.append(self.QS.set_B_channel("cntrl", CHANNEL))
# set button behaviour to toggle
self._send_midi(self.QS.set_B_behaviour("cntrl", 0))
self._messages_to_send.append(self.QS.set_B_behaviour("cntrl", 0))

# set chan button to note mode
self._send_midi(self.QS.set_B_mode("chan", 8))
self._send_midi(self.QS.set_B_channel("chan", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("chan", 1))
self._messages_to_send.append(self.QS.set_B_mode("chan", 8))
self._messages_to_send.append(self.QS.set_B_channel("chan", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("chan", 1))

# set store button to note mode
self._send_midi(self.QS.set_B_mode("store", 8))
self._send_midi(self.QS.set_B_channel("store", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("store", 1))
self._messages_to_send.append(self.QS.set_B_mode("store", 8))
self._messages_to_send.append(self.QS.set_B_channel("store", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("store", 1))

# set store button to note mode
self._send_midi(self.QS.set_B_mode("recall", 8))
self._send_midi(self.QS.set_B_channel("recall", CHANNEL))
self._send_midi(self.QS.set_B_behaviour("recall", 1))
self._messages_to_send.append(self.QS.set_B_mode("recall", 8))
self._messages_to_send.append(self.QS.set_B_channel("recall", CHANNEL))
self._messages_to_send.append(self.QS.set_B_behaviour("recall", 1))

# set transpose encoder channel
self._send_midi(self.QS.set_E_channel("transpose", CHANNEL))
self._send_midi(self.QS.set_E_behaviour("transpose", 2))
self._messages_to_send.append(self.QS.set_E_channel("transpose", CHANNEL))
self._messages_to_send.append(self.QS.set_E_behaviour("transpose", 2))
# set encoder cc to something else than the shift-encoder cc
# (4 is unused since it would represent the ext/sync button)
self._send_midi(self.QS.set_E_cc("transpose", 4))
self._messages_to_send.append(self.QS.set_E_cc("transpose", 4))

def _setup_buttons_and_encoders(self):
# for all buttons and encoders
for i in range(1, 17):
# set pad to note-mode
self._send_midi(self.QS.set_B_mode(i, 9))
self._messages_to_send.append(self.QS.set_B_mode(i, 9))
# set pad channel
self._send_midi(self.QS.set_B_channel(i, CHANNEL))
self._messages_to_send.append(self.QS.set_B_channel(i, CHANNEL))
# set pad behaviour to toggle
self._send_midi(self.QS.set_B_behaviour(i, 1))
self._messages_to_send.append(self.QS.set_B_behaviour(i, 1))

# set encoder channel
self._send_midi(self.QS.set_E_channel(i, CHANNEL))
self._messages_to_send.append(self.QS.set_E_channel(i, CHANNEL))
# set all encoders to relative-mode 2
self._send_midi(self.QS.set_E_behaviour(i, 2))
self._messages_to_send.append(self.QS.set_E_behaviour(i, 2))
# set all encoders to midi cc mode
self._send_midi(self.QS.set_E_mode(i, 1))
self._messages_to_send.append(self.QS.set_E_mode(i, 1))
# set midi cc id's for all encoders
self._send_midi(self.QS.set_E_cc(i, ENCODER_MSG_IDS[i - 1]))
self._messages_to_send.append(self.QS.set_E_cc(i, ENCODER_MSG_IDS[i - 1]))


def _set_control_mode_msgs(self):

def _do_activate_control_mode(self):
# for all buttons
for i in range(1, 17):
# set pad to cc-mode
self._send_midi(self.QS.set_B_mode(i, 8))
self._messages_to_send.append(self.QS.set_B_mode(i, 8))
# set pad channel
self._send_midi(self.QS.set_B_channel(i, CHANNEL))
self._messages_to_send.append(self.QS.set_B_channel(i, CHANNEL))
# set pad behaviour to toggle
self._send_midi(self.QS.set_B_behaviour(i, 1))
self._messages_to_send.append(self.QS.set_B_behaviour(i, 1))

# set encoder channel
self._send_midi(self.QS.set_E_channel(i, CHANNEL))
self._messages_to_send.append(self.QS.set_E_channel(i, CHANNEL))
# set all encoders to relative-mode 2
self._send_midi(self.QS.set_E_behaviour(i, 2))
self._messages_to_send.append(self.QS.set_E_behaviour(i, 2))

# set transpose encoder channel to follow global channel
self._send_midi(self.QS.set_E_channel("transpose", CHANNEL))
self._messages_to_send.append(self.QS.set_E_channel("transpose", CHANNEL))
# set transpose encoder to relative mode 2
self._send_midi(self.QS.set_E_behaviour("transpose", 2))
self._messages_to_send.append(self.QS.set_E_behaviour("transpose", 2))


def _do_activate_control_mode(self):
self._set_control_mode_msgs()
self._setup_hardware()


def _deactivate_control_mode(self):
# recall state before layer was activated
self._send_midi(self.QS.recall_preset(MEMORY_SLOT))
# make sure that buttons relevant for control-features
# are correctly set
# self._setup_control_buttons_and_encoders()

# set pad velocity
self._send_midi(self.QS.set_B_velocity(self._control_component._pad_velocity))
Expand Down