mvrdevelopment / spec

DIN SPEC 15800 General Device Type Format (GDTF) and My Virtual Rig (MVR) File Format description DIN SPEC 15801
64 stars 13 forks source link

Ambiguous DMXTo Values in ChannelFunctions #103

Open Firionus opened 2 years ago

Firionus commented 2 years ago

Is your feature request related to a problem? Please describe.

According to DIN SPEC 15800:2020-07, ChannelFunctions only specify DMXFrom. The value of DMXTo has to be calculated as DMXFrom - 1 of the "next" ChannelFunction or the maximum value of the DMXChannel. Unfortunately, no restrictions are made in the standard that would make the "next" ChannelFunction uniquely identifiable under all circumstances.

Consider this DMXChannel from a GDTF file produced by the Builder (without any errors):

<DMXChannel DMXBreak="1" Geometry="base" Highlight="None" InitialFunction="base_Dimmer.Dimmer.Dimmer 1" Offset="1">
<LogicalChannel Attribute="Dimmer" DMXChangeTimeLimit="0.000000" Master="None" MibFade="0.000000" Snap="No">
    <ChannelFunction Attribute="Dimmer" DMXFrom="0/1" Default="0/1" ModeFrom="0/1" ModeMaster="base_Control1" ModeTo="255/1" Name="Dimmer 1" OriginalAttribute="" PhysicalFrom="0.000000" PhysicalTo="1.000000" RealAcceleration="0.000000" RealFade="0.000000"/>
    <ChannelFunction Attribute="Pan" DMXFrom="0/1" Default="0/1" ModeFrom="0/1" ModeMaster="base_Control1" ModeTo="255/1" Name="Pan 2" OriginalAttribute="" PhysicalFrom="0.000000" PhysicalTo="1.000000" RealAcceleration="0.000000" RealFade="0.000000"/>
    <ChannelFunction Attribute="Tilt" DMXFrom="128/1" Default="255/1" ModeFrom="0/1" ModeMaster="base_Control1" ModeTo="255/1" Name="Tilt 3" OriginalAttribute="" PhysicalFrom="0.000000" PhysicalTo="1.000000" RealAcceleration="0.000000" RealFade="0.000000"/>
</LogicalChannel>
</DMXChannel>

Besides the fact that these attributes don't make any sense, please note that both "Dimmer 1" and "Pan 2" start at 0 and "Tilt 3" starts at 128. It is now undefined whether "Dimmer 1" or "Pan 2" should stop at 127 and which should stop at 255.

In fact whether you make "Dimmer 1" or "Pan 2" stop at 127 in the builder, in each case the same output is produced, just with a different order of the ChannelFunctions (in the above GDTF, "Dimmer 1" ended at 255 and "Pan 2" ended at 127).

The problem is further exacerbated once you include ModeMaster dependencies because you can create cases where DMXTo changes depending on the value of another channel. Consider this pseudo-GDTF:

<DMXChannel Geometry="base">
<LogicalChannel Attribute="Dimmer">
    <ChannelFunction Attribute="Dimmer" DMXFrom="0/1" Name="Dimmer 1" />
    <ChannelFunction Attribute="Pan" DMXFrom="127/1" 
        ModeFrom="127/1" ModeMaster="base_Control1" ModeTo="255/1" Name="Pan 2" />
</LogicalChannel>
</DMXChannel>

Here, when the ModeMaster "base_Control1" is 0-126, "Dimmer 1" goes from 0 to 255. If "base_Control1" is in 127-255, "Dimmer" goes from 0 to 126.

I don't know about you, but I want the standard to forbid dynamic changes of DMXTo depending on the values of other channels. If that is a needed behavior, just repeat the ChannelFunctions with the appropriate ModeMasters.

Describe the solution you'd like

As we've seen, the extremely lenient GDTF standard makes it easy for implementations like the Builder to produce output that is hard to interpret. Therefore, I would like a clear spec that only allows GDTF files that are easy to interpret.

Clean Slate Approach

