Midi Feedback, E.g. Toggle The Lights?

Can someone please point me to the code in Duplex that turns OFF/ON the lights on my Akai LPD8? I can’t seem to find it.

A general explanation on how to do this, preferably by piggy backing on Renoise’s “MIDI MAP” feature, is appreciated. Code examples more so.

Where is this concept documented?

Hey, as you know Duplex is my brainchild, so I’ll try to answer your question as thoroughly as possible

Simple question, complex answer: when lights are turned on and off in Duplex, this involves a lot of things. I’m sorry I can’t give you a more straightforward answer than that, but Duplex really is based on a number of abstractions. For example, pre-optimization before the output stage means that a MIDI message which would tell a light to turn on doesn’t necessarily output anything if the light is already turned on. Although it obviously takes a few extra CPU cycles to compute this, it generally makes Duplex way more responsive on MIDI devices which would otherwise “suffocate” when they receive too many messages. As an example, flipping through pages in the matrix on the Launchpad is really snappy.

I could explain how the message is constructed, how it is abstracted from the hardware, etc. but instead, let’s look at how a button is implemented into an application:

  
local c = UIToggleButton(self.display)  
c.group_name = "Some group in my device control-map"  
c.on_change = function(obj)  
 -- do something  
end  
  

In the context of an application class, this is all the code it takes to instantiate a standard toggle-button. It’s a toggle-button, but it can also be made to respond to the whole set of press/hold/release events.
Then, the device configuration acts as a “permanent mapping” between that part of the application and your controller.

You can of course also choose roll your own user-interface components, but you would want to keep them really simple. For example, although possible, it wouldn’t really make any sense to write a “grid” user interface component, because such a thing is too versatile to describe in generic terms. It might work for one application, but the next…? It would be more practical to make a simple, stateless control and then script the hell out of it. For example, Daxton wrote the following button class for the Duplex Stepsequencer - a simple button with no internal state (features separate press/held/release events, but no toggle).

[luabox]

class ‘UIStepSeqButton’ (UIComponent)

function UIStepSeqButton:__init(display)
TRACE(‘UIStepSeqButton:__init’)

UIComponent.__init(self,display)

self.palette = {
foreground = table.rcopy(display.palette.color_1),
}

self.add_listeners(self)

– external event handlers
self.on_press = nil
self.on_release = nil
self.on_hold = nil

end


– user input via button

function UIStepSeqButton:do_press()
TRACE(“UIStepSeqButton:do_press()”)

if (self.on_press ~= nil) then
local msg = self:get_msg()
if not (self.group_name == msg.group_name) then
return
end
if not self:test(msg.column,msg.row) then
return
end
self:on_press()
end

end

– … and release

function UIStepSeqButton:do_release()
TRACE(“UIStepSeqButton:do_release()”)

if (self.on_release ~= nil) then
local msg = self:get_msg()
if not (self.group_name == msg.group_name) then
return
end
if not self:test(msg.column,msg.row) then
return
end
self:on_release()
end

end


– user input via (held) button
– on_hold() is the optional handler method

function UIStepSeqButton:do_hold()
TRACE(“UIStepSeqButton:do_hold()”)

if (self.on_hold ~= nil) then
local msg = self:get_msg()
if not (self.group_name == msg.group_name) then
return
end
if not self:test(msg.column,msg.row) then
return
end
self:on_hold()
end

end


function UIStepSeqButton:draw()
TRACE(“UIStepSeqButton:draw”)

local color = self.palette.foreground
local point = CanvasPoint()
point:apply(color)
– if the color is completely dark, this is also how
– LED buttons will represent the value (turned off)
if(get_color_average(color.color)>0x00)then
point.val = true
else
point.val = false
end
self.canvas:fill(point)
UIComponent.draw(self)

end


function UIStepSeqButton:add_listeners()

self._display.device.message_stream:add_listener(
self, DEVICE_EVENT_BUTTON_PRESSED,
function() self:do_press() end )

self._display.device.message_stream:add_listener(
self,DEVICE_EVENT_BUTTON_HELD,
function() self:do_hold() end )

self._display.device.message_stream:add_listener(
self,DEVICE_EVENT_BUTTON_RELEASED,
function() self:do_release() end )

end


function UIStepSeqButton:remove_listeners()

