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

Add nanoKONTROL2 scripts #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
353 changes: 353 additions & 0 deletions nanoKONTROL2/ChannelStrip.py

Large diffs are not rendered by default.

862 changes: 862 additions & 0 deletions nanoKONTROL2/ChannelStripController.py

Large diffs are not rendered by default.

240 changes: 240 additions & 0 deletions nanoKONTROL2/MackieControl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#Embedded file name: /Users/versonator/Jenkins/live/output/mac_64_static/Release/python-bundle/MIDI Remote Scripts/MackieControl/MackieControl.py
from consts import *
from MainDisplay import MainDisplay
from MainDisplayController import MainDisplayController
from TimeDisplay import TimeDisplay
from ChannelStrip import ChannelStrip, MasterChannelStrip
from ChannelStripController import ChannelStripController
from SoftwareController import SoftwareController
from Transport import Transport
import Live
import MidiRemoteScript

class MackieControl:
"""Main class that establishes the Mackie Control <-> Live interaction. It acts
as a container/manager for all the Mackie Control sub-components like ChannelStrips,
Displays and so on.
Futher it is glued to Lives MidiRemoteScript C instance, which will forward some
notifications to us, and lets us forward some requests that are needed beside the
general Live API (see 'send_midi' or 'request_rebuild_midi_map').
"""

def __init__(self, c_instance):
self.__c_instance = c_instance
self.__components = []
self.__main_display = MainDisplay(self)
self.__components.append(self.__main_display)
self.__main_display_controller = MainDisplayController(self, self.__main_display)
self.__components.append(self.__main_display_controller)
self.__time_display = TimeDisplay(self)
self.__components.append(self.__time_display)
self.__software_controller = SoftwareController(self)
self.__components.append(self.__software_controller)
self.__transport = Transport(self)
self.__components.append(self.__transport)
self.__channel_strips = [ ChannelStrip(self, i) for i in range(NUM_CHANNEL_STRIPS) ]
for s in self.__channel_strips:
self.__components.append(s)

self.__master_strip = MasterChannelStrip(self)
self.__components.append(self.__master_strip)
self.__channel_strip_controller = ChannelStripController(self, self.__channel_strips, self.__master_strip, self.__main_display_controller)
self.__components.append(self.__channel_strip_controller)
self.__shift_is_pressed = False
self.__option_is_pressed = False
self.__ctrl_is_pressed = False
self.__alt_is_pressed = False
self.is_pro_version = False
self._received_firmware_version = False
self._refresh_state_next_time = 0

def disconnect(self):
for c in self.__components:
c.destroy()

def connect_script_instances(self, instanciated_scripts):
"""Called by the Application as soon as all scripts are initialized.
You can connect yourself to other running scripts here, as we do it
connect the extension modules (MackieControlXTs).
"""
try:
from MackieControlXT.MackieControlXT import MackieControlXT
except:
print 'failed to load the MackieControl XT script (might not be installed)'

found_self = False
right_extensions = []
left_extensions = []
for s in instanciated_scripts:
if s is self:
found_self = True
elif isinstance(s, MackieControlXT):
s.set_mackie_control_main(self)
if found_self:
right_extensions.append(s)
else:
left_extensions.append(s)

raise found_self or AssertionError
self.__main_display_controller.set_controller_extensions(left_extensions, right_extensions)
self.__channel_strip_controller.set_controller_extensions(left_extensions, right_extensions)

def request_firmware_version(self):
if not self._received_firmware_version:
self.send_midi((240,
0,
0,
102,
SYSEX_DEVICE_TYPE,
19,
0,
247))

def application(self):
"""returns a reference to the application that we are running in"""
return Live.Application.get_application()

def song(self):
"""returns a reference to the Live Song that we do interact with"""
return self.__c_instance.song()

def handle(self):
"""returns a handle to the c_interface that is needed when forwarding MIDI events
via the MIDI map
"""
return self.__c_instance.handle()

def refresh_state(self):
for c in self.__components:
c.refresh_state()

self.request_firmware_version()
self._refresh_state_next_time = 30

def is_extension(self):
return False

