Line 500: |
Line 500: |
| | | |
| <br clear=all> | | <br clear=all> |
| + | |
| + | |
| + | ==Technical Info For Driver Developers== |
| + | |
| + | Drivers are python module implementing a class. This class can call internal zynthian-ui API, so it has full access to zynthian functionality. It’s not limited by CUIA. No limits, so you can break the UI too :wink: |
| + | |
| + | You can check the full code here: |
| + | |
| + | [https://github.com/zynthian/zynthian-ui/blob/testing/zyngui/zynthian_ctrldev_manager.py zynthian-ui/zyngui/zynthian_ctrldev_manager.py at testing · zynthian/zynthian-ui · GitHub ] |
| + | |
| + | and some example drivers here: |
| + | |
| + | [https://github.com/zynthian/zynthian-ui/tree/testing/zyngui/ctrldev zynthian-ui/zyngui/ctrldev at testing · zynthian/zynthian-ui · GitHub] |
| + | |
| + | All drivers must inherit from ''zynthian_ctrldev_base'', implementing the needed functionality as commented in the base code: |
| + | |
| + | <pre> |
| + | |
| + | #------------------------------------------------------------------------------------------------------------------ |
| + | # Control device base class |
| + | #------------------------------------------------------------------------------------------------------------------ |
| + | |
| + | class zynthian_ctrldev_base(): |
| + | |
| + | dev_id = None # String that identifies the device (class variable!) |
| + | dev_zynpad = False # Can act as a zynpad trigger device |
| + | dev_zynmixer = False # Can act as an audio mixer controller device |
| + | dev_pated = False # Can act as a pattern editor device |
| + | |
| + | |
| + | # Function to initialise class |
| + | def __init__(self): |
| + | self.idev = 0 # Slot index where the device is connected, starting from 1 (0 = None) |
| + | self.zyngui = zynthian_gui_config.zyngui |
| + | |
| + | |
| + | # Setup the device connected in slot #idev |
| + | # Before calling this, the caller (ctrldev-manager) should check that driver's ID string matches device's ID string |
| + | def setup(self, idev=None): |
| + | if idev != self.idev: |
| + | # Release currently selected device, if any ... |
| + | self.release() |
| + | # Init new selected device |
| + | if idev > 0: |
| + | self.idev = idev |
| + | logging.info("Setting-up {} in slot {}".format(self.dev_id, self.idev)) |
| + | # Setup routing |
| + | lib_zyncore.zmip_set_route_extdev(self.idev - 1, 0) |
| + | zynautoconnect.midi_autoconnect(True) |
| + | # Initialize new selected device |
| + | self.init() |
| + | self.refresh(force=True) |
| + | |
| + | |
| + | def release(self): |
| + | if self.idev > 0: |
| + | logging.info("Releasing {} in slot {}".format(self.dev_id, self.idev)) |
| + | # If device is still connected, call end |
| + | dev_id = zynautoconnect.get_midi_device_name(self.idev) |
| + | if dev_id and dev_id == self.dev_id: |
| + | self.end() |
| + | # Restore routing |
| + | lib_zyncore.zmip_set_route_extdev(self.idev - 1, 1) |
| + | zynautoconnect.midi_autoconnect(True) |
| + | self.idev = 0 |
| + | |
| + | |
| + | # Refresh device status (LED feedback, etc) |
| + | # It *SHOULD* be implemented by child class |
| + | def refresh(self, force=False): |
| + | logging.debug("Refresh LEDs for {}: NOT IMPLEMENTED!".format(self.dev_id)) |
| + | |
| + | |
| + | # Device MIDI event handler |
| + | # It *SHOULD* be implemented by child class |
| + | def midi_event(self, ev): |
| + | logging.debug("MIDI EVENT FROM '{}'".format(self.dev_id)) |
| + | |
| + | |
| + | # Light-Off LEDs |
| + | # It *SHOULD* be implemented by child class |
| + | def light_off(self): |
| + | logging.debug("Lighting Off LEDs for {}: NOT IMPLEMENTED!".format(self.dev_id)) |
| + | |
| + | |
| + | # Sleep On |
| + | # It *COULD* be improved by child class |
| + | def sleep_on(self): |
| + | self.light_off() |
| + | |
| + | |
| + | # Sleep On |
| + | # It *COULD* be improved by child class |
| + | def sleep_off(self): |
| + | self.refresh(True) |
| + | </pre> |
| + | |
| + | Quite simple, right? This is for basic “generic” drivers. |
| + | Zynpad enabled drivers inherit from this base class: |
| + | |
| + | |
| + | <pre> |
| + | # ------------------------------------------------------------------------------------------------------------------ |
| + | # Zynpad control device base class |
| + | # ------------------------------------------------------------------------------------------------------------------ |
| + | |
| + | class zynthian_ctrldev_zynpad(zynthian_ctrldev_base): |
| + | |
| + | dev_zynpad = True # Can act as a zynpad trigger device |
| + | |
| + | |
| + | def __init__(self): |
| + | super().__init__() |
| + | self.zynpad = self.zyngui.screens["zynpad"] |
| + | |
| + | |
| + | def refresh(self, force=False): |
| + | # When zynpad is shown, this is done by refresh_status, so no need to refresh twice |
| + | if force or not self.zynpad.shown: |
| + | self.refresh_pads(force) |
| + | self.refresh_zynpad_bank() |
| + | if force: |
| + | self.refresh_zynpad_bank() |
| + | |
| + | |
| + | # It *SHOULD* be implemented by child class |
| + | def refresh_zynpad_bank(self): |
| + | pass |
| + | |
| + | |
| + | def refresh_pads(self, force=False): |
| + | if force: |
| + | self.light_off() |
| + | for pad in range(self.zyngui.zynseq.col_in_bank ** 2): |
| + | # It MUST be called for cleaning the dirty bit |
| + | changed_state = self.zyngui.zynseq.libseq.hasSequenceChanged(self.zynpad.bank, pad) |
| + | if changed_state or force: |
| + | mode = self.zyngui.zynseq.libseq.getPlayMode(self.zynpad.bank, pad) |
| + | state = self.zynpad.get_pad_state(pad) |
| + | self.update_pad(pad, state, mode) |
| + | |
| + | |
| + | def refresh_pad(self, pad, force=False): |
| + | # It MUST be called for cleaning the dirty bit!! |
| + | changed_state = self.zyngui.zynseq.libseq.hasSequenceChanged(self.zynpad.bank, pad) |
| + | if changed_state or force: |
| + | mode = self.zyngui.zynseq.libseq.getPlayMode(self.zynpad.bank, pad) |
| + | state = self.zynpad.get_pad_state(pad) |
| + | self.update_pad(pad, state, mode) |
| + | |
| + | |
| + | # It *SHOULD* be implemented by child class |
| + | def update_pad(self, pad, state, mode): |
| + | pass |
| + | #------------------------------------------------------------------------------ |
| + | </pre> |
| + | |
| + | |
| + | If you kind-of-understand this, you shouldn’t have problem to understand the driver examples and program your own drivers. Simply copy your driver file in the the zyngui/ctrldev folder and restart zynthian-ui. Refer to this thread for more information [https://discourse.zynthian.org/t/new-control-device-manager-controller-device-drivers/8593/1 NEW: Control-device manager + controller device “drivers”] |