Control surface midi back channel

Hi,
I am using tracktion engine to implement a control surfaces management mechanism.
I would like to implement full-duplex communication between DAW and control surfaces.
Regarding the Control Surface → Daw communication I have realized the mapping between the incoming MIDI message from the control surface and the specific function of the DAW ( play for example ) , but regarding the opposite communication I am experiencing problems.
Always considering the play function, when the Transport changes I can call the function of the ExternalControlManager class :

void ExternalControllerManager::playStateChanged (bool isPlaying)
{
CRASH_TRACER
FOR_EACH_ACTIVE_DEVICE (playStateChanged (isPlaying));
}

That for each active device will allow a command to be sent to the device.
The problem is that for a device to be active ( in a MIDI context ) it must have a valid MIDI input , i.e. :

bool ExternalController::isEnabled() const
{
if (needsChannel)
return getMidiInputDevice (0).isNotEmpty();

return enabled;
}

So if I do not set an input for Control Surface , even if I want to use only the output channel to the connected control surface , the DAW will not send any message to it.
Would anyone be able to help me ?

Why would you only want to use the output to a device? That’s not full-duplex control.

The question is a different one.
If I do not set an input the device does not appear to be enabled.
In waveform I notice a different behavior :
Once I set the input to a constrol surface and create a mapping, removing that input and leaving only the output channel valid when I press “play” the control surface still receives the associated midi message from the DAW.
I would like to achieve such behavior.
When I do not set the input, even through the “midiLearn” view I notice that the mapping does not appear ( which is correct since an input is needed for midi learn ), nevertheless if I click on “play” the midi message is still sent on the output channel set to the control surface.

To be more clear :

I’ve set an input ad create a mapping, then i remove the input and set the output and when i click from the interface the play the control surface gets the message from the output channel set

I’m using “Midi-OX” to simulate phisical midi in/out ports and i can clearly see that the DAW sends message throught the output port even if no input was setted.
In fact, i’ve mapped the “modulation” control change to the play function and when i click on the play button the Midi-OX monitoring screen shows this :

TBH that sounds like a bug in Waveform.
I don’t think a control surface should be enabled if there is no input set for it.

What scenario would you only want output?

Actually, as you rightly say, there is no real scenario.
My question arose because Waveform 11 ( 64bit) for Windows allowed it and so I thought I was programming wrong, but if you tell me that it is a bug and therefore the framework should not allow it it is okay.

Rather, the mapping is done based on the functions in the AppFunction… is there any way to use custom functions ?

Sorry, this slipped off my radar.

You can create your own ControlSurface subclass and override the acceptMidiMessage function to do whatever you like in there. Is that the kind of thing you’re after?

Not really,
I try to explain myself as best I can :

In the CustomControlSurface class via the function loadFunctions() a menu is constructed consisting of items to which ids belonging to the ActionID enum always defined in the CustomControlSurface class and a callback are associated.

Once the [midi command - action] mapping is created when the midi command is received the corresponding action is triggered.

For example I have the mapping [CC modulation - ActionID::play] , when the CC modulation is received the corresponding action is triggered , in this case the method void play (float val, int param)
is called which calls the AppFunctions::play().

Assuming that I do not want to use the AppFunctions::play() but my own play() method that does other things, I would then have to pass in the loadFunctions() a different callback , but such a function is not virtual and therefore I cannot override it.

Another solution would be to override the method void play (float val, int param) so that it does not call the AppFunctions but a method of mine, but that is not virtual either so I cannot do that.

So I was wondering how to solve such a problem.

Even if i want to add custom functions that is not implemented in the CustomControlSurface, i need to create my ID and then call the “addFunctions()” to append it to the menu… but it’s private and so how i can add custom functions ?

Are you going to let the user configure the control surface mapping or is it going to be hardcoded for each surface you support? If you don’t need user customization, I probably wouldn’t inherit from CustomControlSurface but ControlSurface instead.

You use case isn’t currently supported. You will either need to copy/paste the class or we will need to make some changes.

Yes, i would let users to customize the mapping.
It will be really appreciated if at least the loadFunctions() method colud be virtual so i can put items with custom callback that not belong to the appFunctions…

Ok, I made a bunch of functions virtual. Let me know if that solves your issues.

Great !
Thank you so much ! :slight_smile:
Where i can find the framework version with these changes ?
on the trancktion’s “develop” branch ?

Yes, I just pushed to develop. Also using the latest juce from develop.