self._display.device.message_stream:remove_listener(
self,DEVICE_EVENT_BUTTON_PRESSED)

self._display.device.message_stream:remove_listener(
self,DEVICE_EVENT_BUTTON_HELD)

self._display.device.message_stream:remove_listener(
self,DEVICE_EVENT_BUTTON_RELEASED)

end
[/luabox]
Obviously, as this button is lower-level stuff, there’s quite a bit of “framework overhead”. What you are interested in probably lies within the draw() method?

Finally, there’s the question of going through MIDI mapping. This is the path I deliberately chose not to go with Duplex, because of the following limitations:

  • MIDI mapping is one-to-one (could you describe something like Grid Pie with anything but a few hundred individual mappings?). Duplex mappings, on the other hand, are dynamic and group-based - you don’t need to map each button and you don’t need to specify how many buttons your controller has, you only need to provide the name of the control-map group.
  • I want automatic (or one-click) activation of my preferred device configuration(s), without having to go through the process of mapping each song

There’s still limitations in Duplex (no support for relative knobs, >7bit MIDI message support), but they are becoming fewer with each release.

Fascinating.

Right now I’m more interested in code that says: Hey you there, Akai LPD8, turn on your light!

I messed around with this, and the answer is basically:

  
-- ON:   
midi_device:send {0x90, 0x28, 0x7F}  
-- OFF:  
midi_device:send {0x80, 0x28, 0x00}  
  

1st number is the MIDI command, 2nd is the note/variable, 3rd is the velocity.

In Grid Pie, I have mappings named “Grid Pie:Slice 1,1” , “Grid Pie:Slice 1,2”, Etc.

Now, I want to be able to query that mapping in Lua. Something like:

  
local midi_mapping = "Grid Pie:Slice 1,2"  
if renoise.tool():has_midi_mapping(midi_mapping_name) then  
 rprint(renoise.tool():dump_midi_mapping(midi_mapping_name))  
end  
  

With this dump I could figure out, on the fly, which midi message to send?

Is there a way for me to dump_midi_mapping()?

I’m also thinking of a situation where I’d like to copy MIDI MAPPINGS from one song to another. Any insight here?

I totally respect Duplex and it is, obviously, very well thought out. Independent of Duplex, from a purely LUA API standpoint, I would like to see (changed dump() to get() for consistency):

  
-- Dump mapping  
-- Outputs: nil, "CH1|E-3", whatever the user has mapped.  
  
renoise.tool():get_midi_mapping(midi_mapping_name)  
 -> [table, string?]  
  
  
-- Set mapping  
-- Whatever is returned in the previous function can be set here.  
-- I imagine, for example, saving a table of mappings to a file, then loading   
-- that file as mappings into a new song.  
  
renoise.tool():set_midi_mapping(midi_mapping_name, ...)  
  
  
-- Get device  
-- Eg, this should return the same thing as:  
-- `midi_device = renoise.Midi.create_output_device(device_name)`  
-- Ideally, this function is optimized and prevents me from opening the same   
-- device 1000 times. Whatever is most intelligent, do it here.  
  
renoise.tool():get_midi_output_device(midi_mapping_name)  
 -> [midi_device]  
  
  
  

Or, is this already possible somehow?

Yes, of course you can transmit MIDI directly to the device. But what’s the advantage - that the code is something you yourself can understand?
Seems to me that you want to avoid using the framework - because by hardwiring your mappings you are basically bypassing it. That’s fine by me, but then I can’t really see what you want to achieve here?

The whole point of Duplex is to write an application once, and let it run on a number of controllers. And the same code that produce a lit button on a monome via OSC will make a green LED light up via MIDI on a Launchpad.
This is the sort of stuff you really don’t want to deal with, unless you are the slightly masochistic type like me

PS: I promised to port Grid Pie to Duplex when the dust had settled. The time seems to be getting closer?

You say masochistic. I say fun.

I am doing this for my own enjoyment and want to learn more about how my MIDI controller works. Like you say, if I use Duplex I get the benefit of not dealing with this stuff. Right now I’m enjoying figuring out what is going on with my new toy at a lower level.

