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

Helloooow, Do you think we can have the top right led of the launchpads, under the logo, blink to the beat ? Green red red red Green red red red ?
:face_holding_back_tears:

I’m trying to write a driver to use the Akai APC 40, I’m starting as a mixer, then moving on to pad integration. I was able to get basic debugging working using vs code connecting to my zynthian remotely, but I don’t ever see the zynthian_ctrldev_manager or my device definition even loading. I’ve set breakpoints in the files, zynthian_ctrldev_manager.py and my ctrldev/zynthian_ctrldev_akai_apc40_mixer.py, but they never trigger. I’m sure I’m probably missing something basic. Is there some setting to enable the ctrldev manager or something?

Ctrldev_manager is always enabled and tries to autoinit a matching driver of each type (zynpad and mixer) when possible. If the ID string of a connected device match the ID strings from a driver, the driver is initialized for mixer or zynpad, depending on the driver type. I’ve not tested to put together mixer & zynpad functionality in the same driver. It should be possible but you could find problems or limitations. The driver model is being currently improved to allow greater flexibility. Meanwhile, focus in a single type first, mixer or zynpad.

Regards,

1 Like

I’ve just got a basic driver working for the launch pad controls on the APC40 - you can see the code here in case that gives you any leads for the mixer side of things: GitHub - lyserge/zynthian-apc40: Akai APC40 controller driver for Zynthian

1 Like

p.s. thanks @jofemodo for putting this subsystem together! Hopefully this will open up a whole lot of hardware compatibility

Thanks @jofemodo and @lyserge! Lyserge, I’ll be taking a look at your implementation of the the apc40 to zynpad. Thanks for getting me started!

Thanks again for the link, it absolutely helped me get started! I was able to use your code and get an implementation of both the zynmixer and zynpad working on the APC40 mkI - there’s still a couple of bugs but it’s mostly working - GitHub - zhagan/zynthian-controller-drivers: To how

1 Like

Hi!

I’m working on a controller device driver for the Akai APC Key 25 mk2:

Is there someone else working on it? If so, we may join forces. Otherwise, I’ll update as soon as I have anything working :slightly_smiling_face:

Best regards!

UPDATE: Mixer volumes ready! I can control the first 8 channels directly with knobs 1 to 8, and channels 9-15 with knobs 1 to 7 + SHIFT. The shifted knob 8 is for main chain (master volume).

UPDATE 2: Mixer pan working! Pressing SHIFT will lit the selected function for the knobs (currently Volume or Pan), and using the Knob Ctrl buttons you can change the function. Pan (balance) knobs works as volume does.

UPDATE 3: Now I have the mute/solo buttons ready! They are controlled by the Track buttons (button row below the pad matrix). Pressing each one of these buttons will toggle the current selected function for the corresponding chain. As there are only 8 buttons, I’ve implemented a bank function: pressing SHIFT + LEFT/RIGHT will change between bank 0 (chains 1-8) and bank 1 (chains 9-16). The triggered action is selected by the Soft Keys (button column to the right of the pad matrix). Pressing SHIFT + MUTE/SOLO will change the action. Also, the Track buttons will lit if the corresponding function is active in the given channel.

I think the mixer functionality is complete. I’ll give a try to the Zynpad :slight_smile:

3 Likes

Hi again!

I’ve a problem: when I press any control key (which are handled by midi_event, returning True) the event is propagated to the selected chain, and if it is a MIDI one, it produces a note. How can I control (avoid) this behaviour? Any clue? :thinking:

It shouldn’t happen. When a driver is loaded for a device, the device (MIDI port), this is disconnected from the chains. Are you using latest testing branch?

You should see this logging message when your device driver is loaded and enabled:

logging.info(“Setting-up {} in slot {}”.format(self.dev_id, self.idev))

Could you tell me what’s the exact logging message you get?
Also, how many MIDI devices use the controller? Have separated MIDI devices for control and playing?

Regards,

Are you using latest testing branch?

Yes, I’m using the ‘testing’ branch, and I’ve updated everything as well.

Could you tell me what’s the exact logging message you get?

Yep, is this:

INFO:zynthian_ctrldev_manager.setup: Setting-up APC_Key_25_mk2_MIDI_2 in slot 2

Also, how many MIDI devices use the controller? Have separated MIDI devices for control and playing?

Yes, it has two devices, called port 0 and 1, which are MIDI 1 and MIDI 2. Port 0 is used only for the keybed (white and black keys) and for the sustain button. The rest of the buttons, and also the LEDs are in port 1 (MIDI 2).

