Changes

m
Line 55: Line 55:  
* '''Clear a sequence''', pressing any track button + the sequence pad (use SELECT function, and the selected chain's button to avoid changing other settings).  
 
* '''Clear a sequence''', pressing any track button + the sequence pad (use SELECT function, and the selected chain's button to avoid changing other settings).  
   −
* Start '''recording a pattern'''. For this, press <code>RECORD</code> + a pad button, which will 1) select the chain associated with the group of the sequence, 2) open the pattern editor for that sequence, 3) start playing the sequence and 4) start recording that sequence. If you press the pad again (without holding any other button), the recording will stop. Press again to stop playback too. You can also control the playback/recording with <code>PLAY/PAUSE</code> and stop the recording with <code>RECORD</code> button.  
+
* Start '''recording a pattern'''. For this, press <code>RECORD</code> + a pad button, which will 1) select the chain associated with the group of the sequence, 2) open the pattern editor for that sequence, 3) start playing the sequence and 4) start recording that sequence. If you press the pad again (without holding any other button), the recording will stop. Press again to stop playback too. You can also control the playback/recording with <code>PLAY/PAUSE</code> or <code>SHIFT</code> + <code>RECORD</code> buttons.  
    
* If the pattern editor is open, press <code>SHIFT</code> + <code>UP arrow</code> to '''go back to ZynPad'''. Press it again to go to mixer screen.
 
* If the pattern editor is open, press <code>SHIFT</code> + <code>UP arrow</code> to '''go back to ZynPad'''. Press it again to go to mixer screen.
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/oram/zyngine/zynthian_ctrldev_manager.py zynthian-ui/zyngui/zynthian_ctrldev_manager.py zynthian/zyngine · GitHub]
 +
 +
and some example drivers here:
 +
 +
[https://github.com/zynthian/zynthian-ui/tree/oram/zyngine/ctrldev zynthian-ui/zyngine/ctrldev/ · 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”]
229

edits