I’ve been working in web dev for over 15 years. Frameworks come and go. Duplication happens all the time. This is not a comment about Duplex. Just a comment that sometimes it’s fun to learn the basics before inheriting the world. Professionally I don’t use a framework (in a language I can code in) that I can’t fix myself. For fun? Even less so. This “let me deal with this so you don’t have to” is not a selling point for me. I have to start somewhere. My brain doesn’t enjoy lengthy abstractions first, code later. Until I understand what problem is being solved I work the other way around.

I’d say a large portion of developers are like this. It’s a social issue that requires the developer(s) of the Framework to proselytize accordingly.

Yes, I’m still super excited about this!

“Proselytize”, haha I had to look that one up. Speaking of religious references, Duplex is my attempt to avoid the babel tower effect where everyone is re-inventing the wheel over again.

For a masochist, that’s the same thing.

I was hoping this thread would be confined to MIDI/OSC Lua snippets instead of abstraction. But since we’re talking I would like to expand on this:

Duplex is not the first framework in the history of mankind. Pretty sane statement. Just making sure it’s said. Right? :)

A framework is basically a statement. It says “Hey, I’ve thought about this and found a solution to pretty much every problem you will ever have. Trust me.”

Someone gets hired at a company that says use Framework X, there’s no question that Framework X is what said developer will use. Give them a pay check. They’ll even contribute to it.

The other side of the coin is ego. It’s a developer who says “Well, I just got here but there’s no fucking way that this guy/girl is smarter than me. I’m going to do it my way.”

Then, there’s everybody in between.

The Framework has to convince people to use it. Not the other way around. As much as it is technology it’s also a sales pitch. A sales pitch needs to be tailored to the audience.

Duplex has, in my opinion, problems “proselytizing” what it actually does to a broad audience. The way you “pitch it” attracts certain people. On the other hand, turns other types off. I can probably dig up a dozen or so quotes from extremely competent developers saying it’s “overwhelming” or similar.

I would put myself in the middle. I want to use it. But, I also have a life.

Historically, “Awesome Framework X” has often been replaced by “Awesome Framework Y.” JavaScript circa 2006? Dojo, Prototype/Scriptaculous, MooTools, … all pretty decent. 2011? jQuery. Everything else is legacy or particular.

Renoise is obviously a small community. I don’t envision a dozen Duplex clones, but you do have a problem attracting core developers IMHO.

"Record Producer: That’s not an MP, that’s a YP, your problem. " Boogie Nights starring Mark Wahlberg, 1997

You were the one to bring up Duplex, in the context of turning lights on and off. You practically forced me to tell something about the framework

This is where I usually refer to the “Duplex experience levels”:

  • User * you plug in your hardware and run the tool. Alternatively, you don’t have any hardware but is fine with using the virtual control surface
  • Expert * your hardware is not supported, but you’re able to write control-maps and configurations for it (this is how new hardware becomes supported, device configurations are peer reviewed)
  • Hacker * the ability to put together one’s own applications using the Duplex API, or changing features in the API itself.

Good point about developers approaching the framework, and not the other way around. So far, contributions have been plenty and useful, so I’d say the model is working.

Also, those imaginary “core developers” would be focused on the core API? Well, there really isn’t that much work left.
The fun part is making an application, and I’ve already posted a small guide on how to write such a thing.

No, more like instead of giving this answer:

You could do:

Then wait for a reply and do a dialogue. Not a crap flood?

Change your pitch depending on the reader. Don’t turn off this type of learner from using it. Let them discover stuff on their own.

I didn’t see this. Slipped under my radar. Probably another issue that could be looked at.

Sorry that you see things this way. But I won’t go back and change what I wrote, because I think the over-arching concepts are impossible to grasp by reverse-engineering like you suggest.

That’s not what I’m suggesting.

Title of the thread was “Midi Feedback E.g. Toggle the Lights.” I mentioned Duplex because I know Duplex can toggle the lights. The rest, albeit interesting, isn’t helping me do what I want do.

Oh well. Another day, another impasse. That’s cool. Makes it easier to choose where to spend my limited free time doing the Lua I want to do.

Thanks anyway.

Mission accomplished!

Hi nightmorph,

In that scenario, the LED feedback is probably working, not because Ableton support the monome, but because it’s is simply “echoing” the MIDI message back to whatever source it received it from. And in this case, the source is a monome acting as MIDI controller, so the corresponding LED light is turned on. If it was used as an OSC controller, the message going back to the device would need to be different than the one recieved. So in this respect, MIDI is much simpler to work with…