Thanks for the response!

Could you send your driver’s code? I would review it.

Yes, of course! How can I send it?

UPDATE: I’ve reduced the code to a minimal example, and it still happens. This is the code:

from zyngui.zynthian_ctrldev_manager import zynthian_ctrldev_base

class zynthian_ctrldev_akai_apc_key25_mk2(zynthian_ctrldev_base):
	dev_ids = ["APC_Key_25_mk2_MIDI_2"]
	dev_zynmixer = True

	def init(self): pass
	def end(self): pass
	def refresh(self, force=False): pass

	def midi_event(self, ev):
		print(f"midi {ev:06x}")
		return True

UPDATE 2: I’ve edited the “Capture MIDI from” setting of a chain, and removed every source (as you said, ‘Key 25 MIDI 2’ is disabled and I can not enable it). Then, the chain does not receive events from MIDI 1 (or any other source), but is still receiving events from MIDI 2 :sweat_smile:

Could you send the output of this command?

jack_lsp -cA

Thanks

Copy/paste it here, If you can’t, send by email to fernandoARROBAzynthianDOTorg

Regards

Yes. After a reboot, with a single chain, this is the result:

system:playback_1
   alsa_pcm:hw:Device:in1
   zynmixer:output_a
system:playback_2
   alsa_pcm:hw:Device:in2
   zynmixer:output_b
system:midi_capture_1
   in-hw-2-0-0-APC-Key-25-mk2-MIDI-1
   ZynMidiRouter:dev0_in
system:midi_capture_2
   in-hw-2-0-1-APC-Key-25-mk2-MIDI-2
   ZynMidiRouter:dev1_in
system:midi_playback_1
   out-hw-2-0-0-APC-Key-25-mk2-MIDI-1
   ZynMidiRouter:dev0_out
system:midi_playback_2
   out-hw-2-0-1-APC-Key-25-mk2-MIDI-2
   ZynMidiRouter:dev1_out
ttymidi:MIDI_in
   ZynMidiRouter:dev2_in
ttymidi:MIDI_out
   ZynMidiRouter:dev2_out
a2j:Midi Through [14] (capture): Midi Through Port-0
   ZynMidiRouter:dev3_in
a2j:Midi Through [14] (playback): Midi Through Port-0
   ZynMidiRouter:dev3_out
ZynMidiRouter:ch0_out
   synthv1-00:in
ZynMidiRouter:ch1_out
ZynMidiRouter:ch2_out
ZynMidiRouter:ch3_out
ZynMidiRouter:ch4_out
ZynMidiRouter:ch5_out
ZynMidiRouter:ch6_out
ZynMidiRouter:ch7_out
ZynMidiRouter:ch8_out
ZynMidiRouter:ch9_out
ZynMidiRouter:ch10_out
ZynMidiRouter:ch11_out
ZynMidiRouter:ch12_out
ZynMidiRouter:ch13_out
ZynMidiRouter:ch14_out
ZynMidiRouter:ch15_out
ZynMidiRouter:main_out
   zynsmf:midi_in
ZynMidiRouter:mod_out
ZynMidiRouter:midi_out
   ZynMaster:midi_in
ZynMidiRouter:net_out
ZynMidiRouter:step_out
   zynseq:input
ZynMidiRouter:ctrl_out
ZynMidiRouter:dev0_out
   system:midi_playback_1
ZynMidiRouter:dev1_out
   system:midi_playback_2
ZynMidiRouter:dev2_out
   ttymidi:MIDI_out
ZynMidiRouter:dev3_out
   a2j:Midi Through [14] (playback): Midi Through Port-0
ZynMidiRouter:dev4_out
ZynMidiRouter:dev5_out
ZynMidiRouter:dev6_out
ZynMidiRouter:dev7_out
ZynMidiRouter:dev8_out
ZynMidiRouter:dev9_out
ZynMidiRouter:dev10_out
ZynMidiRouter:dev11_out
ZynMidiRouter:dev12_out
ZynMidiRouter:dev13_out
ZynMidiRouter:dev14_out
ZynMidiRouter:dev15_out
ZynMidiRouter:dev0_in
   system:midi_capture_1
ZynMidiRouter:dev1_in
   system:midi_capture_2
ZynMidiRouter:dev2_in
   ttymidi:MIDI_in
