New Tool (2.7/8): Presonus Faderport Implementation


(Airmann) #1

UPDATE 7/11: the FaderPort driver v1.0 has been released. The outcome is a standalone LUA tool. Means: it’s not integrated into duplex as formerly planned. For more details have a look at the tools page

Hi everybody,

I’m totally fascinated by the new LUA scripting framework
and right now I’m implementing support for the Presonus FaderPort DAW controller.

The main thing about the Faderport is it’s professional grade touch-sensitive
moto-fader with a 10 bit (!) resolution. Means values from 0…1023 (fine adjustments are possible)
Therefore, two 7 bit midi messages in a row are sent for each fader position change

This controller is perfectly integrated in all mayor DAW’s, but also in Reaper.
The Reaper control surface driver is licensed under LGPL and a nice template to
copy from.

Here’s my development tactic ;-):

  • create a default 1:1 clone of the Reaper Faderport control surface
  • try to cover as much as possible via midi map / defaults
  • support bi-directional communication for moto-fader movements
  • MOST IMPORTANT: SUPPORT AUTOMATIC TRACK-SWITCHTING NOTIFICATION WITH AUTO-FADER ADJUSTMENT !! - hope that’s technically possible
  • support high-resolution via db level calc function from reaper code
  • support endless pan-trigger
  • Opitonal: support for fader automation rides and buttons read/write/touch
  • Optional: support for native DSP’s ? (Gainer, EQ,Comp,…) AND DSP-SWITCHING LISTENER (?)

Maybe somebody’s interested and wants to help ?

Questions in advance

  • is there a hook/listener possible for track / mixer channel changes ?
  • best way to add own calc functions (new derived class or something ?)
  • are endless triggers for panning supported ?
  • write/read fader rides for automation curves -> any ideas how to implement ?

Reaper SDK with Faderport driver in C++ -> http://www.reaper.fm/sdk/plugin/plugin.php

I will develop this during the next 4 Weeks or so. I will release an alpha here ASAP


OSX: GUI slows down on renoise.app():show_status() calls
FQ: Proper touch automation mode / change value priority
Duplex - Hooks And Sending Midi Messages
(taktik) #2

Is this really a RPN NRPN message or simply a pitchbend message? Pitchbend right now already is supported in Duplex, although only on one channel, RPN NRPN pairs not yet. But because other controllers may need this as well, we should add this to Duplex anyway.

Another thing that we will have to add to Duplex first (this already is in the queue though), is changing the selected track from within the controller. We’ll need a new application for this, “Navigator”.

The Faderport really looks nice! Thanks for taking care of this. We’ll try to help you with any issues.


(Airmann) #3

Hey Taktik,

thanks a lot. Good hint about the NPN/RNPN. Checked it out. It’s neither pitch nor NPN/RNPN.
Instead bank select messge is used. First 0xB 0x00 vv is sent (MSB) and then 0xB 0x20 vv (LSB).

  
See this Reaper event handling code (get code):  
  
if (evt->midi_message[0] == 0xb0)  
 {  
 if (evt->midi_message[1]==0)  
 m_faderport_lasthw=evt->midi_message[2];  
 else if (evt->midi_message[1] == 0x20)  
 {  
 int tid=m_bank_offset;  
 MediaTrack *tr=CSurf_TrackFromID(m_bank_offset,g_csurf_mcpmode);  
  
 if (tr)  
 {  
 if (m_flipmode)  
 {  
 CSurf_SetSurfacePan(tr,CSurf_OnPanChange(tr,int14ToPan(m_faderport_lasthw,evt->midi_message[2]),false),NULL);  
 }  
 else  
 CSurf_SetSurfaceVolume(tr,CSurf_OnVolumeChange(tr,int14ToVol(m_faderport_lasthw,evt->midi_message[2]),false),NULL);  
 }  
 }  
 }  
  
  
//And the set function(s) for fader volume:  
  
void SetSurfaceVolume(MediaTrack *trackid, double volume)   
 {  
 FIXID(id)  
 if (m_midiout && !id && !m_flipmode)  
 {  
 int volint=volToInt14(volume);  
 volint/=16;  
  
 if (m_vol_lastpos!=volint)  
 {  
 m_vol_lastpos=volint;  
 m_midiout->Send(0xb0,0x00,volint>>7,-1);  
 m_midiout->Send(0xb0,0x20,volint&127,-1);  
  
 }  
 }  
 }  
  
// and the calc functions  
  
static double int14ToVol(unsigned char msb, unsigned char lsb)  
{  
 int val=lsb | (msb<<7);  
 double pos=((double)val*1000.0)/16383.0;  
 pos=SLIDER2DB(pos);  
 return DB2VAL(pos);  
}  
  
static int volToInt14(double vol)  
{  
 double d=(DB2SLIDER(VAL2DB(vol))*16383.0/1000.0);  
 if (d<0.0)d=0.0;  
 else if (d>16383.0)d=16383.0;  
  
 return (int)(d+0.5);  
}  
  
  

BTW: the Reapder code contains a “flip mode” with exchanges roles of PAN and FADER. Means: when flip mode is active my moto fader represents PAN and the endless pan-encoder represents VOLUME. Thus it’s possible to fine tune PAN-Values with the moto fader. Great feature IMO. Want to support that, too.

Good to hear. Does it maybe also support navigation between the DSPs of a track ?
Background: I use the gainer-Device very often in my songs. Now imagine the following scenario:

I click on any gainer device in mixer view and voila my FaderPort’s moto fader adjusts automatically to the set gain value. Wow would that be cool ! Makes IMO a lot of sense, because it supports the natural workflow of mixing.

Best Regards

HINT: I attached the Reaper Code


(taktik) #4

oki. But don’t waste too much time understanding the reaper code. A MIDI implementation chart of the Faderport (should be in the manual?) and your ideas for what you want to do with this in Renoise should be enough for Duplex:

  • with the MIDI implementation chart of the Faderport, you can create the raw controlmap XML for duplex. this also will create the virtual UI of the controller in Renoise -> Faderport.xml
  • as soon as you know what to to with the controller in Renoise, write the controller Lua file in Duplex which “binds” your controller to existing duplex apps (control effects, transport, mixing and so on) -> Faderport.lua

You also don’t have to understand all the details of the Duplex code. Just take a look at how other controllers (Launchpad, Behringers and so on) are implemented.


(danoise) #5

The Effect app can be used for that. Simply assign the “device” mapping to some buttons. It can also “automap” the selected device to the controller, like you desire.

As taktik says, check out the other devices in “virtual mode” to see how they work


(Airmann) #6

Hey Taktik,

So far, I couldn’t find any midi implementation chart for the Faderport.
My only sources ATM are the Reaper Code and the MIDI Dumper in Renoise.
I also had looks at the various Duplex drivers and also did some simple mapping.
I’ll try to extend that ASAP and will post it here.

The reason for modeling after Reaper controller is simple: I use both programs and don’t want to have to adapt to different behaviours.
Moreover, I think that the Reaper Implementation is pretty complete and really well done. So it’s helpfull to look at it.
Beside that I have of course also own ideas (like the fx controlling) and will adapt it to those ideas.

Cheers

@Danoise:
Thanks for the hint. Will check it out


(taktik) #7

If the device selection is in the Effect app, track selection could temporarily be added to the mixer app? Until we have a navigator which takes care of all this of course.


(Airmann) #8

Hello,

want to throw in my first ALPHA ALPHA results (see attachement) and give some early feedback.
Good news: I managed to bind master volume to moto fader.
Now when I move moto fader the master volume changes,
and when I move the fader of the Duplex GUI then moto fader is magically adjusted by the motor ! YAY :w00t: !!

I created a little media pack to demonstrate this: http://www.airmann.de/prg/faderport/FaderPort_demo.zip
Pack contains: all LIGHTS ON demo, funky lights demo and moto fader movement by Duplex.

But there’s a general issue with the FaderPort :
it’s obvious that the available mapping possibilities are not generic enough to properly bind the controller features.
Let’s say the controller has almost no own logic - kind of dumb - and is pretty low level.
So the logic must be fully implemented by the driver or application.

in Detail:

the PAN Encoder is controlled by Pitch Shift 0xEn 00 01 (turn one unit right) and 0xEn 00 7E (turn one unit left). Means: the driver must convert those steps into usable panning values. Maybe this can be mapped somehow already ? Haven’t tried it, yet.

Conclusion:

Because the mappings which I need are not available, and because the buttons are not bijective mappable, and because the moto fader needs the bank selects MSB/LSB pair and a convert function, and because the PAN Encoder is special, I had to enhance the FaderPort Class with LUA Code and work directly on MIDI Port Layer. Means: send/receive messages directly to/from midi_out/in. In detail I added some init code to the FaderPort constructor. Also some debug code: Lights test, funky lights test with high resolution fader movement.

For sending moto fader positions I overloaded MidiDevice:send_cc_message(). The fader itself is mapped like any other fader, but this also means the resolution is low 0…127. Means: the fader moves not continuously, but instead jumps over certain values (see also my video below). For high resolution (0…1023) I need to overload more functions I guess. Any hints ?

I really try to use as much from Duplex as I can. I want to integrate nicely and not reinvent the wheel.
But so far the low level way seems to be the only way for me.


(danoise) #9

Wow, some serious investigation on your side!

Without actually having looked at the code, I can tell you as much as that the monome (which is part of the next update) sends a different message than it receives, so the “value” attribute had to be supplemented by another pattern as well. Perhaps we can find some “common ground” here (as mentioned, I have not yet seen how you’ve approached this)?

Increasing the maximum size of the control-map parameters would be the first thing to consider. Next, I think you’d have to check if the MidiDevice.point_to_value() method (which translates individual CanvasPoint values into something that the device can understand) is outputting the correct value. Then, I guess it’s mostly a matter of overriding device methods like you have already done?


(Airmann) #10

hey Danoise :-D,

thanks for the reply. Was really helpfull.

Regarding high resultion fader values:

I just set the range for fader to min=0,max=1023. Modified my overloaded function
and voila now I can send precise values from 0…1023 to control the moto fader. It’s so smooth.

I didn’t have to override MidiDevice:point_to_value(). AFAIK it already does the job.
Only problem is a good rounding function in LUA math. Floor is simply to unprecise. E.g. calculated max value for fader is not 1023 (as expected), but always 1022. BTW: that’s the same for values from 0…127. The max is 126. I think that’s even worse for lower ranges.
Couldn’t Taktik provide a better also quick (!) rounding function ? That would really be helpfull for more precise fader value calculations.

The opposite direction (receiving high resolution values) I still have to implement.

Regarding mapping and Monome

Good hint about the Monome. How is it exactly done ? Via XML map or explicit LUA code ? If it’s done via map: are there maybe different mapping attributes for I/O supported (means: not just one controller number) ? Guess that would be enough for me, too.

The problem in my case: the state (light) is seperated from the trigger buttons and they send - as mentioned - aftertouch messages (0xA0 kk 0/1) when being pressed/released. So far you just support CC,note on off and pitchbend, right ? Why not allow any midi message in mapping (e.g. via something like a raw attribute ?)

Best Regards


(taktik) #11

math.floor rounds down, chops, so:

  
quantized_value = math.floor(value + 0.5)  
  

should do the job.


(Airmann) #12

Thanks a lot Taktik. The method is obvious, but my eyes were covered :-).
Only thing is that according to http://de.wikipedia.org/wiki/Rundung (See Rundung am Computer)
your method (=Banker’s rounding) - despite it’s efficiency - introduces some systematic error / rounding error.
It seems that the better solution would be an algorithm after IEEE-754 standard (round to even).

But for first improvement something like

  
quantized_value = math.floor(value + 0.5555)   
  

would also be better I guess

BTW: can you tell which floating point mechanism / precision modell is internally bound to your LUA Implementation ?
See also: http://lua-users.org/wiki/FloatingPoint


(taktik) #13

We don’t deal with negative values here, and floor() rounds a number downwards, so “math.floor(value + 0.5)” is just fine - really.

Our Lua implementation uses 64 bit double precision floating point numbers internally.


(Airmann) #14

Ok, checked it out on fader. Think you’re right.
Thanks for the info.


(Airmann) #15

Released v1.0 Today: http://code.google.com/p/airmann-faderport-driver, including manual:

http://airmann-faderport-driver.googlecode.com/files/faderport_manual_1_0.pdf


(Conner_Bw) #16

Do you work for Presonus?

This is ridiculously high quality VCR for a script, PDF manual?!

Congrats on the release!


(Airmann) #17

Thanks Conner :)

to answer your question: nope … absolutely not connected to this company, but maybe I’ll ask them for some kind of support.
IMO the manual is necessary to understand what the whole thing is about, and how to use it.
Moreover, it helps me as a developer to keep the overview of the mechanisms behind.


(danoise) #18

Having some risotto and reading your PDF manual is a good combination

The level of sophistication is unbelievable. You’ve done a great job!


(Airmann) #19

Hey Danoise :),

thanks for the motivation kick ;). Your Duplex code was really a big help for doing this (tracing rocks !). Indeed it served me as a template over the full project. I’m sorry that I couldn’t integrate better / as planed. But as you can see this thing has a different “flavour” than the Duplex framework.
That said there is still a common subset of technology - I still have the old “integration approach code”. So this could maybe be used together with the midi charts for a FaderPort Duplex template.

Anyway, the scripting framework is really very well done, too. And the support of you guys is excellent.
So I had a lot of fun using it.

Best regards


(Conner_Bw) #20

Will you be adding this to the tools page?