Take the same approach as with Ableton Live, and simply use it as an external MIDI device? No need for scripting anything.

I’m going to ignore all the discussion about frameworks and suggest this document for the original question:

AlphaTrack_Native_1.0.pdf

This is the native mode reference document for the Alphatrack controller which I used to create the AlphaTrack tool.

I found it reasonably easy to understand and it shows how I/O is usually controlled (not all hardware works like this, just most of it in my experience).

Note that buttons send a note on messsage on press and release and the associated light is controlled by sending the same note back again with or without a velocity > 0 (on or off).

Faders are assigned to pitchbend for the additional resolution and LCD displays are usually sysex.

Multichannel DAW controllers such as the full desks work in the same way and one of my ambitions was to have a tool for full mixing desk support (it’s a lack of hardware issue rather than a lack of documentation).

Hope this helps if you want to get into the low-level MIDI details.

I was also wondering about this as a way to, for example, enhance the capabilities of duplex by having certain elements of the controller use duplex and others use simple MIDI mappings (depending on the song) while still having LED response to the state of the mapped controls. It would be really nice to have a low-level tool that sends MIDI output to reflect the state changes of any MIDI-mapped control. I don’t really mind if the solution is easier in the Renoise API or in Duplex but I’m very interested in such a tool and I think it would benefit a lot of users. Can we discuss what would be involved in this?

The problem - or rather - challenge, is that any MIDI signal that is passed to the normal MIDI input in Renoise will be “doubled” because the tool is receiving the same signal. The doubling of messages is a problem because if for example we have selected a Launchpad as MIDI device in Renoise, it’s main grid is assigned to notes as the default. Any tool responding to the notes would work fine, but the notes would also become recorded in the pattern, which is probably not what we intended.

If the script could actively reject a message (i.e. make it so that it never got passed to Renoise), this would be an easy fix. However, we currently don’t have such a thing.
I can think of three solutions to this problem.

First one is that it’s possible to exclude certain messages using the (very basic) MIDI filter in the Renoise MIDI preferences. So you would list the ones that are needed exclusively by the tool.

Second solution is to create a more sophisticated filter in something like Bome’s or MIDI-OX, and then route the resulting virtual MIDI notes to Renoise and the Renoise tool, respectively.

The third approach would be to rely on the Renoise tool to do this work for you. I’m thinking that this wouldn’t be so hard when all the basic MIDI I/O is hooked to a framework like Duplex - in practice it could work like the feature that many tools have for their UI dialogs: when a key is struck, and the tool doesn’t have a handler for it, it gets passed on to Renoise.
And we have the right tools to make this happen, as the OSC server have a few “realtime” methods reserved for exactly this sort of thing. The response time is OK - although a native feature like latency compensation seems to get lost, we are only talking a few hundreds of a second (my tests indicate between 1-3 ms).

You could also remove parts of your controller from the Duplex XML file to “reserve” certain parts for normal MIDI mappings couldn’t you?

Just to start with the simplest solution to the basic problem of integrating basic MIDI talkback into Renoise, ignoring issues with MIDI messages doubling up because of mappings shared between the native Renoise mapper and Duplex. Do you know what would be involved in the following?

  1. maintain a list of all renoise objects that are natively mapped to MIDI inputs
  2. when the state of a mapped renoise object changes, send a MIDI output with a value/velocity that reflects the state of the object

No, you get doubled messages, as long as you have selected the device in the main MIDI input and the tool is listening for messages as well. The tool can’t prevent Renoise from acting on this information.

The simplest solution is to select the device as an external MIDI instrument.

Not the same thing, but a soon-to-arrive Duplex application is called “Keyboard” :ph34r:

To dig out this long discussed topic.

It would be nice to get the following information back, if the user has assigned a midi mapping to a gui-controller (like e.g. a value box):

  • Midi Device
  • Midi CC
  • Midi Channel

With this information, one could send on some events (e.g. page change/parameter change within a tool) the current value back to the assigned device.
Then we have a kind of midi-feedback, to have jumpless parameter changes, if the tool changes the assigned value; Great to use for midi-controllers with endless dials like Behringer BCR2000 or Doepfer Pocket Dial.