If we were to start with a clean slate, I would suggest completely dropping the "LogicalChannel" in favor of a "ModeMasterContainer" that has ModeMaster/ModeFrom/ModeTo as XML-attributes, which would be dropped from ChannelFunction. The ModeMasterContainer would require its children ChannelFunctions to have a unique DMXFrom value.
ModeMasterContainers with duplicate ModeMaster/ModeFrom/ModeTo would be allowed for cases where two attributes need to change depending on the value of a DMXChannel.
The XML-attributes of LogicalChannel would be moved to another child of DMXChannel, named "DMXBehaviors" containing children of type "DMXBehavior". DMXBehaviors has an XML-attribute ModeMaster which can only be a DMXChannel, not a ChannelFunction, to ensure mutual exclusion of the DMXBehaviors. If ModeMaster is not present, only one child DMXBehavior must be present. Each DMXBehavior specifies a DMXFrom and DMXChangeTimeLimit/Master/etc. There must be at least one DMXBehavior with DMXFrom=0 and no two DMXBehaviors can have the same DMXFrom.

This clean slate approach has the following desirable characteristics:

Getting the benefits without changes to the file format

One can get all of the above benefits without changing the file format.

First, we need to somehow enforce mutual exclusivity of the LogicalChannels because its XML-attributes can only be active one at a time. Mutual exclusivity is worded in the standard, but the formulation is muddy and there is no described mechanism that provides this mutual exclusivity. So, I'll propose such a mechanism:

In a given DMXChannel, only one LogicalChannel may be active at a time. A LogicalChannel is active if at least one contained ChannelFunction is active that does not control the Attribute "NoFeature". If multiple LogicalChannels would be active, the first one takes precedence. ChannelFunctions in an inactive LogicalChannel must be considered inactive.

If we put this in the standard, we should remember to also define what "active" means for a ChannelFunction. I consider a ChannelFunction active if:

Unfortunately, the above mechanism introduces a dependence on the order of the LogicalChannels, but I don't see a better way to do it.

Next, we'd have to introduce a mechanism that ensures DMX ranges never intersect, yet allows multiple attributes to be controlled:

Inside each LogicalChannel, ChannelFunctions can be grouped by three values: The ModeMaster string, ModeFrom in the byte count of the ModeMaster channel and ModeTo in the byte count of the ModeMaster channel. In each group where all three values are equal, all values of DMXFrom must occur the same amount of times. The DMXTo value for the ChannelFunctions is determined inside each group, either by the next higher DMXFrom - 1 or the maximum value of the DMXChannel.

Since DMXFrom occurs the same amount of times in each of these groups, it is always clear what the "next" ChannelFunction DMXFrom is. For example, it would be okay to have 2 ChannelFunctions in a group starting at 0 and 2 ChannelFunctions starting at 128. It would not be okay to have 2 ChannelFunctions in a group start at 0 and only 1 ChannelFunction starting at 128, because then we'd have the same situation as in the beginning of this post.

Note that I did not require a ChannelFunction with DMXFrom=0 in each group, so that the groups don't have to form fully filled DMX ranges. While the builder does not allow empty parts at the bottom of the range, the GDTF standard itself currently allows this and I don't see a reason to change that.

The charming characteristic of the above solution is that there are no changes to the file format, only the semantics and validation change from an undefined to a more restricted state. Of course, these changes might still invalidate some old files or require changes to existing code.

I'm looking forward to your comments on my proposal and would love to hear how you've tackled this issue so far in your code bases :smiley:

Firionus commented 2 years ago

Come to think of it, I guess the easier way to handle this issue is to understand "next ChannelFunction" as "next ChannelFunction according to XML order".

So. second proposal for the "DMXTo" issue:

Change Description of ChannelFunction XML-attribute DMXFrom to:

Start DMX value; The end DMX value is calculated as DMXFrom - 1 of the next channel function in XML order, or, if the next channel function has a smaller DMXFrom, the maximum value of the DMX channel. Default value: "0/1".

To make it even more clear that ChannelFunction order has an influence, I'd propose adjusting parts of the LogicalChannel, too:

As children the logical channel has an ordered list of a channel function.

The expressiveness of this is even better than what I suggested above, since one isn't bound to the rule that each DMXFrom value has to occur a certain amount of time for each ModeMaster group.

I like this second version a lot more :joy: It's much less text, much less complicated and probably closer to what you meant originally?

PS: Ensuring the Logical Channels are mutually exclusive are a different topic, really... Shall I move it to a new issue?

moritzstaffel commented 3 months ago

Even this is not a low hanging fruit, I want to take this on our list for the meetings.

moritzstaffel commented 2 months ago

Bildschirmfoto 2024-06-26 um 15 16 09

I think the Builder prevents this now.

moritzstaffel commented 2 months ago

Start DMX value; The end DMX value is calculated as a DMXFrom of the next channel function – 1 or the maximum value of the DMX channel. Default value: "0/1".

We need to define more what the next channel function is. This is depended on: