padKONTROL in native mode for Duplex

Hello people,

Today I have started to write an extension to Duplex to support the KORG padKONTROL in native mode. It was not that easy to support a device using SYSEX, but I finally got it to work with some ugly hacks.

It supports the segment display (through a parameter of type “label”), all the buttons, dials and the XY pad. The lights also work. However I did not do the “blinking” values for the lights.

The code is available there: https://github.com/valentindavid/padKONTROL-Duplex

There is no interesting configuration yet. Just the code and a control map. But it should be enough to be able to create your own mapping. If you make any interesting configuration, I would like to know about it.

Awesome! This controller doesn’t seem like the most simple one to support, especially getting the segment display to work is a huge thing.

I’m putting some finishing touches on the next release of Duplex and it contains a sequencer that should be really interesting to use on the padKONTROL.
Will surely make a few configurations for it, even though I don’t actually own one of these controllers myself.

Again, great stuff and a great first post.

Thanks.

Today, I have added the possibility to remap pads to custom midi notes and the XY pad to custom CC from Duplex. The USB provides several MIDI devices, the SYSEX native mode is not the same as the standard MIDI. So that means I can make configurations for Duplex that sets the midi note of each pad, or eventually disable them. And those events are received as another device. By default they are now all disabled. We could imagine that some configurations use some pads for Duplex, but some other should be redirected directly to Renoise depending on the current configuration. Eventually I could make a more complex Duplex application that can do the setup live for instance with the rotary encoder.

But to do it in a cleaner way I would like to have hidden UI components and hidden parameters. There is no point showing it in the interface. It actually takes too much space. For the moment I am using labels. But those expects strings, and I need integers. So I need to convert the value when I get it.

It would be also nice do some cleanup into Device.lua, Display.lua and MessageStream.lua. I have noticed a lot of midi and OSC specific code there that makes it hard to implement devices in native mode. In my case I just hacked around and finally found a way to make it work. Ideally, there should be nothing in those three files that should have to do with midi or OSC. The right abstractions should be provided.

Do you have any public repository for the current code of Duplex? If there was one, that would be easier to send you patches to fix those few details. Now, I have no idea how different your working copy is compared to the last beta release.

So, Renoise is basically seeing the padKONTROL as multiple devices? How is this being split up - does the pads register separately from the buttons, etc?
I’m asking because this could be something we’d want to look into.

Duplex right now is assuming that a device is registering as a single port - and a configuration (running any number of applications) is then tied to that device. You can of course run multiple configurations, but then the various applications are running as separate instances, independently from one another…

I’m not entirely sure I get you here - you want to hide certain elements because they take up too much screen real estate, or? I mean, you would only add an on-screen control when it’s supposed to be manipulated somehow…
If it’s a matter of size, I’m sure you know that the on-screen UI can always be closed, and Duplex will continue running in the background (in the new version, it’s even easier: you can now toggle the virtual UI on/off by pressing TAB, but still leave the basic options visible).

The Device class really is quite abstract as it is, the only MIDI/OSC specific code is for creating “settings” dialog - so, just some ViewBuilder stuff. They could of course be refactored into their respective classes, but I hardly found it necessary to do so. Messagestream is also quite abstract - when you see “MIDI” there, it might be because an OSC message actually has a MIDI message as “payload” - as these messages are handy for describing a note event.
The Display class on the other hand, is a mess. It definitely needs critical review :slight_smile:

Actually I just put up the latest bunch of changes on the Renoise SVN - I think you will benefit from the new code comments, I literally spent days tuning everything to the “ldoc” syntax.

Nice ldocs!

It has several midi interfaces, 3 inputs, 2 outputs. I am not certain exactly what they all do. But I know that when using the native mode, then one pair of interfaces is used for SYSEX only, there is no standard midi events. But then another interface has optionally the pads assigned to notes and the x-y pad as well as knobs to CCs or pitch bend. This can controlled from the native mode. So basically, you can have your notes sent to an instrument in Renoise, and have Duplex remapping live what the pads are assigned to. Buttons will not show up as midi events. They can only used as native mode.

This is OK. You can get the pads also as SYSEX. So Duplex has access to everything.

Well, some parameters are read/write, like a pad with light. Some are read-only, like a knob. Some are write-only, like the display. There are configuration parameters that you want to have write-only, but will not show up in the device. It is just configuration, the internal state of the device. In the case I am thinking is the mapping of pads to midi notes (on the other port, not the one used by Duplex). This does not show up on the device visually. It is an internal parameter. I use for the moment labels. But there are around 60 internal parameters. That makes quite some space.

