# NEW: Control-device manager + controller device "drivers"

**URL:** <https://discourse.zynthian.org/t/new-control-device-manager-controller-device-drivers/8593>\
**Category:** Software\
**Tags:** midimix, launchkey, midi-controllers, launchpad\
**Created:** [September 29, 2023, 12:38pm UTC](https://discourse.zynthian.org/t/new-control-device-manager-controller-device-drivers/8593 "2023-09-29T12:38:33Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![jofemodo](https://discourse.zynthian.org/user_avatar/discourse.zynthian.org/jofemodo/32/2_2.png) [@jofemodo](https://discourse.zynthian.org/u/jofemodo)\
**Post date:** [September 29, 2023, 12:38pm UTC](https://discourse.zynthian.org/t/new-control-device-manager-controller-device-drivers/8593/1 "2023-09-29T12:38:33Z")

</div>

Hi @zynthianers!

I’m proud of announcing the new control-device manager and driver subsystem is merged on testing branch. This is nice step forward in our road towards world domination 😁

The concept behind this is simple to explain with some examples:

- You plug your “Novation Launchpad Mini MK3” to zynthian, and it 's recognized and configured to manage the zynpad. The pads are colored like the zynpad ones. You can play/stop sequences, change scene, etc. LED feedback works great and you have the experience of full integration. Similar to what you get with this kind of control devices in Ableton Live.

 ![photo_2023-09-29_14-47-02](https://discourse.zynthian.org/uploads/default/original/2X/5/510565ea82ba33abd5a012894655670635e1831c.jpeg)

- You plug your “Akai MIDIMIX” controller and … voilaaaaa!! it’s recognized and configured to manage zynthian’s mixer. You can control main volume. Also volume, balance, mute & solo for each chain, you can change the active chain (on stage mode) or arm the multi-channel recorder (multi-timbral mode). You have LED feedback for everything. And the unassigned knobs are still available for MIDI learning. Just like it should be, a full integration experience.

 ![photo_2023-09-29_14-47-03](https://discourse.zynthian.org/uploads/default/original/2X/a/a340d07b1544874174833e0e6511e32b9d231519.jpeg)

- Etc.

The list of currently supported devices is this;

- Novation LaunchPad MINI MK3
- Novation LaunchPad MINI MK2
- Novation LaunchPad MINI MK1
- Novation LaunchKey MINI MK3
- Akai MiDI-Mix

Not too long yet, but i hope this list grows up fast!! Creating a driver is not too difficult and code-guys will find it quite fun. If you have the device and some idea about how it works, it’s a question of 1-2 hours to have something running. Novation, for instance, is a good example of well documented MIDI controllers, so you can find detailed documentation like this:

> **[Launchpad%20Mini%20-%20Programmers%20Reference%20Manual.pdf](https://fael-downloads-prod.focusrite.com/customer/prod/s3fs-public/downloads/Launchpad%20Mini%20-%20Programmers%20Reference%20Manual.pdf)**

Each manufacturer has his own policy about this, and in the worst case, you would need to reverse engineering the device, if someone didn’t before, what is highly probable. Also, the Ableton Live’s unofficial python drivers are a very good starting point because you will find examples for most devices and brands in the market:

> **[GitHub - gluon/AbletonLive10.1\_MIDIRemoteScripts: Unofficial repository for...](https://github.com/gluon/AbletonLive10.1_MIDIRemoteScripts)**
>
> Unofficial repository for Ableton Live 10.1.19 MIDI Remote Scripts - GitHub - gluon/AbletonLive10.1\_MIDIRemoteScripts: Unofficial repository for Ableton Live 10.1.19 MIDI Remote Scripts

The goal should be creating generic drivers that manage devices with its “factory” configuration untouched. I mean, many MIDI controllers have some kind of “editor” software that allow to modify its factory configuration. Creating drivers for modified “non-factory” configurations is possible, of course, and it could be interesting in some cases, but it’s sub-optimal because. It’s always better to make the driver to work with the factory default configuration, so everybody can use it out-the-box.

Regarding the controller-driver model, it’s quite simple. Take a look to the examples and ask all you want. Of course, you would need some programming skills.

* * *

* * *

**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 😉

You can check the full code here:

> [zynthian-ui/zyngui/zynthian\_ctrldev\_manager.py at testing · zynthian/zynthian-ui · GitHub](https://github.com/zynthian/zynthian-ui/blob/testing/zyngui/zynthian_ctrldev_manager.py)

and some example drivers here:

> [zynthian-ui/zyngui/ctrldev at testing · zynthian/zynthian-ui · GitHub](https://github.com/zynthian/zynthian-ui/tree/testing/zyngui/ctrldev)

All drivers must inherit from _zynthian\_ctrldev\_base_, implementing the needed functionality as commented in the base code:

```auto
#------------------------------------------------------------------------------------------------------------------
# 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)

```

Quite simple, right? This is for basic “generic” drivers.  
Zynpad enabled drivers inherit from this base class:

```auto
# ------------------------------------------------------------------------------------------------------------------
# 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
#------------------------------------------------------------------------------

```

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. BTW, hot driver reload is on the way 😉

**Yes. This part should be copied to the wiki.**

Enjoy!

---

_[View the full topic](https://discourse.zynthian.org/t/new-control-device-manager-controller-device-drivers/8593)._