def request_rebuild_midi_map(self):
""" To be called from any components, as soon as their internal state changed in a
way, that we do need to remap the mappings that are processed directly by the
Live engine.
Dont assume that the request will immediately result in a call to
your build_midi_map function. For performance reasons this is only
called once per GUI frame."""
self.__c_instance.request_rebuild_midi_map()

def build_midi_map(self, midi_map_handle):
"""New MIDI mappings can only be set when the scripts 'build_midi_map' function
is invoked by our C instance sibling. Its either invoked when we have requested it
(see 'request_rebuild_midi_map' above) or when due to a change in Lives internal state,
a rebuild is needed."""
for s in self.__channel_strips:
s.build_midi_map(midi_map_handle)

self.__master_strip.build_midi_map(midi_map_handle)
for i in range(SID_FIRST, SID_LAST + 1):
if i not in function_key_control_switch_ids:
Live.MidiMap.forward_midi_note(self.handle(), midi_map_handle, 0, i)

Live.MidiMap.forward_midi_cc(self.handle(), midi_map_handle, 0, JOG_WHEEL_CC_NO)

def update_display(self):
if self._refresh_state_next_time > 0:
self._refresh_state_next_time -= 1
if self._refresh_state_next_time == 0:
for c in self.__components:
c.refresh_state()

self.request_firmware_version()
for c in self.__components:
c.on_update_display_timer()

def send_midi(self, midi_event_bytes):
"""Use this function to send MIDI events through Live to the _real_ MIDI devices
that this script is assigned to."""
self.__c_instance.send_midi(midi_event_bytes)

def receive_midi(self, midi_bytes):
if midi_bytes[0] & 240 == NOTE_ON_STATUS or midi_bytes[0] & 240 == NOTE_OFF_STATUS:
note = midi_bytes[1]
value = BUTTON_PRESSED if midi_bytes[2] > 0 else BUTTON_RELEASED
if note in range(SID_FIRST, SID_LAST + 1):
if note in display_switch_ids:
self.__handle_display_switch_ids(note, value)
if note in channel_strip_switch_ids + fader_touch_switch_ids:
for s in self.__channel_strips:
s.handle_channel_strip_switch_ids(note, value)

if note in channel_strip_control_switch_ids:
self.__channel_strip_controller.handle_assignment_switch_ids(note, value)
if note in function_key_control_switch_ids:
self.__software_controller.handle_function_key_switch_ids(note, value)
if note in software_controls_switch_ids:
self.__software_controller.handle_software_controls_switch_ids(note, value)
if note in transport_control_switch_ids:
self.__transport.handle_transport_switch_ids(note, value)
if note in marker_control_switch_ids:
self.__transport.handle_marker_switch_ids(note, value)
if note in jog_wheel_switch_ids:
self.__transport.handle_jog_wheel_switch_ids(note, value)
elif midi_bytes[0] & 240 == CC_STATUS:
cc_no = midi_bytes[1]
cc_value = midi_bytes[2]
if cc_no == JOG_WHEEL_CC_NO:
self.__transport.handle_jog_wheel_rotation(cc_value)
elif cc_no in range(FID_PANNING_BASE, FID_PANNING_BASE + NUM_CHANNEL_STRIPS):
for s in self.__channel_strips:
s.handle_vpot_rotation(cc_no - FID_PANNING_BASE, cc_value)

elif midi_bytes[0] == 240 and len(midi_bytes) == 12 and midi_bytes[5] == 20:
version_bytes = midi_bytes[6:-2]
major_version = version_bytes[1]
self.is_pro_version = major_version > 50
self._received_firmware_version = True

def can_lock_to_devices(self):
return False

def suggest_input_port(self):
return ''

def suggest_output_port(self):
return ''

def suggest_map_mode(self, cc_no, channel):
result = Live.MidiMap.MapMode.absolute
if cc_no in range(FID_PANNING_BASE, FID_PANNING_BASE + NUM_CHANNEL_STRIPS):
result = Live.MidiMap.MapMode.relative_signed_bit
return result

def shift_is_pressed(self):
return self.__shift_is_pressed

def set_shift_is_pressed(self, pressed):
self.__shift_is_pressed = pressed

