If you are creating a custom layer, there is the possibility of even including custom control presets with your layer. This come in handy if your layer has push buttons that controls the behaviour of your layer when its live.
Data Structure
The following data structure is a sample output which needs to be present in your composition under the `tvOut_ControlRepresentations` structure output key:
{
"tvOut_ControlRepresentations": [
{
"title": "Compact Cut's",
"buttons": [
{
"actions": [
{
"layerId": "03627C25-CC8A-474F-A738-C4DEB3353FA4",
"variantId": "F6F52194-06DE-462E-983A-8D505C533A69",
"type": "signal",
"signalKey": "tvGroup_Control__Cut_1_TypeSignal",
"signalName": "Cut 1"
}
],
"liveState": {
"layerId": "03627C25-CC8A-474F-A738-C4DEB3353FA4",
"variantId": "F6F52194-06DE-462E-983A-8D505C533A69",
"dynamicSourceId": "tvIn_VideoSourceAImage"
},
"sourcePreview": {
"layerId": "03627C25-CC8A-474F-A738-C4DEB3353FA4",
"variantId": "F6F52194-06DE-462E-983A-8D505C533A69",
"dynamicSourceId": "tvIn_VideoSourceAImage"
},
"color": "#BBBBBB",
"width": 1,
"height": 1,
"x": 0,
"y": 0,
"icon": "facetime-video",
"dynamicText": {
"template": "{{inputputValues.tvIn_VideoSourceAName}}",
"layerId": "03627C25-CC8A-474F-A738-C4DEB3353FA4",
"variantId": "F6F52194-06DE-462E-983A-8D505C533A69"
}
}
]
}
]
}
A preset has a “title” and an array of “buttons”, but one single button is also ok.
The keys `layerId` and `variantId` need to be filled out dynamically with the string data from the composition input keys `tvIn_LayerIdentifier` and `tvIn_RuntimeIdentifier`.
Using the Remote Control Preset API in Quartz Composer
The build in layers of mimoLive are using a LUA script to dynamical generate this data structure because Quartz Composer doesn’t support generating data structures on it own. The LUA script contains a complex function addButton() that can be used to create the structure for a single button. Usually you just can use this function and don’t need to modify it. Therefor it is quite easy to build new remote control default settings for your own layer.
For example this is the LUA script to generate the remote controls for the Stop Watch layer. Please notice that you can adopt this code by just changing the lines 15 through 29 of the code.
inLayerIdentifier = QC_STRING
inVariantIdentifier = QC_STRING
outControlRepresentations = QC_STRUCT
main = function()
controlRepresentations = {}
-- first representation
controlRepresenation = {}
-- {
controlRepresenation["Title"] = "Stop Watch"
buttons = {}
-- {
-- addButton(buttonsTable, buttonTitle, subTitle, positionX, positionY, width, height, color, icon, actionTarget, liveStateSource, previewSource)
-- first row
addButton(buttons, "{{outputValues.tvOut_TimeString}}", "", 0, 0, 2, 1, "#333333", "", "", "", "")
addButton(buttons, "Live", "{\{name\}}", 2, 0, 1, 1, "#333333", "", "toggleLive", "layerVariant", "")
-- second row
addButton(buttons, "Start", "", 0, 1, 1, 1, "#33cc33", "play", "tvGroup_Control__Start_TypeSignal", "", "")
addButton(buttons, "Stop", "", 1, 1, 1, 1, "#cc3333", "pause", "tvGroup_Control__Stop_TypeSignal", "", "")
addButton(buttons, "Reset", "", 2, 1, 1, 1, "#cccc33", "stop", "tvGroup_Control__Reset_TypeSignal", "", "")
-- }
controlRepresenation["buttons"] = buttons
-- }
table.insert(controlRepresentations , controlRepresenation)
outControlRepresentations = controlRepresentations
end
function addButton(buttonsTable, buttonTitle, buttonSubTitle, positionX, positionY, width, height, color, icon, actionTarget, liveStateSource, previewSource)
local button = {}
-- {
if #buttonTitle > 0 then
if string.find(buttonTitle, "{{") then
-- dynamic titel
local dynamicText = {}
-- {
dynamicText["layerId"] = inLayerIdentifier
dynamicText["variantId"] = inVariantIdentifier
dynamicText["template"] = buttonTitle
-- }
button["dynamicText"] = dynamicText
else
-- static title
button["text"] = buttonTitle
end -- if dynamic
end -- if buttonTitle
if #buttonSubTitle > 0 then
if string.find(buttonSubTitle, "{{") then
-- dynamic titel
local dynamicSubText = {}
-- {
dynamicSubText["layerId"] = inLayerIdentifier
dynamicSubText["variantId"] = inVariantIdentifier
dynamicSubText["template"] = buttonSubTitle
-- }
button["dynamicSubText"] = dynamicSubText
else
-- static title
button["subtext"] = buttonSubTitle
end -- if dynamic
end -- if buttonSubTitle
button["x"] = positionX
button["y"] = positionY
button["width"] = width
button["height"] = height
if #color > 0 then
button["color"] = color
end -- if
if #actionTarget > 0 then
local actions = {}
-- {
local action = {}
-- {
action["layerId"] = inLayerIdentifier
action["variantId"] = inVariantIdentifier
if string.ends(actionTarget, "_TypeSignal") then
action["type"] = "signal"
action["signalKey"] = actionTarget
action["signalName"] = buttonTitle
elseif actionTarget == "toggleLive" then
action["type"] = "toggleLive"
else
action["type"] = "toggleSwitch"
action["toggleSwitchKey"] = actionTarget
action["toggleSwitchName"] = buttonTitle
end -- if
-- }
table.insert(actions , action)
-- }
button["actions"] = actions
end -- if actionTarget
if #liveStateSource > 0 then
local liveState = {}
-- {
liveState["layerId"] = inLayerIdentifier
liveState["variantId"] = inVariantIdentifier
if string.starts(liveStateSource,"tvIn_VideoSource") then
-- get the live state from a dynamic video source
liveState["dynamicSourceId"] = liveStateSource
elseif liveStateSource == "layerVariant" then
-- nor more information needed
else
liveState["propertyPath"] = liveStateSource end -- if
-- }
button["liveState"] = liveState
end -- if liveState Source
if #previewSource > 0 then
local sourcePreview = {}
-- {
sourcePreview["layerId"] = inLayerIdentifier
sourcePreview["variantId"] = inVariantIdentifier
sourcePreview["dynamicSourceId"] = previewSource
-- }
button["sourcePreview"] = sourcePreview
end -- if
if #icon > 0 then
-- See bottom of documentation page for list of avaiable icon names
button["icon"] = icon
end -- if
-- }
table.insert(buttonsTable, button)
end -- func
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function string.ends(String,End)
return End=='' or string.sub(String,-string.len(End))==End
end
Button Positioning
When using multiple buttons, the button layout has to be controlled manually. That means that buttons are positioned on the grid using “x” and “y” attributes, which, just as the “width” and “height” attributes, are given in multiples of grid cells. Positions are relative to the top left grid cell the button group was dragged onto.
Button Actions
Buttons can have multiple actions, one single action, or none at all. There are various valid combinations of action targets and action types.
Note that “textInput” and “numericInput” actions should be on their own, as a consistent user experience in the remote control is not guaranteed.
Action Targets
Action targets are elements inside the mimoLive document. They are addressed in a hierarchical way:
The “document ID” is always drawn at runtime, so it has not to be present in the preset.
- If neither “layerId” nor “variantId” are given, the action will be triggered on the document itself.
- If “layerId” is given, the action will be triggered on the specified layer.
- If “layerId” and “variantId” are given, the action will be triggered on the specified variant. Only in this case, the additional “toggleSwitchKey” or “signalKey” may be specified (see Action Types below).
Action Types
Action Type | Explanation | Additional Attributes | Valid Targets |
---|---|---|---|
toggleLive | Toggle live state | none | Document (Start/Stop show), Layer, Variant |
setLive | Set live state to “live” | none | Document (Start Show), Layer, Variant |
setOff | Set live state to “off” | none | Document (Stop Show), Layer, Variant |
signal | Trigger a signal | signalKey, signalName (recommended) | Variant |
toggleSwitch | Toggle a boolean input value | toggleSwitchKey, toggleSwitchName (recommended) | Variant |
textInput | Opens a text input field that, on submit, updates the value at inputKey. | inputKey, inputName (recommended) | Layer, Variant, Source |
numericInput | Slider that controls a numeric value at inputKey. | inputKey, inputDescription (object with both min and max values), inputName (recommended) | Layer, Variant, Source |
“textInput” and “numericInput” action types are available since mimoLive 5.2.
Button Live State
The “liveState” object can be used to configure the button’s tally light state, which can be on (red marking), transition (yellow marking) or off (no marking).
Live State Sources
Similar to button action targets, the live state can be obtained from multiple sources:
- The “document ID” is always drawn at runtime, so it has not to be present in the preset.
- If neither “layerId” nor “variantId” are given, the document’s live state is used (show has started or not).
- If “layerId” is given, the layer’s live state is used.
- If “layerId” and “variantId” are given, the variant’s live state is used.
– Only in this case, the additional “propertyPath” may be specified to indicate that a boolean property on the variant’s input or output values has to be used.
– Another possibility (mutually exclusive with propertyPath!) is to specify a “dynamicSourceId” property that redirects the live state lookup to a source (fetched by ID), which respects dynamic changes of the selected source (e.g. in the video switcher). The value of “dynamicSourceId” is looked up in the variant’s input values.
*Give the full path to the property, starting at the variant as root, e.g. “inputValues.some_Boolean_Property” or “outputValues.other_Property”.
- If “sourceId” is given, the source’s live state is used.
Button Text
Action buttons can display primary and secondary lines of text, both may be of static or dynamic nature.
Primary Text
This text will be the primary indicator what the button will do for the user. If the button preset contains a text property, it is a static text that will be shown as-is.
Alternatively, a dynamicText can be given which enables the inclusion of mimoLive layer or variant properties using a simple templating syntax. In the template string, regular characters can be combined with property paths enclosed in curly braces:
"template": "{\{propertyPath\}}"
Property paths are evaluated relative to the object that is resolved using “layerId”, “variantId”, “sourceId” and “filterId”. Target resolution follows these rules:
- The “document ID” is always drawn at runtime, so it has not to be present in the preset.
- If neither “layerId” nor “variantId” are given, the property paths are evaluated relative to the document
- If “layerId” (and optionally “variantId”) is given, the property paths are evaluated relative to a layer/variant.
- A variant’s parent layer can be accessed by starting a path with “layer”, such “aslayer.name”.
The first path segment of input and output values has to be camelCased (“inputValues.tvIn…”) and not kebab-cased (“input-values.tvIn…”) as in the API documents.
- Static text will always take precedence over dynamic text, because this allows users to overwrite the dynamic text with some custom text that might be more useful in their situation.
Secondary Text
To give the user more information about a button, such as its parent layer’s name, a secondary text can be given using the “subtext” or “dynamicSubText” properties.
Templating syntax and object resolution follow the exact same rules as the primary text.
Button Text And Icon Size
The text an icons on buttons may vary in size base on the context. With the option “fontScale” you can specify a relative scale to a default size calculated for the current screen size. “1” will be the default size. Less than 1 (e.g. 0.8) will downscale the font size and greater than on (e.g. 1.5) will increase it.
Button Preview
Action buttons can display a preview image of the source they are controlling, or any other source available in the document.
Depending on whether the source is static (only images that were dragged into mimoLive), a static PNG representation of the source is loaded once which does support an alpha channel. For any other source, a JPEG representation of the source is updated regularly. The target refresh rate for dynamic sources is 5 frames per second, but this rate is reduced when network connection speed is found to be insufficient for the repeated loading of preview frames.
It is currently not supported to display the document’s program output on an action button. Layer default images, though not visible in the list of sources, are available for preview.
To specify a source preview, an object resolution mechanism is used:
- The “document ID” is always drawn at runtime, so it has not to be present in the preset.
- If “sourceId” is given, the source with that ID is used for preview.
- If all of “layerId”, “variantId”, and “dynamicSourceId” are given, the source’s ID will be looked up on the specified layer’s variant under the key in “dynamicSourceId”. This can be used to reference sources like the video switcher’s source A-G, even when they are changed in the document.
Button Color
By using the “color” attribute, buttons can be colored with any color code that is representable in HTML (e.g. “#f80”, “#ff0088”, “orange”, or “rgb(255, 66, 88)”).
Button Icon
Buttons can display an icon to indicate their functionality. To see a list of all available icons, open http://localhost:8989/icons/demo.html while mimoLive is running and remote controls are enabled.
Use the plain name of the icon. e.g. “backward” or “arrow-down”.