There are some “msg.context == MIDI_NOTE_MESSAGE” around those files for example. But maybe what was meant was whether message can be ON or OFF. It is not totally clear how to build a message correctly.

And yes, I agree that Display is much worse. Especially Display:set_parameter that calls different methods on the device depending on whether some flag says whether the parameter name corresponds to and OSC or MIDI parameter. If it was a statically typed language, it would just not compile.

That is great. I will look at that as soon as I have some time.

Ah, I get it now. Seeing your ‘Blank’ template, there is indeed a lot of space being eaten up by those widgets.

I wonder what would be the optimal way to deal with this ? I mean, having a hidden UIComponent would be an easy fix but still comes with a little bit of overhead, compared to a parameter which had no visual representation at all…

So, prompted by your concerns I have begun to refactor some core classes in Duplex…

First thing that struck was how much of a hack the “xypad” integration was. It is ugly, with code scattered all over the place :wacko:

I’m following some “guidelines” for how to improve this situation, and begun implementing those changes:

1. Refactor UIComponent-specific code into the actual UIComponent class
Not really a fundamental difference from how the Display class currently works, but it makes it much easier to see how the various elements fit together. So, essentially a nice thing for developers.

I imagine it would works by having the various UIComponents expose some static methods that the Display can then choose to use. For example, the ability to validate that required attributes are present in the control-map, and the actual code for building a virtual representation in the Duplex browser UI.

Also, I believe that such a change could make it simpler to write a class specifically for holding the sysex value (potentially, the “invisible labels” could then be defined completely inside a custom UI class). Your input on this would be very much appreciated (I’m planning to share these modifications as soon as there’s something roughly working…)

2. Introducing , a new control-map node
This will allow us to get rid of the ugly XYPad hack, and make it possible to use the native XYPad widget in Renoise for MIDI controllers, too. I suspect this approach could be used for other things as well.

Consider the following XML snippets:

  
-- Example OSC mapping  
<param value="/tilt %f %f" type="xypad" minimum="0" maximum="127">  
  
  
-- Example MIDI mapping  
<param name="XYPad_X" type="xypad">  
<subparam value="CC#56" orientation="vertical" minimum="0" maximum="127"></subparam>  
<subparam value="CC#57" orientation="horizontal" minimum="0" maximum="127"></subparam>  
  

3. Reduce boilerplate code
An application like mlrx contains nearly 10.000 lines of lua code. So, having written all of that I have started looking into how much it could be reduced, while still being readable.

Here’s a couple of examples:

This:

  
local map = self.mappings.label_x  
local c = UILabel(self)  
c.group_name = map.group_name  
c.tooltip = map.description  
c:set_pos(map.index)  
self._controls.label_x = c  
self:_add_component(c)  
  

…becomes

  
self._controls.label_x = UILabel(self,self.mappings.label_x)  
  

Wait, what just happened here? Simple enough, all the basic properties (like group_name, position and tooltip) was assigned by supplying the “mapping” table as an extra argument. Also, the call to Application.add_component() was automatically done when creating the UIComponent

With more complex examples, you can’t expect to save as much typing though:

  
local map = self.mappings.toggle_shuffle_cut  
local c = UIButton(self)  
c.group_name = map.group_name  
c.tooltip = map.description  
c:set_pos(map.index)  
c.on_press = function()   
 -- do something  
end  
self._controls.toggle_shuffle_cut = c  
  

…becomes

  
local c = UIButton(self,self.mappings.toggle_shuffle_cut)  
c.on_press = function()   
 -- do something  
end  
self._controls.toggle_shuffle_cut = c   
  

That sounds interesting. I will try to keep in sync. I was busy the last week working on another project.

I have just committed a bunch of changes, and created a github repository as well.
Hopefully, this will enable a more smooth co-operation (planning to maintain the github one along with google code for now).

Also, I implemented your suggested feature - you can now specify a hidden group in the control-map:

<group visible="false"><br>```

<br>
Still, I feel that we are working around the issue instead of addressing it. It would be nice to have an optimal padKONTROL integration.<br>
<br>
Oh, and I did a fork of your code, too. It's nothing much, just to avoid that running the padKONTROL tool will throw errors). <br>
While poking about your code, I wondered about the 'Constant' application. Generally speaking, you would want an application to do something, what is the intention behind this one?</group>

It is for the output parameters. You might want to set some constant values to some configuration parameters or even to the screen. That makes sense only when you have this output only parameters.