ZynMidiRouter:dev3_in
   a2j:Midi Through [14] (capture): Midi Through Port-0
ZynMidiRouter:dev4_in
ZynMidiRouter:dev5_in
ZynMidiRouter:dev6_in
ZynMidiRouter:dev7_in
ZynMidiRouter:dev8_in
ZynMidiRouter:dev9_in
ZynMidiRouter:dev10_in
ZynMidiRouter:dev11_in
ZynMidiRouter:dev12_in
ZynMidiRouter:dev13_in
ZynMidiRouter:dev14_in
ZynMidiRouter:dev15_in
ZynMidiRouter:net_in
ZynMidiRouter:seq_in
   zynsmf:midi_out
ZynMidiRouter:step_in
   zynseq:output
ZynMidiRouter:ctrl_in
ZynMaster:midi_in
   ZynMidiRouter:midi_out
ZynMaster:midi_out
zynmixer:input_moduia
zynmixer:input_moduib
zynmixer:output_moduia
zynmixer:output_moduib
zynmixer:input_01a
   synthv1-00:Out_L
zynmixer:input_01b
   synthv1-00:Out_R
zynmixer:input_02a
zynmixer:input_02b
zynmixer:input_03a
zynmixer:input_03b
zynmixer:input_04a
zynmixer:input_04b
zynmixer:input_05a
zynmixer:input_05b
zynmixer:input_06a
zynmixer:input_06b
zynmixer:input_07a
zynmixer:input_07b
zynmixer:input_08a
zynmixer:input_08b
zynmixer:input_09a
zynmixer:input_09b
zynmixer:input_10a
zynmixer:input_10b
zynmixer:input_11a
zynmixer:input_11b
zynmixer:input_12a
zynmixer:input_12b
zynmixer:input_13a
zynmixer:input_13b
zynmixer:input_14a
zynmixer:input_14b
zynmixer:input_15a
zynmixer:input_15b
zynmixer:input_16a
zynmixer:input_16b
zynmixer:input_17a
   audioplayer:out_01a
   zynseq:metronome
zynmixer:input_17b
   audioplayer:out_01b
   zynseq:metronome
zynmixer:return_a
zynmixer:return_b
zynmixer:output_a
   system:playback_1
zynmixer:output_b
   system:playback_2
zynmixer:send_a
zynmixer:send_b
zynseq:input
   ZynMidiRouter:step_out
zynseq:output
   ZynMidiRouter:step_in
zynseq:metronome
   zynmixer:input_17a
   zynmixer:input_17b
audioplayer:in
audioplayer:out_01a
   zynmixer:input_17a
audioplayer:out_01b
   zynmixer:input_17b
zynsmf:midi_out
   ZynMidiRouter:seq_in
zynsmf:midi_in
   ZynMidiRouter:main_out
synthv1-00:in
   ZynMidiRouter:ch0_out
synthv1-00:in_L
synthv1-00:in_R
synthv1-00:Out_L
   zynmixer:input_01a
synthv1-00:Out_R
   zynmixer:input_01b

How did you update? From webconf? Command line?

Regards

I updated from the webconf, and also, I did an apt upgrade.

Could you try adding this:

lib_zyncore.zmip_set_route_extdev(self.idev - 1, 0)

to your init function?

This call should “unroute” the input device from the chains and it’s called from the “setup” function in the base class. I don’t understand why the device is still routed to the chains in your setup!

You can get the current routing by calling this.

lib_zyncore.zmop_get_route_from(izmop, izmip)

  • izmop is the chain index (well, indeed is the router’s output port index)
  • izmip is the device index (again, it’s the router’s input port index)

Some thing like this will help debugging:

for i in range(17):
    routed = lib_zyncore.zmop_get_route_from(i, self.idev-1)
    logging.debug(f"Routed to izmop {i} => {routed}")

The routes should be set to 0 for all izmop from 0 to 16.

Regards,

1 Like

Ok, I put that line inside the init function, and I printed the routes immediately after, and everything seems fine (all returns 0). BUT, I also put the print in midi_event, and there, channels from 0 to 15 get routed (zmop_get_route_from returns 1), and only channel 16 get 0.

Something between init and midi_event is changing the routing… :thinking:

UPDATE: If I set the route again the first time midi_event is called, then it stops sending events, and it keeps that way.

UPDATE 2: I’ve added a print inside the C function zmip_set_route_extdev, recompiled, and tested again. It is called only once, in the ctrldev’s setup(); the re-routing must be donde elsewhere.