diff --git a/README.md b/README.md
index 3619088..2a83173 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
-# AtemOSC v2.5.7
+# AtemOSC v3.0.0
## Features
-This is a macOS application, providing an interface to control an ATEM video switcher via OSC.
+This is a macOS application, providing an interface to control an ATEM video switcher via OSC.
-![atemOSC](https://github.com/danielbuechele/atemOSC/raw/master/atemOSC.jpg)
+
-The current version is built for Mac OS 10.15.1 (as of version 2.5.7). A compiled and runnable version of the atemOSC is included which has been built against Blackmagic SDK 8.1 (as of version 2.5.7).
+The current version is built for Mac OS 10.15.1 (since version 2.5.7). A compiled and runnable version of the atemOSC is included which has been built against Blackmagic SDK 8.1 (since version 2.5.7).
## Download the App
@@ -32,36 +32,36 @@ AtemOSC is a proxy, listening for commands following the [OSC protocol](http://o
## OSC API
- - A full overview of the actual OSC-addresses available for your switcher can be obtained from the help-menu inside the application.
- - Unless otherwise specified, send the value 1 along with the OSC address below. Sending any other value may result in the command not being processed.
-
+A full overview of the actual OSC-addresses available for your switcher can be obtained from the help menu inside the application.
### Program and Preview Selection
- - **Black** `/atem/program/0`
+ - **Black** `/atem/program 0`
- - **Cam 1** `/atem/program/1`
- - **Cam 2** `/atem/program/2`
- - **Cam 3** `/atem/program/3`
- - **Cam 4** `/atem/program/4`
- - **Cam 5** `/atem/program/5`
- - **Cam 6** `/atem/program/6`
+ - **Cam 1** `/atem/program 1`
+ - **Cam 2** `/atem/program 2`
+ - **Cam 3** `/atem/program 3`
+ - **Cam 4** `/atem/program 4`
+ - **Cam 5** `/atem/program 5`
+ - **Cam 6** `/atem/program 6`
- and so on...
- - **Color Bars** `/atem/program/1000`
- - **Color 1** `/atem/program/2001`
- - **Color 2** `/atem/program/2002`
- - **Media 1** `/atem/program/3010`
- - **Media 2** `/atem/program/3020`
- - **Key 1 Mask** `/atem/program/4010`
- - **DSK 1 Mask**: `/atem/program/5010`
- - **DSK 2 Mask**: `/atem/program/5020`
- - **Clean Feed 1** `/atem/program/7001`
- - **Clean Feed 2** `/atem/program/7002`
- - **Auxiliary 1** `/atem/program/8001`
+ - **Color Bars** `/atem/program 1000`
+ - **Color 1** `/atem/program 2001`
+ - **Color 2** `/atem/program 2002`
+ - **Media 1** `/atem/program 3010`
+ - **Media 2** `/atem/program 3020`
+ - **Key 1 Mask** `/atem/program 4010`
+ - **DSK 1 Mask**: `/atem/program 5010`
+ - **DSK 2 Mask**: `/atem/program 5020`
+ - **Clean Feed 1** `/atem/program 7001`
+ - **Clean Feed 2** `/atem/program 7002`
+ - **Auxiliary 1** `/atem/program 8001`
- and so on...
-For preview selection `/atem/preview/$i` can be used.
+For preview selection `/atem/preview $i` can be used.
+
+Also supports sending the input in the address instead of as a value (e.g. `/atem/program/5`)
Feedback: Enabled for all values
@@ -74,41 +74,42 @@ Note: You can fetch the names of each input by sending the `/atem/send-status` c
- **T-bar** `/atem/transition/bar <0-1>`
- **Cut** `/atem/transition/cut`
- **Auto** `/atem/transition/auto`
- - **Fade to Black** `/atem/transition/ftb`
- - **Preview Transition** `/atem/transition/preview <0|1>`
+ - **Fade to Black Toggle** `/atem/transition/ftb`
+ - **Preview Transition** `/atem/transition/preview `
To set the transition type of the Auto transition:
- - **Mix** `/atem/transition/set-type/mix`
- - **Dip** `/atem/transition/set-type/dip`
- - **Wipe** `/atem/transition/set-type/wipe`
- - **Stinger** `/atem/transition/set-type/sting`
- - **DVE** `/atem/transition/set-type/dve`
-
+ - **Mix** `/atem/transition/type mix`
+ - **Dip** `/atem/transition/type dip`
+ - **Wipe** `/atem/transition/type wipe`
+ - **Stinger** `/atem/transition/type sting`
+ - **DVE** `/atem/transition/type dve`
+ - Also supports sending the type in the address instead of as a string value (e.g. `/atem/transition/type/dve`)
+
Feedback: None
-
+
### Auxiliary Source Selection
- **Set Aux $i source to $x** `/atem/aux/$i $x`
- - Where `$x` is an integer value that is a valid program source, and can be 1-6 depending on the capability of your ATEM switcher. Check the Help Menu for the correct values.
- - e.g. `/atem/aux/1 1` to set Aux 1 output to source 1 (Camera 1)
+ - Where `$x` is an integer value that is a valid program source, and can be 1-6 depending on the capability of your ATEM switcher. Check the Help Menu for the correct values.
+ - e.g. `/atem/aux/1 1` to set Aux 1 output to source 1 (Camera 1)
Feedback: None
### Upstream Keyers
- - **Set Tie BKGD** `/atem/usk/0/tie <0|1>`
+ - **Set Tie BKGD** `/atem/usk/0/tie `
- Send a value of 1 to enable tie, and 0 to disable
- **Toggle Tie BKGD** `/atem/usk/0/tie/toggle`
- - **Set On-Air Upstream Keyer $i** `/atem/usk/$i/on-air <0|1>`
- - Send a value of 1 to cut the USK on-air, and a value of 0 to cut it off-air
+ - **Set On-Air Upstream Keyer $i** `/atem/usk/$i/on-air `
+ - Send a value of true to cut the USK on-air, and a value of false to cut it off-air
- **Cut Toggle On-Air Upstream Keyer $i** `/atem/usk/$i/on-air/toggle`
- - **Set Tie Upstream Keyer $i** `/atem/usk/$i/tie <0|1>`
- - Send a value of 1 to enable tie, and 0 to disable
+ - **Set Tie Upstream Keyer $i** `/atem/usk/$i/tie `
+ - Send a value of true to enable tie, and false to disable
- **Toggle Tie Upstream Keyer $i** `/atem/usk/$i/tie/toggle`
- - **Set Upstream Keyer $i for Next Scene** `/atem/usk/$i/tie/set-next <0|1>`
- - Send a value of 1 to show the USK after next transition, and 0 if you don’t want to show the USK after next transition
- - e.g. If USK 1 is on air, `/atem/usk/1/tie/set-next 1` will untie USK 1 so that it remains on, while `/atem/usk/1/tie/set-next 0` will tie USK 1 so that it will go off air after the next transition.
+ - **Set Upstream Keyer $i for Next Scene** `/atem/usk/$i/tie/set-next `
+ - Send a boolean value of true to show the USK after next transition, and false if you don’t want to show the USK after next transition
+ - e.g. If USK 1 is on air, `/atem/usk/1/tie/set-next true` will untie USK 1 so that it remains on, while `/atem/usk/1/tie/set-next false` will tie USK 1 so that it will go off air after the next transition.
- **Set Key type for Upstream Keyer $i** `/atem/usk/$i/type `
- Also supports sending the type in the address instead of as a string value (e.g. `/atem/usk/$i/type/luma`)
@@ -137,23 +138,43 @@ Feedback: None
- Float value should be between 0.0 (for 0%) and 1.0 (for 100%)
- **Set "Narrow Chroma Key Range" Parameter for Upstream Keyer $i** `/atem/usk/$i/chroma/narrow `
+#### USK DVE Parameters
+ - **Set DVE Border Enabled for Upstream Keyer $i** `/atem/usk/$i/dve/enabled `
+ - Other values supported are: `border-width-outer`, `border-width-inner`, `border-softness-outer`, `border-softness-inner`, `border-opacity`, `border-hue`, `border-saturation`, and `border-luma`
+
Where `$i` can be 1, 2, 3, or 4 depending on the capability of your ATEM switcher
Feedback: Enabled for '/atem/usk/$i/on-air', '/atem/usk/$i/tie', '/atem/usk/$i/source/*', '/atem/usk/$i/luma/*', and '/atem/usk/$i/chroma/*'
### Downstream Keyers
- - **Set On-Air Downstreamkeyer $i** `/atem/dsk/$i/on-air <0|1>`
- - Send a value of 1 to cut the DSK on-air, and a value of 0 to cut it off-air
+ - **Set On-Air Downstreamkeyer $i** `/atem/dsk/$i/on-air `
+ - Send a value of true to cut the DSK on-air, and a value of false to cut it off-air
- **Auto Toggle On-Air Downstreamkeyer $i** `/atem/dsk/$i/on-air/auto`
- **Cut Toggle On-Air Downstreamkeyer $i** `/atem/dsk/$i/on-air/toggle`
- - **Set Tie Downstreamkeyer $i** `/atem/dsk/$i/tie <0|1>`
- - Send a value of 1 to enable tie, and 0 to disable
+ - **Set Tie Downstreamkeyer $i** `/atem/dsk/$i/tie `
+ - Send a value of true to enable tie, and false to disable
- **Toggle Tie Downstreamkeyer $i** `/atem/dsk/$i/tie/toggle`
- - **Set Downstreamkeyer $i for Next Scene** `/atem/dsk/$i/tie/set-next <0|1>`
- - Send a value of 1 to show the DSK after next transition, and 0 if you don’t want to show the DSK after next transition
- - e.g. If DSK1 is on air, `/atem/dsk/1/tie/set-next 1` will untie DSK1 so that it remains on, while `/atem/dsk/1/tie/set-next 0` will tie DSK1 so that it will go off air after the next transition.
-
+ - **Set Downstreamkeyer $i for Next Scene** `/atem/dsk/$i/tie/set-next `
+ - Send a value of true to show the DSK after next transition, and false if you don’t want to show the DSK after next transition
+ - e.g. If DSK1 is on air, `/atem/dsk/1/tie/set-next true` will untie DSK1 so that it remains on, while `/atem/dsk/1/tie/set-next false` will tie DSK1 so that it will go off air after the next transition.
+
+#### DSK Source
+ - **Set Fill Source for Downstreamkeyer $i** `/atem/dsk/$i/source/fill `
+ - Int value should be the ID of the input to set as the source (from in-app help menu, under the Sources section)
+ - **Set Key (cut) Source for Downstreamkeyer $i** `/atem/dsk/$i/source/cut `
+ - Int value should be the ID of the input to set as the source (from in-app help menu, under the Sources section)
+
+#### DSK Parameters
+ - **Set Clip Parameter for Downstreamkeyer $i** `/atem/dsk/$i/clip `
+ - Float value should be between 0.0 (for 0%) and 1.0 (for 100%)
+ - **Set Gain Parameter for Downstreamkeyer $i** `/atem/dsk/$i/gain `
+ - Float value should be between 0.0 (for 0%) and 1.0 (for 100%)
+ - **Set Pre-multiplied Parameter for Downstreamkeyer $i** `/atem/dsk/$i/pre-multiplied `
+ - **Set Invert Parameter for Downstreamkeyer $i** `/atem/dsk/$i/inverse `
+ - **Set Rate Parameter for Downstreamkeyer $i** `/atem/dsk/$i/rate `
+ - Int value is number of frames, so 30 is 1 second and 60 is 2 seconds (given 30 fps base value)
+
Where `$i` can be 1, 2, 3, or 4 depending on the capability of your ATEM switcher
Feedback: Enabled for '/atem/dsk/$i/on-air' and '/atem/dsk/$i/tie'
@@ -177,41 +198,41 @@ Feedback: Enabled for all values
### Media Players
- - **Set Media Player $i source to Clip $x** `/atem/mplayer/$i/clip/$x`
+ - **Set Media Player $i source to Clip $x** `/atem/mplayer/$i/clip $x`
- Where `$i` can be 1 or 2, and `$x` can be 1 or 2 depending on the capability of your ATEM switcher
- - e.g. `/atem/mplayer/2/clip/1`
- - **Set Media Player $i source to Still $x** `/atem/mplayer/$i/still/$x`
+ - e.g. `/atem/mplayer/2/clip 1`
+ - **Set Media Player $i source to Still $x** `/atem/mplayer/$i/still $x`
- Where `$i` can be 1 or 2, and `$x` can be 1-20 depending on the capability of your ATEM switcher
- - e.g. `/atem/mplayer/1/still/5`
+ - e.g. `/atem/mplayer/1/still 5`
Feedback: None
### SuperSource (when available)
- - **Toggle SuperSource Box $i enabled** `/atem/supersource/$i/enabled <0|1>`
+ - **Toggle SuperSource Box $i enabled** `/atem/supersource/$i/enabled `
- Send a value of 1 to enable, and 0 to disable
- - **Set SuperSource Box $i source to input $x** `/atem/supersource/$i/source $x`
+ - **Set SuperSource Box $i source to input $x** `/atem/supersource/$i/source $x`
- Where `$x` is a valid program source. Check the Help Menu for the correct values.
- - Other options are available. Check the Help Menu in the app for the full list.
+ - Other options are available. Check the Help Menu in the app for the full list.
Feedback: None
### Macros
- - Macros should be recorded within the ATEM Control Panel software.
- - Macros are stored within the ATEM in a 0-index array
+ - Macros should be recorded within the ATEM Control Panel software.
+ - Macros are stored within the ATEM in a 0-index array
- This means that to access the first recorded Macro, you should use an index `$i` of `0`, to access the second recorded Macro, you should use an index of `1` etc.
- - Get the Maximum Number of Macros: `/atem/macros/max-number`
+ - Get the Maximum Number of Macros: `/atem/macros/max-number`
- Returns an `int` of the maximum number of Macros supported by your ATEM
- Access to these Macros should be used via an index of `n-1`
- - Stop the currently active Macro (if any): `/atem/macros/stop`
- - Get the Name of a Macro: `/atem/macros/$i/name`
+ - Stop the currently active Macro (if any): `/atem/macros/stop`
+ - Get the Name of a Macro: `/atem/macros/$i/name`
- Returns a `string` with the name, or "" if the Macro is invalid
- - Get the Description of a Macro: `/atem/macros/$i/description`
+ - Get the Description of a Macro: `/atem/macros/$i/description`
- Returns a `string` with the description, or "" if the Macro is invalid
- - Get whether the Macro at index $i is valid: `/atem/macros/$i/is-valid`
+ - Get whether the Macro at index $i is valid: `/atem/macros/$i/is-valid`
- Returns an `int` of `0|1` to indicate whether the requested Macro is valid
- - Run the Macro at index $i: `/atem/macros/$i/run`
+ - Run the Macro at index $i: `/atem/macros/$i/run`
- Returns an `int` of `0|1` to indicate whether the requested Macro was executed. A `0` will be returned if the Macro is invalid, or does not exist
Feedback: Enabled for `/atem/macros/max-number`, `/atem/macros/$i/name`, `/atem/macros/$i/description`, and `/atem/macros/$i/is-valid`. Also available On-Request (you can send the command to get the value in a return message)
@@ -230,14 +251,30 @@ Feedback: Enabled for `/atem/macros/max-number`, `/atem/macros/$i/name`, `/atem/
- e.g. `/atem/hyperdeck/1/shuttle 0` = stopped
- **Jog Clip on HyperDeck $i** `/atem/hyperdeck/$i/jog $x`
- Where `$x` is an integer value specifying the number of frames to jump forward or backward in the selected clip
+ - **Jump to clip time $x on HyperDeck $i** `/atem/hyperdeck/$i/clip-time $x`
+ - Where `$x` is a string in the format 'hh:mm:ss' (h = hour, m = minute, s = second)
+ - e.g. `/atem/hyperdeck/1/clip-time 00:05:00` = jump 5 minutes into the clip
+ - **Jump to timeline time $x on HyperDeck $i** `/atem/hyperdeck/$i/timeline-time $x`
Feedback: Enabled for `/atem/hyperdeck/$i/clip`. The state of the HyperDeck is available as a string value at `/atem/hyperdeck/$i/state`, and is sent out automatically when the state changes. State options are `play`, `record`, `shuttle`, `idle`, or `unknown`.
### Other
- **Request all feedback available** `/atem/send-status`
- - This will query the switcher and send back the status for the program/preview, transition control, keyers, and macros
- - e.g. This can be used when a new OSC client device is brought online, so that it gets the current status of the system
+ - This will query the switcher and send back the status for the program/preview, transition control, keyers, and macros
+ - e.g. This can be used when a new OSC client device is brought online, so that it gets the current status of the system
+
+### Type Casting
+
+For your convenience, atemOSC will cast certain types to the correct type for certain endpoints
+
+ - If you pass an int or float value of 0 to a boolean method, the 0 will be interpreted as false
+ - If you pass an int or float value of 1 to a boolean method, the 1 will be interpreted as true
+ - If you pass an int value to an endpoint that requires a float, it will be properly converted (and vice-versa)
+
+### TouchOSC Support
+
+Due to the limited capabilities of the TouchOSC client, atemOSC supports an alternative form of passing values. For any method listed above that requires a string or int value, you can pass the string or int as part of the address instead of as a value. For example, you can send `/atem/transition/type/wipe` instead of `/atem/transition/type wipe`, or send `/atem/usk/1/source/fill/3` instead of `/atem/usk/1/source/fill 3`. Additionally, any command sent with one of these alternative addresses and a float value of `0.0` will be ignored, as that represents a button release and commonly causes issues. If you would like to trigger a change on button release instead of button press, simply flip the values in the TouchOSC Editor.
----------
@@ -256,7 +293,7 @@ You are free to open an issue or comment on and existing issue, but the quickest
### Auto and cut commands don’t seem to work, or look buggy, when combining atemOSC with MIDI control
#### Problem
-A lot of MIDI controls send two signals when a button is pressed, one signal when you press down, and another when you release. If you connect the button the `/atem/transition/auto` or `cut`, atemOSC recieves both events and attempts to send the transition command to the switcher twice. This can cause buggy behavior or just not work at all.
+A lot of MIDI controls send two signals when a button is pressed, one signal when you press down, and another when you release. If you connect the button the `/atem/transition/auto` or `cut`, atemOSC recieves both events and attempts to send the transition command to the switcher twice. This can cause buggy behavior or just not work at all.
#### Solution
Tune your MIDI software to send only one of the two signals, either ok button press (rising edge) or button release (falling edge). See #120 for instructions for OSCulator.
@@ -265,6 +302,6 @@ Tune your MIDI software to send only one of the two signals, either ok button pr
## Acknowledgements
-- The code is based on the *SwitcherPanel*-Democode (Version 3.5) provided by Blackmagic.
-- [VVOSC](http://code.google.com/p/vvopensource/) is used as OSC-framework.
-- Program icon based heavily on the ATEM Software Control icon by [Blackmagic Design](http://www.blackmagicdesign.com).
+ - The code is based on the *SwitcherPanel*-Democode (Version 3.5) provided by Blackmagic.
+ - [VVOSC](http://code.google.com/p/vvopensource/) is used as OSC-framework.
+ - Program icon based heavily on the ATEM Software Control icon by [Blackmagic Design](http://www.blackmagicdesign.com).
diff --git a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/Resources/Info.plist b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/Resources/Info.plist
index 3b94c59..1da6496 100644
--- a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/Resources/Info.plist
+++ b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/Resources/Info.plist
@@ -3,7 +3,7 @@
BuildMachineOSBuild
- 19B88
+ 19C57
CFBundleDevelopmentRegion
English
CFBundleExecutable
@@ -27,7 +27,7 @@
DTCompiler
com.apple.compilers.llvm.clang.1_0
DTPlatformBuild
- 11C29
+ 11C504
DTPlatformVersion
GM
DTSDKBuild
@@ -37,7 +37,7 @@
DTXcode
1130
DTXcodeBuild
- 11C29
+ 11C504
LSMinimumSystemVersion
10.10
diff --git a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/VVBasics b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/VVBasics
index ac2e37b..20972b9 100755
Binary files a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/VVBasics and b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/VVBasics differ
diff --git a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/_CodeSignature/CodeResources b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/_CodeSignature/CodeResources
index 870090f..84e833f 100644
--- a/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/_CodeSignature/CodeResources
+++ b/atemOSC.app/Contents/Frameworks/VVBasics.framework/Versions/A/_CodeSignature/CodeResources
@@ -6,7 +6,7 @@
Resources/Info.plist
- aBL+oY/babUC+/4PXQDuGCTbmIw=
+ uxEKDA4xNxNzYQMJwxftRXcOP2M=
Resources/LICENSE
@@ -19,11 +19,11 @@
hash
- aBL+oY/babUC+/4PXQDuGCTbmIw=
+ uxEKDA4xNxNzYQMJwxftRXcOP2M=
hash2
- I3kcVlezeZlWoLli8U0I6OCnnNyQPKeZEthbPS+ES+s=
+ JtIQ5rzuMY6YYg90jOvb5iAZ4wutfFEj3yExFg+9Q/E=
Resources/LICENSE
diff --git a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/Resources/Info.plist b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/Resources/Info.plist
index 59cc03a..2f84748 100644
--- a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/Resources/Info.plist
+++ b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/Resources/Info.plist
@@ -3,7 +3,7 @@
BuildMachineOSBuild
- 19B88
+ 19C57
CFBundleDevelopmentRegion
English
CFBundleExecutable
@@ -27,7 +27,7 @@
DTCompiler
com.apple.compilers.llvm.clang.1_0
DTPlatformBuild
- 11C29
+ 11C504
DTPlatformVersion
GM
DTSDKBuild
@@ -37,7 +37,7 @@
DTXcode
1130
DTXcodeBuild
- 11C29
+ 11C504
LSMinimumSystemVersion
10.10
diff --git a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/VVOSC b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/VVOSC
index aa82879..47db49d 100755
Binary files a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/VVOSC and b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/VVOSC differ
diff --git a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/_CodeSignature/CodeResources b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/_CodeSignature/CodeResources
index fcfbce9..6ce12d0 100644
--- a/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/_CodeSignature/CodeResources
+++ b/atemOSC.app/Contents/Frameworks/VVOSC.framework/Versions/A/_CodeSignature/CodeResources
@@ -6,7 +6,7 @@
Resources/Info.plist
- IsW4dK+rcFdi1JzfNodOnrc5F/Y=
+ mqULvg+wkarUvTMUapHTROcyujM=
files2
@@ -15,11 +15,11 @@
hash
- IsW4dK+rcFdi1JzfNodOnrc5F/Y=
+ mqULvg+wkarUvTMUapHTROcyujM=
hash2
- RYrLPHhxjjDUYtKfdyUwNSb4fw5I1TVqQ8VgYW6JBNY=
+ YIoogBUi8DzA62rHenVRfCuKfeALHv16pllebYunLTY=
diff --git a/atemOSC.app/Contents/Info.plist b/atemOSC.app/Contents/Info.plist
index 52691dc..ba09ca3 100644
--- a/atemOSC.app/Contents/Info.plist
+++ b/atemOSC.app/Contents/Info.plist
@@ -3,7 +3,7 @@
BuildMachineOSBuild
- 19B88
+ 19C57
CFBundleDevelopmentRegion
English
CFBundleExecutable
@@ -20,6 +20,8 @@
atemOSC
CFBundlePackageType
APPL
+ CFBundleShortVersionString
+ 3.0.0
CFBundleSignature
????
CFBundleSupportedPlatforms
@@ -31,7 +33,7 @@
DTCompiler
com.apple.compilers.llvm.clang.1_0
DTPlatformBuild
- 11C29
+ 11C504
DTPlatformVersion
GM
DTSDKBuild
@@ -41,7 +43,7 @@
DTXcode
1130
DTXcodeBuild
- 11C29
+ 11C504
LSMinimumSystemVersion
10.9
NSMainNibFile
diff --git a/atemOSC.app/Contents/MacOS/atemOSC b/atemOSC.app/Contents/MacOS/atemOSC
index 429db61..e99999e 100755
Binary files a/atemOSC.app/Contents/MacOS/atemOSC and b/atemOSC.app/Contents/MacOS/atemOSC differ
diff --git a/atemOSC.app/Contents/Resources/Assets.car b/atemOSC.app/Contents/Resources/Assets.car
index 25ca199..59fe4e3 100644
Binary files a/atemOSC.app/Contents/Resources/Assets.car and b/atemOSC.app/Contents/Resources/Assets.car differ
diff --git a/atemOSC.app/Contents/Resources/English.lproj/MainMenu.nib b/atemOSC.app/Contents/Resources/English.lproj/MainMenu.nib
index 7b8a191..f024610 100644
Binary files a/atemOSC.app/Contents/Resources/English.lproj/MainMenu.nib and b/atemOSC.app/Contents/Resources/English.lproj/MainMenu.nib differ
diff --git a/atemOSC.app/Contents/_CodeSignature/CodeResources b/atemOSC.app/Contents/_CodeSignature/CodeResources
index cb79d42..a102782 100644
--- a/atemOSC.app/Contents/_CodeSignature/CodeResources
+++ b/atemOSC.app/Contents/_CodeSignature/CodeResources
@@ -10,7 +10,7 @@
Resources/Assets.car
- 9gmHOetLoTsB+b0r87t6MoQuORc=
+ b6iNnrhvUAfxRlrhCZbpnJgvRho=
Resources/English.lproj/InfoPlist.strings
@@ -25,7 +25,7 @@
hash
- V+dzOHGDjeHhBzzBXteotCCe+gc=
+ wBuaKUn4VZeFg2mKnDZt4E//u8M=
optional
@@ -41,7 +41,7 @@
cdhash
- n3+ahAsOMfbLU+FvA8mFDklLWvY=
+ VnUyxGww9kziBAKkzjrSQUs1Nnk=
requirement
identifier "com.yourcompany.VVBasics" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: Ken Steffey (649CFX9WPE)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */
@@ -50,7 +50,7 @@
cdhash
- 4rqnrWBG8OshBChmyX5Yx8jesuA=
+ XFVVMK3iviuw2KR4y+2msv0xias=
requirement
identifier "com.yourcompany.VVOSC" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: Ken Steffey (649CFX9WPE)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */
@@ -70,11 +70,11 @@
hash
- 9gmHOetLoTsB+b0r87t6MoQuORc=
+ b6iNnrhvUAfxRlrhCZbpnJgvRho=
hash2
- oxvdPooL+X1LHZZzaZyTDJ/kHpUx4C2RLtRvjbSEMvs=
+ WlH2b62MgYZ9yW2C0fu9CkGN6RKRzC8m6nISEYMQM3Q=
Resources/English.lproj/InfoPlist.strings
@@ -94,11 +94,11 @@
hash
- V+dzOHGDjeHhBzzBXteotCCe+gc=
+ wBuaKUn4VZeFg2mKnDZt4E//u8M=
hash2
- /zb8JN1gbA8IBmz7W6Z6gq5on6+sgO1ad+gqZMK+I/I=
+ 2yOOtP93uzCJP0oVdMX9JJmAmC3+1tIBHx2urhDwi0s=
optional
diff --git a/atemOSC.jpg b/atemOSC.jpg
deleted file mode 100644
index e72f6d0..0000000
Binary files a/atemOSC.jpg and /dev/null differ
diff --git a/atemOSC/AppDelegate.h b/atemOSC/AppDelegate.h
index 9f1e2c4..f72efdb 100644
--- a/atemOSC/AppDelegate.h
+++ b/atemOSC/AppDelegate.h
@@ -33,6 +33,7 @@
#import
#import "FeedbackMonitors.h"
+#import "OSCEndpoint.h"
@class OSCAddressPanel;
@class OSCReceiver;
@@ -87,6 +88,8 @@
@property (strong) IBOutlet NSWindow* window;
@property (strong) id activity;
+@property(nonatomic, retain) NSMutableArray *endpoints;
+
- (void)connectBMD;
- (void)portChanged:(int)inPortValue out:(int)outPortValue ip:(NSString *)outIpStr;
- (IBAction)githubPageButtonPressed:(id)sender;
diff --git a/atemOSC/AppDelegate.mm b/atemOSC/AppDelegate.mm
index 5d0783f..9393e04 100644
--- a/atemOSC/AppDelegate.mm
+++ b/atemOSC/AppDelegate.mm
@@ -29,6 +29,7 @@
#include
#import "OSCAddressPanel.h"
#import "SettingsWindow.h"
+#import "OSCReceiver.h"
@implementation AppDelegate
@@ -58,6 +59,7 @@ @implementation AppDelegate
@synthesize mSwitcher;
@synthesize mHyperdecks;
@synthesize mHyperdeckMonitors;
+@synthesize endpoints;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
@@ -76,6 +78,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
mMacroPool = NULL;
isConnectedToATEM = NO;
+ endpoints = [[NSMutableArray alloc] init];
mOscReceiver = [[OSCReceiver alloc] initWithDelegate:self];
mSwitcherMonitor = new SwitcherMonitor(self);
@@ -111,8 +114,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self switcherDisconnected]; // start with switcher disconnected
- // make an osc manager- i'm using a custom in-port to record a bunch of extra conversion for the display, but you can just make a "normal" manager
manager = [[OSCManager alloc] init];
+ [manager setDelegate:mOscReceiver];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
@@ -158,17 +161,20 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
- (void)portChanged:(int)inPortValue out:(int)outPortValue ip:(NSString *)outIpStr
{
- [manager removeInput:inPort];
-
- if (outIpStr != nil)
- {
- [manager removeOutput:outPort];
+ if (inPort == nil)
+ inPort = [manager createNewInputForPort:inPortValue withLabel:@"atemOSC"];
+ else if (inPortValue != [inPort port])
+ [inPort setPort:inPortValue];
+
+ if (outPort == nil)
outPort = [manager createNewOutputToAddress:outIpStr atPort:outPortValue withLabel:@"atemOSC"];
+ else
+ {
+ if (![outIpStr isEqualToString: [outPort addressString]])
+ [outPort setAddressString:outIpStr];
+ if (outPortValue != [outPort port])
+ [outPort setPort:outPortValue];
}
-
- inPort = [manager createNewInputForPort:inPortValue withLabel:@"atemOSC"];
-
- [manager setDelegate:mOscReceiver];
}
- (void)applicationWillTerminate:(NSNotification*)aNotification
@@ -508,12 +514,16 @@ - (void)switcherDisconnected
self.activity = nil;
- OSCMessage *newMsg = [OSCMessage createWithAddress:@"/atem/led/green"];
- [newMsg addFloat:0.0];
- [outPort sendThisMessage:newMsg];
- newMsg = [OSCMessage createWithAddress:@"/atem/led/red"];
- [newMsg addFloat:1.0];
- [outPort sendThisMessage:newMsg];
+ if (outPort != nil)
+ {
+ OSCMessage *newMsg = [OSCMessage createWithAddress:@"/atem/led/green"];
+ [newMsg addFloat:0.0];
+ [outPort sendThisMessage:newMsg];
+ newMsg = [OSCMessage createWithAddress:@"/atem/led/red"];
+ [newMsg addFloat:1.0];
+ [outPort sendThisMessage:newMsg];
+ }
+
[(SettingsWindow *)window showSwitcherDisconnected];
@@ -656,6 +666,7 @@ - (void)logMessage:(NSString *)message
if (message) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendMessage:message];
+ [(SettingsWindow *)window updateLogLabel:message];
});
NSLog(@"%@", message);
}
diff --git a/atemOSC/Base.lproj/MainMenu.xib b/atemOSC/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..8bb745b
--- /dev/null
+++ b/atemOSC/Base.lproj/MainMenu.xib
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/atemOSC/English.lproj/MainMenu.xib b/atemOSC/English.lproj/MainMenu.xib
index 8bb745b..77160f0 100644
--- a/atemOSC/English.lproj/MainMenu.xib
+++ b/atemOSC/English.lproj/MainMenu.xib
@@ -1,8 +1,8 @@
-
+
-
+
@@ -117,18 +117,19 @@
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -136,20 +137,20 @@
-
-
+
+
-
+
-
-
+
+
@@ -157,8 +158,8 @@
-
-
+
+
@@ -166,8 +167,8 @@
-
-
+
+
@@ -175,8 +176,8 @@
-
-
+
+
@@ -184,12 +185,12 @@
-
-
+
+
-
+
@@ -197,24 +198,24 @@
-
-
+
+
-
+
-
-
+
+
-
+
@@ -222,8 +223,8 @@
-
-
+
+
@@ -231,28 +232,71 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -266,23 +310,23 @@
-
+
-
+
-
+
-
+
-
+
@@ -293,7 +337,7 @@
-
+
@@ -303,11 +347,11 @@
-
+
-
+
@@ -319,23 +363,23 @@
-
+
-
+
-
+
-
+
-
+
@@ -347,18 +391,18 @@
-
+
-
+
-
+
diff --git a/atemOSC/FeedbackMonitors.mm b/atemOSC/FeedbackMonitors.mm
index cb77089..0f56df4 100644
--- a/atemOSC/FeedbackMonitors.mm
+++ b/atemOSC/FeedbackMonitors.mm
@@ -85,6 +85,10 @@
OSCMessage *newMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/program/%lld",it.first]];
if (programId==it.first) {[newMsg addFloat:1.0];} else {[newMsg addFloat:0.0];}
[static_cast(appDel).outPort sendThisMessage:newMsg];
+
+ OSCMessage *newMsg2 = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/program"]];
+ [newMsg2 addInt:(int)it.first];
+ [static_cast(appDel).outPort sendThisMessage:newMsg2];
}
}
@@ -98,6 +102,10 @@
OSCMessage *newMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/preview/%lld",it.first]];
if (previewId==it.first) {[newMsg addFloat:1.0];} else {[newMsg addFloat:0.0];}
[static_cast(appDel).outPort sendThisMessage:newMsg];
+
+ OSCMessage *newMsg2 = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/preview"]];
+ [newMsg2 addInt:(int)it.first];
+ [static_cast(appDel).outPort sendThisMessage:newMsg2];
}
}
@@ -233,11 +241,6 @@
bool isTied;
key->GetTie(&isTied);
- // Deprecated
- OSCMessage *oldMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/dsk/set-tie/%d",i]];
- [oldMsg addInt: isTied];
- [static_cast(appDel).outPort sendThisMessage:oldMsg];
-
OSCMessage *newMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/dsk/%d/tie",i]];
[newMsg addInt: isTied];
[static_cast(appDel).outPort sendThisMessage:newMsg];
@@ -255,11 +258,6 @@
bool isOnAir;
key->GetOnAir(&isOnAir);
- // Deprecated
- OSCMessage *oldMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/dsk/on-air/%d",i]];
- [oldMsg addInt: isOnAir];
- [static_cast(appDel).outPort sendThisMessage:oldMsg];
-
OSCMessage *newMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/dsk/%d/on-air",i]];
[newMsg addInt: isOnAir];
[static_cast(appDel).outPort sendThisMessage:newMsg];
@@ -651,11 +649,6 @@
for (int i = 0; i <= ((int) reinterpret_cast(appDel).keyers.size()); i++) {
uint32_t requestedTransitionSelection = transitionSelections[i];
-
- // Deprecated
- OSCMessage *oldMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/nextusk/%d",i]];
- [oldMsg addInt: ((requestedTransitionSelection & currentTransitionSelection) == requestedTransitionSelection)];
- [static_cast(appDel).outPort sendThisMessage:oldMsg];
OSCMessage *newMsg = [OSCMessage createWithAddress:[NSString stringWithFormat:@"/atem/usk/%d/tie",i]];
[newMsg addInt: ((requestedTransitionSelection & currentTransitionSelection) == requestedTransitionSelection)];
diff --git a/atemOSC/OSCAddressPanel.mm b/atemOSC/OSCAddressPanel.mm
index 980d118..c3c8635 100644
--- a/atemOSC/OSCAddressPanel.mm
+++ b/atemOSC/OSCAddressPanel.mm
@@ -8,6 +8,7 @@
#import "OSCAddressPanel.h"
#import "BMDSwitcherAPI.h"
#import "AppDelegate.h"
+#import "OSCEndpoint.h"
@implementation OSCAddressPanel
@@ -48,44 +49,7 @@ - (void)setupWithDelegate:(AppDelegate *)appDel
[self addEntry:@"Set to Wipe" forAddress:@"/atem/transition/set-type/wipe" toString:helpString];
[self addEntry:@"Set to Stinger" forAddress:@"/atem/transition/set-type/sting" toString:helpString];
[self addEntry:@"Set to DVE" forAddress:@"/atem/transition/set-type/dve" toString:helpString];
-
- [self addHeader:@"Upstream Keyers" toString:helpString];
- [self addEntry:@"Set Tie BKGD" forAddress:@"/atem/usk/0/tie" toString:helpString];
- [self addEntry:@"Toggle Tie BKGD" forAddress:@"/atem/usk/0/tie/toggle" toString:helpString];
- for (int i = 0; i<[appDel keyers].size();i++)
- {
- [self addEntry:[NSString stringWithFormat:@"Set USK%d On Air",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/on-air\t<0|1>",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Toggle On Air USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/on-air/toggle",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Tie USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/tie\t<0|1>",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Toggle Tie USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/tie/toggle",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Next-Transition State USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/tie/set-next\t<0|1>",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Fill Source USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/source/fill\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Cut Source USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/source/cut\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Clip Luma Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/luma/clip\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Gain Luma Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/luma/gain\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Pre-Multiplied Luma Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/luma/pre-multiplied\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Inverse Luma Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/luma/inverse\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Enabled DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-enabled/true",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Inner Width DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-width-inner\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Outer Width DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-width-outer\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Inner Softness DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-softness-inner\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Outer Softness DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-softness-outer\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Hue DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-hue\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Saturation DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-saturation\t",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Border Luman DVE Parameter USK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/usk/%d/dve/border-luma\t",i+1] toString:helpString];
- }
-
- [self addHeader:@"Downstream Keyers" toString:helpString];
- for (int i = 0; i<[appDel dsk].size();i++)
- {
- [self addEntry:[NSString stringWithFormat:@"Set DSK%d On Air",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/on-air\t<0|1>",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Auto-Transistion DSK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/on-air/auto",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Toggle On Air DSK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/on-air/toggle",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Tie DSK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/tie\t<0|1>",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Toggle Tie DSK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/tie/toggle",i+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Next-Transition State DSK%d",i+1] forAddress:[NSString stringWithFormat:@"/atem/dsk/%d/tie/set-next\t<0|1>",i+1] toString:helpString];
- }
-
+
[self addHeader:@"Sources" toString:helpString];
HRESULT result;
@@ -117,6 +81,52 @@ - (void)setupWithDelegate:(AppDelegate *)appDel
}
inputIterator->Release();
+ [self addHeader:@"Upstream Keyers" toString:helpString];
+ [self addEntry:@"Set Tie BKGD" forAddress:@"/atem/usk/0/tie" toString:helpString];
+ [self addEntry:@"Toggle Tie BKGD" forAddress:@"/atem/usk/0/tie/toggle" toString:helpString];
+ for (int i = 0; i<[appDel keyers].size();i++)
+ {
+ for (OSCEndpoint* endpoint : [appDel endpoints])
+ {
+ if ([[endpoint addressTemplate] containsString:@"/usk/"])
+ {
+ NSString *label = [[endpoint label] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:(i+1)] stringValue]];
+ NSString *address = [[endpoint addressTemplate] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:(i+1)] stringValue]];
+ if (endpoint.valueType == OSCValInt)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValBool)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValFloat)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValString)
+ address = [address stringByAppendingString:@" "];
+ [self addEntry:label forAddress:address toString:helpString];
+ }
+ }
+ }
+
+ [self addHeader:@"Downstream Keyers" toString:helpString];
+ for (int i = 0; i<[appDel dsk].size();i++)
+ {
+ for (OSCEndpoint* endpoint : [appDel endpoints])
+ {
+ if ([[endpoint addressTemplate] containsString:@"/dsk/"])
+ {
+ NSString *label = [[endpoint label] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:(i+1)] stringValue]];
+ NSString *address = [[endpoint addressTemplate] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:(i+1)] stringValue]];
+ if (endpoint.valueType == OSCValInt)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValBool)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValFloat)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValString)
+ address = [address stringByAppendingString:@" "];
+ [self addEntry:label forAddress:address toString:helpString];
+ }
+ }
+ }
+
[self addHeader:@"Audio Inputs" toString:helpString];
for (auto const& it : [appDel mAudioInputs])
@@ -198,45 +208,76 @@ - (void)setupWithDelegate:(AppDelegate *)appDel
for (int i = 1; i <= [appDel mSuperSourceBoxes].size(); i++)
{
- [self addEntry:[NSString stringWithFormat:@"Set Box %d enabled",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/enabled\t<0|1>",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Input source",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/source\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Position X",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/x\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Position Y",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/y\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Size",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/size\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Cropped Enabled",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/cropped\t<0|1>",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Crop Top",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/crop-top\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Crop Bottom",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/crop-bottom\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Crop Left",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/crop-left\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set Box %d Crop Right",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/crop-right\t",i] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Reset Box %d Crop",i] forAddress:[NSString stringWithFormat:@"/atem/supersource/box/%d/crop-reset\t<1>",i] toString:helpString];
+ for (OSCEndpoint* endpoint : [appDel endpoints])
+ {
+ if ([[endpoint addressTemplate] containsString:@"/supersource/"])
+ {
+ NSString *label = [[endpoint label] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:i] stringValue]];
+ NSString *address = [[endpoint addressTemplate] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithInt:i] stringValue]];
+ if (endpoint.valueType == OSCValInt)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValBool)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValFloat)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValString)
+ address = [address stringByAppendingString:@" "];
+ [self addEntry:label forAddress:address toString:helpString];
+ }
+ }
}
}
[self addHeader:@"Macros" toString:helpString];
- [self addEntry:@"Get the Maximum Number of Macros" forAddress:@"/atem/macros/max-number" toString:helpString];
- [self addEntry:@"Stop the currently active Macro (if any)" forAddress:@"/atem/macros/stop" toString:helpString];
- [self addEntry:@"Get the Name of a Macro" forAddress:@"/atem/macros//name" toString:helpString];
- [self addEntry:@"Get the Description of a Macro" forAddress:@"/atem/macros//description" toString:helpString];
- [self addEntry:@"Get whether the Macro at is valid" forAddress:@"/atem/macros//is-valid" toString:helpString];
- [self addEntry:@"Run the Macro at " forAddress:@"/atem/macros//run" toString:helpString];
+ for (OSCEndpoint* endpoint : [appDel endpoints])
+ {
+ if ([[endpoint addressTemplate] containsString:@"/macros/"])
+ {
+ NSString *address = [endpoint addressTemplate];
+ if (endpoint.valueType == OSCValInt)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValBool)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValFloat)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValString)
+ address = [address stringByAppendingString:@" "];
+ [self addEntry:[endpoint label] forAddress:address toString:helpString];
+ }
+ }
- [self addHeader:@"HyperDecks" toString:helpString];
for (auto const& it : [appDel mHyperdecks])
{
BMDSwitcherHyperDeckConnectionStatus status;
it.second->GetConnectionStatus(&status);
if (status == bmdSwitcherHyperDeckConnectionStatusConnected)
{
- [self addEntry:[NSString stringWithFormat:@"HyperDeck %lld Play", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/play", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"HyperDeck %lld Stop", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/stop", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"HyperDeck %lld Record", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/record", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"HyperDeck %lld Shuttle", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/shuttle\t", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"HyperDeck %lld Jog", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/jog\t", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set HyperDeck %lld Current Clip Number", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/clip\t", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set HyperDeck %lld Current Clip Time", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/clip-time\t", it.first+1] toString:helpString];
- [self addEntry:[NSString stringWithFormat:@"Set HyperDeck %lld Current Timeline Time", it.first+1] forAddress:[NSString stringWithFormat:@"/atem/hyperdeck/%lld/timeline-time\t", it.first+1] toString:helpString];
+ if (it.first == 0)
+ [self addHeader:@"HyperDecks" toString:helpString];
+
+ for (OSCEndpoint* endpoint : [appDel endpoints])
+ {
+ if ([[endpoint addressTemplate] containsString:@"/hyperdeck/"])
+ {
+ NSString *label = [[endpoint label] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithLongLong:it.first+1] stringValue]];
+ NSString *address = [[endpoint addressTemplate] stringByReplacingOccurrencesOfString:@"" withString:[[NSNumber numberWithLongLong:it.first+1] stringValue]];
+ if (endpoint.valueType == OSCValInt)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValBool)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValFloat)
+ address = [address stringByAppendingString:@" "];
+ else if (endpoint.valueType == OSCValString)
+ address = [address stringByAppendingString:@" "];
+ [self addEntry:label forAddress:address toString:helpString];
+ }
+ }
}
}
+
+ [helpString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\nNote: Additional addresses are available that provide backward-compatibility with TouchOSC. See the Readme on Github for details.\n\n"]];
+
+ [helpString appendAttributedString:[[NSAttributedString alloc] initWithString:@"We add support for addresses on an as-needed basis. If you are in need of an additional address, open an issue on Github letting us know what it is.\n"]];
[helpString addAttribute:NSForegroundColorAttributeName value:[NSColor whiteColor] range:NSMakeRange(0,helpString.length)];
[[helpTextView textStorage] setAttributedString:helpString];
diff --git a/atemOSC/OSCEndpoint.h b/atemOSC/OSCEndpoint.h
new file mode 100644
index 0000000..5421360
--- /dev/null
+++ b/atemOSC/OSCEndpoint.h
@@ -0,0 +1,21 @@
+//
+// OSCEndpoint.h
+// AtemOSC
+//
+// Created by Peter Steffey on 1/11/20.
+//
+
+#import
+#import "VVOSC/VVOSC.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OSCEndpoint : NSObject
+ @property(nonatomic) NSString *addressTemplate;
+ @property(nonatomic) NSString *helpText;
+ @property(nonatomic) NSString *label;
+ @property(nonatomic) OSCValueType valueType;
+ @property(nonatomic, copy) void (^handler)(NSDictionary *, OSCValue *);
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/atemOSC/OSCEndpoint.m b/atemOSC/OSCEndpoint.m
new file mode 100644
index 0000000..95bda8e
--- /dev/null
+++ b/atemOSC/OSCEndpoint.m
@@ -0,0 +1,12 @@
+//
+// OSCEndpoint.m
+// AtemOSC
+//
+// Created by Peter Steffey on 1/11/20.
+//
+
+#import "OSCEndpoint.h"
+
+@implementation OSCEndpoint
+
+@end
diff --git a/atemOSC/OSCReceiver.h b/atemOSC/OSCReceiver.h
index 7bc7f16..02e5c58 100644
--- a/atemOSC/OSCReceiver.h
+++ b/atemOSC/OSCReceiver.h
@@ -2,6 +2,7 @@
#define OSCReveiver_h
#import "VVOSC/VVOSC.h"
+#import "OSCEndpoint.h"
@class AppDelegate;
@@ -10,9 +11,14 @@
AppDelegate *appDel;
}
+@property(nonatomic, retain) NSMutableDictionary *endpointMap;
+@property(nonatomic, retain) NSMutableDictionary *validators;
+
- (instancetype) initWithDelegate:(AppDelegate *)delegate;
- (void) receivedOSCMessage:(OSCMessage *)m;
@end
+
+
#endif /* OSCReveiver_h */
diff --git a/atemOSC/OSCReceiver.mm b/atemOSC/OSCReceiver.mm
index 958cff0..f1f3592 100644
--- a/atemOSC/OSCReceiver.mm
+++ b/atemOSC/OSCReceiver.mm
@@ -4,1063 +4,857 @@
@implementation OSCReceiver
+@synthesize endpointMap;
+@synthesize validators;
+
- (instancetype) initWithDelegate:(AppDelegate *) delegate
{
self = [super init];
appDel = delegate;
- return self;
-}
+
+ endpointMap = [[NSMutableDictionary alloc] init];
+ validators = [[NSMutableDictionary alloc] init];
+
+ NSLog(@"Setting up validators");
-- (void) receivedOSCMessage:(OSCMessage *)m
-{
- [appDel logMessage:[NSString stringWithFormat:@"Received OSC message: %@\tValue: %@", [m address], [m value]]];
- if ([appDel isConnectedToATEM]) { //Do nothing if not connected
- NSArray *address = [[m address] componentsSeparatedByString:@"/"];
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (key > 0 && key <= [appDel dsk].size())
+ return true;
+ [appDel logMessage:[NSString stringWithFormat:@"DSK %d is not available on your switcher, valid DSK values are 1 - %lu", key, [appDel dsk].size()]];
+ return false;
+ } copy] forKey:@"/atem/dsk"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ NSString *address = [d objectForKey:@"address"];
- if ([[address objectAtIndex:1] isEqualToString:@"atem"])
- {
- if ([[address objectAtIndex:2] isEqualToString:@"send-status"])
- [appDel sendStatus];
-
- else if ([[address objectAtIndex:2] isEqualToString:@"preview"] || [[address objectAtIndex:2] isEqualToString:@"program"])
- activateChannel([[address objectAtIndex:3] intValue], [[address objectAtIndex:2] isEqualToString:@"program"]);
-
- else if ([[address objectAtIndex:2] isEqualToString:@"transition"])
- {
- if ([[address objectAtIndex:3] isEqualToString:@"bar"])
- {
- if ([appDel mMixEffectBlockMonitor]->mMoveSliderDownwards)
- [appDel mMixEffectBlock]->SetTransitionPosition([m calculateFloatValue]);
- else
- [appDel mMixEffectBlock]->SetTransitionPosition(1.0-[m calculateFloatValue]);
- }
-
- else if ([[address objectAtIndex:3] isEqualToString:@"cut"])
- [appDel mMixEffectBlock]->PerformCut();
-
- else if ([[address objectAtIndex:3] isEqualToString:@"auto"])
- [appDel mMixEffectBlock]->PerformAutoTransition();
-
- else if ([[address objectAtIndex:3] isEqualToString:@"ftb"])
- [appDel mMixEffectBlock]->PerformFadeToBlack();
-
- else if ([[address objectAtIndex:3] isEqualToString:@"preview"])
- [appDel mMixEffectBlock]->SetPreviewTransition((int)[m calculateFloatValue]);
-
- else if ([[address objectAtIndex:3] isEqualToString:@"set-type"])
- {
-
- HRESULT result;
- NSString *style = [address objectAtIndex:4];
- REFIID transitionStyleID = IID_IBMDSwitcherTransitionParameters;
- IBMDSwitcherTransitionParameters* mTransitionStyleParameters=NULL;
- result = [appDel mMixEffectBlock]->QueryInterface(transitionStyleID, (void**)&mTransitionStyleParameters);
- if (SUCCEEDED(result))
- {
- if ([style isEqualToString:@"mix"])
- mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleMix);
-
- else if ([style isEqualToString:@"dip"])
- mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleDip);
-
- else if ([style isEqualToString:@"wipe"])
- mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleWipe);
-
- else if ([style isEqualToString:@"sting"])
- mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleStinger);
-
- else if ([style isEqualToString:@"dve"])
- mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleDVE);
-
- else
- [appDel logMessage:@"You must specify a transition type of 'mix', 'dip', 'wipe', 'sting', or 'dve'"];
- }
- }
-
- else
- [appDel logMessage:@"You must specify a transition action of 'bar', 'cut', 'auto', 'ftb', or 'set-type"];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"usk"] && [address count] > 4)
- {
- if (stringIsNumber([address objectAtIndex:3]))
- {
- int t = [[address objectAtIndex:3] intValue];
-
- if ([[address objectAtIndex:4] isEqualToString:@"tie"])
- {
- // Toggle tie
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"toggle"])
- {
- uint32_t currentTransitionSelection;
- [appDel switcherTransitionParameters]->GetNextTransitionSelection(¤tTransitionSelection);
-
- uint32_t transitionSelections[5] = { bmdSwitcherTransitionSelectionBackground, bmdSwitcherTransitionSelectionKey1, bmdSwitcherTransitionSelectionKey2, bmdSwitcherTransitionSelectionKey3, bmdSwitcherTransitionSelectionKey4 };
- uint32_t requestedTransitionSelection = transitionSelections[t];
-
- [self changeTransitionSelection:t select:!((requestedTransitionSelection & currentTransitionSelection) == requestedTransitionSelection)];
- }
-
- // Set for state after next transition
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"set-next"])
- {
- bool value = [[m value] floatValue] != 0.0;
-
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- bool isOnAir;
- key->GetOnAir(&isOnAir);
-
- [self changeTransitionSelection:t select:(value != isOnAir)];
- }
- }
-
- // Set tie
- else if ([address count] == 5)
- {
- bool value = [[m value] floatValue] != 0.0;
- [self changeTransitionSelection:t select:value];
- }
-
- else
- [appDel logMessage:@"You must specify a usk tie command of 'toggle' or 'set-next', or send a value to set the tie on or off"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"on-air"])
- {
- // Cut toggle on-air
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"toggle"])
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- bool onAir;
- key->GetOnAir(&onAir);
- key->SetOnAir(!onAir);
- }
- }
-
- // Force set on-air
- else if ([address count] == 5)
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- bool value = [[m value] floatValue] != 0.0;
- key->SetOnAir(value);
- }
- }
-
- else
- [appDel logMessage:@"You must specify a usk on-air command of 'toggle' or send a value to cut the usk on or off air"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"source"])
- {
- // Get/Set Fill Source
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"fill"])
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- if ([m valueCount] > 0)
- {
- BMDSwitcherInputId inputId = [[m value] intValue];
- key->SetInputFill(inputId);
- }
- }
- }
-
- // Get/Set Key (cut) Source
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"cut"])
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- if ([m valueCount] > 0)
- {
- BMDSwitcherInputId inputId = [[m value] intValue];
- key->SetInputCut(inputId);
- }
- }
- }
-
- else
- [appDel logMessage:@"You must specify a usk source command of 'fill' or 'cut'"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"type"])
- {
- if (([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"luma"]) || [[[m value] stringValue] isEqualToString: @"luma"])
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- key->SetType(bmdSwitcherKeyTypeLuma);
- }
- }
- else if (([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"chroma"]) || ([m valueCount] > 0 && [[[m value] stringValue] isEqualToString: @"chroma"]))
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- key->SetType(bmdSwitcherKeyTypeChroma);
- }
- }
- else if (([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"pattern"]) || ([m valueCount] > 0 && [[[m value] stringValue] isEqualToString: @"pattern"]))
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- key->SetType(bmdSwitcherKeyTypePattern);
- }
- }
- else if (([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"dve"]) || ([m valueCount] > 0 && [[[m value] stringValue] isEqualToString: @"dve"]))
- {
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- key->SetType(bmdSwitcherKeyTypeDVE);
- }
- }
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"luma"])
- {
- // Get/Set PreMultiplied
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"pre-multiplied"])
- {
- if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:t])
- {
- if ([m valueCount] > 0)
- {
- lumaParams->SetPreMultiplied([[m value] boolValue]);
- }
- }
- }
-
- // Get/Set Clip
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"clip"])
- {
- if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:t])
- {
- if ([m valueCount] > 0)
- {
- lumaParams->SetClip([[m value] floatValue]);
- }
- }
- }
-
- // Get/Set Gain
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"gain"])
- {
- if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:t])
- {
- if ([m valueCount] > 0)
- {
- lumaParams->SetGain([[m value] floatValue]);
- }
- }
- }
-
- // Get/Set Inverse
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"inverse"])
- {
- if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:t])
- {
- if ([m valueCount] > 0)
- {
- lumaParams->SetInverse([[m value] boolValue]);
- }
- }
- }
-
- else
- [appDel logMessage:@"You must specify a usk luma command of 'pre-multiplied', 'clip', 'gain', or 'inverse'"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"chroma"])
- {
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"hue"])
- {
- if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:t])
- {
- if ([m valueCount] > 0)
- {
- chromaParams->SetHue([[m value] floatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"gain"])
- {
- if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:t])
- {
- if ([m valueCount] > 0)
- {
- chromaParams->SetGain([[m value] floatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"y-suppress"])
- {
- if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:t])
- {
- if ([m valueCount] > 0)
- {
- chromaParams->SetYSuppress([[m value] floatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"lift"])
- {
- if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:t])
- {
- if ([m valueCount] > 0)
- {
- chromaParams->SetLift([[m value] floatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"narrow"])
- {
- if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:t])
- {
- if ([m valueCount] > 0)
- {
- chromaParams->SetNarrow([[m value] boolValue]);
- }
- }
- }
-
- else
- [appDel logMessage:@"You must specify a usk chroma command of 'hue', 'gain', 'y-suppress', 'lift', or 'narrow'"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"dve"])
- {
- if ([address count] == 7 && [[address objectAtIndex:5] isEqualToString:@"enabled"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderHue([[address objectAtIndex:6] boolValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-width-inner"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderWidthIn([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-width-outer"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderWidthOut([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-softness-outer"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderSoftnessOut([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-softness-outer"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderSoftnessOut([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-opacity"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderOpacity([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-hue"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderHue([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-saturation"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderSaturation([m calculateFloatValue]);
- }
- }
- }
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"border-luma"])
- {
- if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:t])
- {
- if ([m valueCount] > 0)
- {
- dveParams->SetBorderLuma([m calculateFloatValue]);
- }
- }
- }
-
- else
- [appDel logMessage:@"You must specify a usk dve command of 'border-width-inner', 'border-width-outer', 'border-softness-inner', 'border-softness-outer', 'border-opacity', 'border-hue', 'border-saturation', or 'border-luma'"];
- }
-
- else
- [appDel logMessage:@"You must specify a usk command of 'tie', 'on-air', 'type', 'source', 'chroma', or 'luma'"];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"You must specify a usk between 0 and %lu", [appDel keyers].size()]];
- }
-
- // Deprecated
- else if ([[address objectAtIndex:2] isEqualToString:@"set-nextusk"])
- {
- int t = [[address objectAtIndex:3] intValue];
- bool value = [m calculateFloatValue] != 0.0;
-
- if (IBMDSwitcherKey* key = [self getUSK:t])
- {
- bool isOnAir;
- key->GetOnAir(&isOnAir);
-
- [self changeTransitionSelection:t select:(value != isOnAir)];
- }
- }
-
- // Deprecated
- else if ([[address objectAtIndex:2] isEqualToString:@"nextusk"])
- {
- int t = [[address objectAtIndex:3] intValue];
- bool value = [m calculateFloatValue] != 0.0;
- [self changeTransitionSelection:t select:value];
- }
-
- // Deprecated
- else if ([[address objectAtIndex:2] isEqualToString:@"usk"])
- {
- IBMDSwitcherKey* key = [self getUSK:[[address objectAtIndex:3] intValue]];
- if (key && [m calculateFloatValue] != 0.0)
- {
- bool onAir;
- key->GetOnAir(&onAir);
- key->SetOnAir(!onAir);
- }
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"dsk"])
- {
- // Deprecated
- if ([[address objectAtIndex:3] isEqualToString:@"set-tie"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:4] intValue]])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetTie(value);
- }
- }
-
- // Deprecated
- else if ([[address objectAtIndex:3] isEqualToString:@"tie"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:4] intValue]])
- {
- bool isTied;
- key->GetTie(&isTied);
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetTie(!isTied);
- }
- }
-
- // Deprecated
- else if ([[address objectAtIndex:3] isEqualToString:@"toggle"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:4] intValue]])
- {
- bool isLive;
- key->GetOnAir(&isLive);
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetOnAir(!isLive);
- }
- }
-
- // Deprecated
- else if ([[address objectAtIndex:3] isEqualToString:@"on-air"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:4] intValue]])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetOnAir(value);
- }
- }
-
- // Deprecated
- else if ([[address objectAtIndex:3] isEqualToString:@"set-next"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:4] intValue]])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning, isOnAir;
- key->IsTransitioning(&isTransitioning);
- key->GetOnAir(&isOnAir);
- if (!isTransitioning) key->SetTie(value != isOnAir);
- }
- }
-
- // Deprecated
- else if ([address count] == 4 && stringIsNumber([address objectAtIndex:3]))
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:[[address objectAtIndex:3] intValue]])
- {
- bool isTransitioning;
- key->IsAutoTransitioning(&isTransitioning);
- if (!isTransitioning) key->PerformAutoTransition();
- }
- }
-
- else if ([address count] > 4 && stringIsNumber([address objectAtIndex:3]))
- {
- int t = [[address objectAtIndex:3] intValue];
-
- if ([[address objectAtIndex:4] isEqualToString:@"tie"])
- {
- // Toggle tie
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"toggle"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool isTied;
- key->GetTie(&isTied);
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetTie(!isTied);
- }
- }
-
- // Set for state after next transition
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"set-next"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning, isOnAir;
- key->IsTransitioning(&isTransitioning);
- key->GetOnAir(&isOnAir);
- if (!isTransitioning) key->SetTie(value != isOnAir);
- }
- }
-
- // Set tie
- else if ([address count] == 5)
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetTie(value);
- }
- }
-
- else
- [appDel logMessage:@"You must specify a dsk tie command of 'toggle' or 'set-next', or send a value to set the tie on or off"];
- }
-
- else if ([[address objectAtIndex:4] isEqualToString:@"on-air"])
- {
- // Cut toggle on-air
- if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"toggle"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool isLive;
- key->GetOnAir(&isLive);
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetOnAir(!isLive);
- }
- }
-
- // Auto on-air
- else if ([address count] == 6 && [[address objectAtIndex:5] isEqualToString:@"auto"])
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool isTransitioning;
- key->IsAutoTransitioning(&isTransitioning);
- if (!isTransitioning) key->PerformAutoTransition();
- }
- }
-
- // Set on-air
- else if ([address count] == 5)
- {
- if (IBMDSwitcherDownstreamKey* key = [self getDSK:t])
- {
- bool value = [m calculateFloatValue] != 0.0;
- bool isTransitioning;
- key->IsTransitioning(&isTransitioning);
- if (!isTransitioning) key->SetOnAir(value);
- }
- }
-
- else
- [appDel logMessage:@"You must specify a dsk on-air command of 'toggle' or 'auto', or send a value to cut the dsk on or off air"];
- }
-
- else
- [appDel logMessage:@"You must specify a dsk command of 'tie' or 'on-air'"];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"You must specify a dsk between 1 and %lu", [appDel dsk].size()]];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"mplayer"])
- {
- int mplayer = [[address objectAtIndex:3] intValue];
- NSString *type = [address objectAtIndex:4];
- int requestedValue = [[address objectAtIndex:5] intValue];
- BMDSwitcherMediaPlayerSourceType sourceType;
-
- // check we have the media pool
- if (![appDel mMediaPool])
- {
- [appDel logMessage:@"No media pool\n"];
- return;
- }
-
- if ([appDel mMediaPlayers].size() < mplayer || mplayer < 0)
- {
- [appDel logMessage:[NSString stringWithFormat:@"No media player %d", mplayer]];
- return;
- }
-
- if ([type isEqualToString:@"clip"])
- {
- sourceType = bmdSwitcherMediaPlayerSourceTypeClip;
- }
- else if ([type isEqualToString:@"still"])
- {
- sourceType = bmdSwitcherMediaPlayerSourceTypeStill;
- }
- else
- {
- [appDel logMessage:@"You must specify the Media type 'clip' or 'still'"];
- return;
- }
- // set media player source
- HRESULT result;
- result = [appDel mMediaPlayers][mplayer-1]->SetSource(sourceType, requestedValue-1);
- if (FAILED(result))
- {
- [appDel logMessage:[NSString stringWithFormat:@"Could not set media player %d source\n", mplayer]];
- return;
- }
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"supersource"])
- {
- [self handleSuperSource:m address:address];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"macros"])
- {
- [self handleMacros:m address:address];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"aux"])
- {
- int auxToChange = [[address objectAtIndex:3] intValue];
- int source = [m calculateFloatValue];
- [self handleAuxSource:auxToChange channel:source];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"audio"] && [address count] > 3)
- {
- if ([[address objectAtIndex:3] isEqualToString:@"input"])
- {
- if (stringIsNumber([address objectAtIndex:4]))
- {
- BMDSwitcherAudioInputId inputNumber = [[address objectAtIndex:4] intValue];
- if ([appDel mAudioInputs].count(inputNumber) > 0)
- {
- if ([[address objectAtIndex:5] isEqualToString:@"gain"])
- [appDel mAudioInputs][inputNumber]->SetGain([m calculateFloatValue]);
-
- else if ([[address objectAtIndex:5] isEqualToString:@"balance"])
- [appDel mAudioInputs][inputNumber]->SetBalance([m calculateFloatValue]);
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid option '%@'. You must specify an audio input option of 'gain' or 'balance'", [address objectAtIndex:5]]];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid input %lld. Please choose a valid audio input number from the list in Help > OSC addresses.", inputNumber]];
- }
+ // Normal USK
+ if (key > 0 && key <= [appDel keyers].size())
+ return true;
+
+ // Background
+ if (key == 0 && [address containsString:@"tie"])
+ return true;
+
+ [appDel logMessage:[NSString stringWithFormat:@"USK %d is not available on your switcher, valid USK values are 1 - %lu", key, [appDel keyers].size()]];
+ return false;
+ } copy] forKey:@"/atem/usk"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int number = [[d objectForKey:@""] intValue];
+ if (number > 0 && [appDel mHyperdecks].count(number-1) > 0)
+ return true;
+ [appDel logMessage:[NSString stringWithFormat:@"Hyperdeck %d is not available on your switcher, valid Hyperdecks are 1 - %lu", number, [appDel mHyperdecks].size()]];
+ return false;
+ } copy] forKey:@"/atem/hyperdeck"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int number = [[d objectForKey:@""] intValue];
+ if (number > 0 && [appDel mAudioInputs].count(number) > 0)
+ return true;
+ [appDel logMessage:[NSString stringWithFormat:@"Invalid input %d. Please choose a valid audio input number from the list in Help > OSC addresses.", number]];
+ return false;
+ } copy] forKey:@"/atem/audio/input"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int mplayer = [[d objectForKey:@""] intValue];
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid input %@. The address following input/ must be a number", [address objectAtIndex:4]]];
- }
+ if (![appDel mMediaPool])
+ {
+ [appDel logMessage:@"No media pool\n"];
+ return false;
+ }
+
+ if ([appDel mMediaPlayers].size() < mplayer || mplayer < 0)
+ {
+ [appDel logMessage:[NSString stringWithFormat:@"No media player %d", mplayer]];
+ return false;
+ }
+ return true;
+ } copy] forKey:@"/atem/mplayer"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
- else if ([[address objectAtIndex:3] isEqualToString:@"output"])
- {
- if ([[address objectAtIndex:4] isEqualToString:@"gain"])
- [appDel mAudioMixer]->SetProgramOutGain([m calculateFloatValue]);
-
- else if ([[address objectAtIndex:4] isEqualToString:@"balance"])
- [appDel mAudioMixer]->SetProgramOutBalance([m calculateFloatValue]);
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid option '%@'. You must specify an audio output option of 'gain' or 'balance'", [address objectAtIndex:4]]];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid command '%@'. You must specify an audio command of 'input' or 'output'", [address objectAtIndex:3]]];
- }
-
- else if ([[address objectAtIndex:2] isEqualToString:@"hyperdeck"])
- {
- if (stringIsNumber([address objectAtIndex:3]))
- {
- BMDSwitcherHyperDeckId hyperdeckNumber = [[address objectAtIndex:3] intValue];
- if ([appDel mHyperdecks].count(hyperdeckNumber-1) > 0)
- {
- if ([[address objectAtIndex:4] isEqualToString:@"play"])
- [appDel mHyperdecks][hyperdeckNumber-1]->Play();
-
- else if ([[address objectAtIndex:4] isEqualToString:@"stop"])
- [appDel mHyperdecks][hyperdeckNumber-1]->Stop();
-
- else if ([[address objectAtIndex:4] isEqualToString:@"record"])
- [appDel mHyperdecks][hyperdeckNumber-1]->Record();
-
- else if ([[address objectAtIndex:4] isEqualToString:@"shuttle"])
- [appDel mHyperdecks][hyperdeckNumber-1]->Shuttle([[m value] intValue]);
-
- else if ([[address objectAtIndex:4] isEqualToString:@"jog"])
- [appDel mHyperdecks][hyperdeckNumber-1]->Jog([[m value] intValue]);
-
- else if ([[address objectAtIndex:4] isEqualToString:@"clip"])
- [appDel mHyperdecks][hyperdeckNumber-1]->SetCurrentClip([[m value] intValue]-1);
-
- else if ([[address objectAtIndex:4] isEqualToString:@"clip-time"])
- [self setHyperDeckTime:hyperdeckNumber-1 time:[[m value] stringValue] clip:YES];
-
- else if ([[address objectAtIndex:4] isEqualToString:@"timeline-time"])
- [self setHyperDeckTime:hyperdeckNumber-1 time:[[m value] stringValue] clip:NO];
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid option '%@'. You must specify a hyperdeck option of 'play', 'stop', 'record', 'shuttle', or 'jog'", [address objectAtIndex:4]]];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid HyperDeck identifier %lld. Please choose a valid HyperDeck identifier from the list in Help > OSC addresses.", hyperdeckNumber]];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Invalid input %@. The address following hyperdeck/ must be a number", [address objectAtIndex:3]]];
- }
-
- else
- [appDel logMessage:[NSString stringWithFormat:@"Cannot handle command: %@\nYou can find a list of valid commands in the help menu", [m address]]];
+ if (![appDel mSuperSource])
+ {
+ [appDel logMessage:@"No super source"];
+ return false;
+ }
+
+ if ([appDel mSuperSourceBoxes].size() < key)
+ {
+ [appDel logMessage:[NSString stringWithFormat:@"No super source box %d", key]];
+ return false;
}
+
+ return true;
+ } copy] forKey:@"/atem/supersource"];
+
+ [validators setObject:[^bool(NSDictionary *d, OSCValue *v) {
+ int auxToChange = [[d objectForKey:@""] intValue];
+ if (auxToChange > 0 && auxToChange-1 < [appDel mSwitcherInputAuxList].size())
+ return true;
+ [appDel logMessage:[NSString stringWithFormat:@"Aux number %d not available on your switcher", auxToChange]];
+ return false;
+ } copy] forKey:@"/atem/aux"];
+
+
+
+ NSLog(@"Setting up endpoints");
+
+ [self addEndpoint:@"/atem/send-status" handler:^void(NSDictionary *d, OSCValue *v) {
+ [appDel sendStatus];
+ }];
+
+ [self addEndpoint:@"/atem/preview" valueType: OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ activateChannel([v intValue], false);
+ }];
+
+ [self addEndpoint:@"/atem/program" valueType: OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ activateChannel([v intValue], true);
+ }];
+
+ [self addEndpoint:@"/atem/transition/bar" valueType: OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ if ([appDel mMixEffectBlockMonitor]->mMoveSliderDownwards)
+ [appDel mMixEffectBlock]->SetTransitionPosition([v floatValue]);
else
- [appDel logMessage:[NSString stringWithFormat:@"Cannot handle command: %@\nYou can find a list of valid commands in the help menu", [m address]]];
- }
- else
- [appDel logMessage:[NSString stringWithFormat:@"Cannot process command %@ because no switcher connected", [m address]]];
-}
+ [appDel mMixEffectBlock]->SetTransitionPosition(1.0-[v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/transition/cut" handler:^void(NSDictionary *d, OSCValue *v) {
+ [appDel mMixEffectBlock]->PerformCut();
+ }];
+
+ [self addEndpoint:@"/atem/transition/auto" handler:^void(NSDictionary *d, OSCValue *v) {
+ [appDel mMixEffectBlock]->PerformAutoTransition();
+ }];
+
+ [self addEndpoint:@"/atem/transition/ftb" handler:^void(NSDictionary *d, OSCValue *v) {
+ [appDel mMixEffectBlock]->PerformFadeToBlack();
+ }];
+
+ [self addEndpoint:@"/atem/transition/preview" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ [appDel mMixEffectBlock]->SetPreviewTransition([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/transition/type" valueType:OSCValString handler:^void(NSDictionary *d, OSCValue *v) {
+ REFIID transitionStyleID = IID_IBMDSwitcherTransitionParameters;
+ IBMDSwitcherTransitionParameters* mTransitionStyleParameters=NULL;
+ [appDel mMixEffectBlock]->QueryInterface(transitionStyleID, (void**)&mTransitionStyleParameters);
+
+ if ([[v stringValue] isEqualToString:@"mix"])
+ mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleMix);
+ else if ([[v stringValue] isEqualToString:@"dip"])
+ mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleDip);
+ else if ([[v stringValue] isEqualToString:@"wipe"])
+ mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleWipe);
+ else if ([[v stringValue] isEqualToString:@"sting"])
+ mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleStinger);
+ else if ([[v stringValue] isEqualToString:@"dve"])
+ mTransitionStyleParameters->SetNextTransitionStyle(bmdSwitcherTransitionStyleDVE);
+ }];
+
+ [self addEndpoint:@"/atem/usk//tie" label:@"Set USK Tie" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [self changeTransitionSelection:key select:[v boolValue]];
+ }];
+
+ [self addEndpoint:@"/atem/usk//tie/toggle" label: @"Toggle USK Tie" handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ uint32_t currentTransitionSelection;
+ [appDel switcherTransitionParameters]->GetNextTransitionSelection(¤tTransitionSelection);
+
+ uint32_t transitionSelections[5] = { bmdSwitcherTransitionSelectionBackground, bmdSwitcherTransitionSelectionKey1, bmdSwitcherTransitionSelectionKey2, bmdSwitcherTransitionSelectionKey3, bmdSwitcherTransitionSelectionKey4 };
+ uint32_t requestedTransitionSelection = transitionSelections[key];
+
+ [self changeTransitionSelection:key select:!((requestedTransitionSelection & currentTransitionSelection) == requestedTransitionSelection)];
+ }];
+
+ [self addEndpoint:@"/atem/usk//tie/set-next" label:@"Set Next-Transition State for USK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isOnAir;
+ [appDel keyers][key-1]->GetOnAir(&isOnAir);
+ [self changeTransitionSelection:key select:([v boolValue] != isOnAir)];
+ }];
+
+ [self addEndpoint:@"/atem/usk//on-air" label:@"Set USK On Air" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel keyers][key-1]->SetOnAir([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//on-air/toggle" label:@"Toggle USK On Air" handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool onAir;
+ [appDel keyers][key-1]->GetOnAir(&onAir);
+ [appDel keyers][key-1]->SetOnAir(!onAir);
+ }];
+
+ [self addEndpoint:@"/atem/usk//source/fill" label:@"Set Fill Source for USK" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel keyers][key-1]->SetInputFill([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//source/cut" label:@"Set Cut Source for USK" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel keyers][key-1]->SetInputCut([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//type" label:@"Set USK Type" valueType:OSCValString handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if ([[v stringValue] isEqualToString:@"luma"])
+ [appDel keyers][key-1]->SetType(bmdSwitcherKeyTypeLuma);
+ else if ([[v stringValue] isEqualToString:@"chroma"])
+ [appDel keyers][key-1]->SetType(bmdSwitcherKeyTypeChroma);
+ else if ([[v stringValue] isEqualToString:@"pattern"])
+ [appDel keyers][key-1]->SetType(bmdSwitcherKeyTypePattern);
+ else if ([[v stringValue] isEqualToString:@"dve"])
+ [appDel keyers][key-1]->SetType(bmdSwitcherKeyTypeDVE);
+ }];
+
+ [self addEndpoint:@"/atem/usk//luma/pre-multiplied" label:@"Set Pre-Multiplied Luma Parameter for USK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:key])
+ lumaParams->SetPreMultiplied([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//luma/clip" label:@"Set Clip Luma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:key])
+ lumaParams->SetClip([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//luma/gain" label:@"Set Gain Luma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:key])
+ lumaParams->SetGain([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//luma/inverse" label:@"Set Inverse Luma Parameter for USK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyLumaParameters* lumaParams = [self getUSKLumaParams:key])
+ lumaParams->SetInverse([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//chroma/hue" label:@"Set Hue Chroma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:key])
+ chromaParams->SetHue([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//chroma/gain" label:@"Set Gain Chroma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:key])
+ chromaParams->SetGain([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//chroma/y-suppress" label:@"Set Y-Suppress Chroma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:key])
+ chromaParams->SetYSuppress([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//chroma/lift" label:@"Set Lift Chroma Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:key])
+ chromaParams->SetLift([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//chroma/narrow" label:@"Set Narrow Chroma Parameter for USK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyChromaParameters* chromaParams = [self getUSKChromaParams:key])
+ chromaParams->SetNarrow([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/enabled" label:@"Set Border Enabled DVE Parameter for USK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderEnabled([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-width-inner" label:@"Set Border Inner Width DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderWidthIn([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-width-outer" label:@"Set Border Outer Width DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderWidthOut([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-softness-inner" label:@"Set Border Inner Softness DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderSoftnessIn([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-softness-outer" label:@"Set Border Outer Softness DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderSoftnessOut([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-opacity" label:@"Set Border Opacity DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderOpacity([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-hue" label:@"Set Border Hue DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderHue([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-saturation" label:@"Set Border Saturation DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderSaturation([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/usk//dve/border-luma" label:@"Set Border Luma DVE Parameter for USK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ if (IBMDSwitcherKeyDVEParameters* dveParams = [self getUSKDVEParams:key])
+ dveParams->SetBorderLuma([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//tie" label:@"Set DSK Tie" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isTransitioning;
+ [appDel dsk][key-1]->IsTransitioning(&isTransitioning);
+ if (!isTransitioning) [appDel dsk][key-1]->SetTie([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//tie/toggle" label:@"Toggle DSK Tie" handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isTied, isTransitioning;
+ [appDel dsk][key-1]->GetTie(&isTied);
+ [appDel dsk][key-1]->IsTransitioning(&isTransitioning);
+ if (!isTransitioning) [appDel dsk][key-1]->SetTie(!isTied);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//tie/set-next" label:@"Set Next-Transition State for DSK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isTransitioning, isOnAir;
+ [appDel dsk][key-1]->IsTransitioning(&isTransitioning);
+ [appDel dsk][key-1]->GetOnAir(&isOnAir);
+ if (!isTransitioning) [appDel dsk][key-1]->SetTie([v boolValue] != isOnAir);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//on-air" label:@"Set DSK On Air" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isTransitioning;
+ [appDel dsk][key-1]->IsTransitioning(&isTransitioning);
+ if (!isTransitioning) [appDel dsk][key-1]->SetOnAir([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//on-air/toggle" label:@"Toggle DSK On Air" handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isLive, isTransitioning;
+ [appDel dsk][key-1]->GetOnAir(&isLive);
+ [appDel dsk][key-1]->IsTransitioning(&isTransitioning);
+ if (!isTransitioning) [appDel dsk][key-1]->SetOnAir(!isLive);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//on-air/auto" label:@"Auto-Transistion DSK" handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ bool isTransitioning;
+ [appDel dsk][key-1]->IsAutoTransitioning(&isTransitioning);
+ if (!isTransitioning) [appDel dsk][key-1]->PerformAutoTransition();
+ }];
+
+ [self addEndpoint:@"/atem/dsk//source/fill" label:@"Set Fill Source for DSK" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetInputFill([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//source/cut" label:@"Set Cut Source for DSK" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetInputCut([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//clip" label:@"Set Clip Level for DSK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetClip([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//gain" label:@"Set Gain Level for DSK" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetGain([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//rate" label:@"Set Rate for DSK" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetRate([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//inverse" label:@"Set Inverse Parameter for DSK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetInverse([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/dsk//pre-multiplied" label:@"Set Pre-multiplied Parameter for DSK" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel dsk][key-1]->SetPreMultiplied([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/mplayer//clip" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int mplayer = [[d objectForKey:@""] intValue];
+ [appDel mMediaPlayers][mplayer-1]->SetSource(bmdSwitcherMediaPlayerSourceTypeClip, [v intValue]-1);
+ }];
+
+ [self addEndpoint:@"/atem/mplayer//still" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int mplayer = [[d objectForKey:@""] intValue];
+ [appDel mMediaPlayers][mplayer-1]->SetSource(bmdSwitcherMediaPlayerSourceTypeStill, [v intValue]-1);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//enabled" label:@"Set Box enabled" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetEnabled([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//source" label:@"Set Box Input Source" valueType:OSCValInt handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetInputSource([v intValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//x" label:@"Set Box X Position" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetPositionX([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//y" label:@"Set Box Y Position" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetPositionY([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//size" label:@"Set Box Size" valueType:OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetSize([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//cropped" label:@"Set Box Crop Enabled" valueType:OSCValBool handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetCropped([v boolValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//crop-top" label:@"Set Box Crop Top Amount" valueType: OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@""] intValue];
+ [appDel mSuperSourceBoxes][key-1]->SetCropTop([v floatValue]);
+ }];
+
+ [self addEndpoint:@"/atem/supersource/box//crop-bottom" label:@"Set Box Crop Bottom Amount" valueType: OSCValFloat handler:^void(NSDictionary *d, OSCValue *v) {
+ int key = [[d objectForKey:@"