def option_is_pressed(self):
return self.__option_is_pressed

def set_option_is_pressed(self, pressed):
self.__option_is_pressed = pressed

def control_is_pressed(self):
return self.__control_is_pressed

def set_control_is_pressed(self, pressed):
self.__control_is_pressed = pressed

def alt_is_pressed(self):
return self.__alt_is_pressed

def set_alt_is_pressed(self, pressed):
self.__alt_is_pressed = pressed

def __handle_display_switch_ids(self, switch_id, value):
if switch_id == SID_DISPLAY_NAME_VALUE:
if value == BUTTON_PRESSED:
self.__channel_strip_controller.toggle_meter_mode()
elif switch_id == SID_DISPLAY_SMPTE_BEATS:
if value == BUTTON_PRESSED:
self.__time_display.toggle_mode()
42 changes: 42 additions & 0 deletions nanoKONTROL2/MackieControlComponent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#Embedded file name: /Users/versonator/Jenkins/live/output/mac_64_static/Release/python-bundle/MIDI Remote Scripts/MackieControl/MackieControlComponent.py
from consts import *
import Live

class MackieControlComponent:
"""Baseclass for every 'sub component' of the Mackie Control. Just offers some """

def __init__(self, main_script):
self.__main_script = main_script

def destroy(self):
self.__main_script = None

def main_script(self):
return self.__main_script

def shift_is_pressed(self):
return self.__main_script.shift_is_pressed()

def option_is_pressed(self):
return self.__main_script.option_is_pressed()

def control_is_pressed(self):
return self.__main_script.control_is_pressed()

def alt_is_pressed(self):
return self.__main_script.alt_is_pressed()

def song(self):
return self.__main_script.song()

def script_handle(self):
return self.__main_script.handle()

def application(self):
return self.__main_script.application()

def send_midi(self, bytes):
self.__main_script.send_midi(bytes)

def request_rebuild_midi_map(self):
self.__main_script.request_rebuild_midi_map()
61 changes: 61 additions & 0 deletions nanoKONTROL2/MainDisplay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#Embedded file name: /Users/versonator/Jenkins/live/output/mac_64_static/Release/python-bundle/MIDI Remote Scripts/MackieControl/MainDisplay.py
from MackieControlComponent import *

class MainDisplay(MackieControlComponent):
""" Representing one main 2 row display of a Mackie Control or Extension
"""

def __init__(self, main_script):
MackieControlComponent.__init__(self, main_script)
self.__stack_offset = 0
self.__last_send_messages = [[], []]

def destroy(self):
NUM_CHARS_PER_DISPLAY_LINE = 54
upper_message = 'Ableton Live'.center(NUM_CHARS_PER_DISPLAY_LINE)
self.send_display_string(upper_message, 0, 0)
lower_message = 'Device is offline'.center(NUM_CHARS_PER_DISPLAY_LINE)
self.send_display_string(lower_message, 1, 0)
MackieControlComponent.destroy(self)

def stack_offset(self):
return self.__stack_offset

def set_stack_offset(self, offset):
"""This is the offset that one gets by 'stacking' several MackieControl XTs:
the first is at index 0, the second at 8, etc ...
"""
self.__stack_offset = offset

def send_display_string(self, display_string, display_row, cursor_offset):
if display_row == 0:
offset = cursor_offset
elif display_row == 1:
offset = NUM_CHARS_PER_DISPLAY_LINE + 2 + cursor_offset
else:
raise 0 or AssertionError
message_string = [ ord(c) for c in display_string ]
for i in range(len(message_string)):
if message_string[i] >= 128:
message_string[i] = 0

if self.__last_send_messages[display_row] != message_string:
self.__last_send_messages[display_row] = message_string
if self.main_script().is_extension():
device_type = SYSEX_DEVICE_TYPE_XT
else:
device_type = SYSEX_DEVICE_TYPE
display_sysex = (240,
0,
0,
102,
device_type,
18,
offset) + tuple(message_string) + (247,)
self.send_midi(display_sysex)

def refresh_state(self):
self.__last_send_messages = [[], []]

def on_update_display_timer(self):
pass
Loading