QLabKit is an Objective-C library for controlling QLab over the OSC API in QLab 3 or later. QLabKit requires macOS 11+ or iOS 14+.
NOTE: This library is under active development and the API may change.
All the files for the library are in the lib
folder. Copy all the files from that folder into your project. Make sure you also include the code in the F53OSC
folder which is a submodule. You'll also need to link against Security.framework
. All files in QLabKit and F53OSC use ARC.
QLabKit can also be installed on iOS using CocoaPods by adding the following to your podfile:
pod 'F53OSC', :git => 'https://github.com/Figure53/F53OSC.git'
pod 'QLabKit', :git => 'https://github.com/Figure53/QLabKit.objc.git'
There are four primary classes you will use for talking to QLab:
QLKBrowser
- automatically discover QLab instances on the networkQLKServer
- represents an individual QLab server with a name, host, and portQLKWorkspace
- a single workspace on a serverQLKCue
- a simplified representation of a cue. A cue list cue is also represented as a cue
There are other classes in QLabKit that these classes rely on that you need not worry about. Read the headers of these primary classes for more usage information. All classes in QLabKit use ARC.
The first thing you need to do is to get a QLKWorkspace
instance to communicate with a single workspace from QLab. You can get this in one of two ways:
QLab advertises itself using Bonjour. This allows for the automatic discovery of all QLab machines on the same local network. The QLKBrowser
class handles this for you like so:
Create a browser object and give it a delegate that implements QLKBrowserDelegate
protocol:
QLKBrowser *browser = [[QLKBrowser alloc] init];
browser.delegate = self;
[browser start];
// Optional: continuously refresh every 5 seconds
[browser enableAutoRefreshWithInterval:5];
Implement the required QLKBrowserDelegate
methods. The browser has a servers
property that is an array of QLKServer objects. Each QLKServer has a workspaces
property that holds an array of QLKWorkspace objects discovered on the network.
- (void) browserDidUpdateServers:(QLKBrowser *)browser
{
// Browser has a servers property
for ( QLKServer *aServer in browser.servers )
{
for ( QLKWorkspace *aWorkspace in aServer.workspaces )
{
// do something with the servers and workspaces, i.e. add to a selection UI, etc
}
}
}
NOTE: As of iOS 14, your app must be granted permission by the user to connect to devices on the local network. To enable this, your Info.plist must include the NSLocalNetworkUsageDescription
key with a description of your app's network usage and the NSBonjourServices
key with the QLab Bonjour service value _qlab._tcp
. For example:
<key>NSLocalNetworkUsageDescription</key>
<string>Some descriptive text explaining your app's reason to connect to the local network.</string>
<key>NSBonjourServices</key>
<array>
<string>_qlab._tcp</string>
</array>
If you don't want to automatically discover QLab, you can also do it manually by first creating a server:
NSString *hostIP = @"10.0.1.1";
NSInteger port = 53000;
QLKServer *server = [[QLKServer alloc] initWithHost:hostIP port:port];
[server refreshWorkspacesWithCompletion:^(NSArray<QLKWorkspace *> * _Nonnull workspaces) {
// server now has workspaces
for ( QLKWorkspace *aWorkspace in workspaces )
{
// ... do something with aWorkspace
}
}];
Once you have a workspace, you can send commands to and get data from QLab. The first thing is to connect to the workspace:
QLKWorkspace *workspace; // assume this exists as a result of one of the earlier methods
// Connect to workspace
NSString *passcode = ...
[workspace connectWithPasscode:passcode completion:nil];
// Tell workspace to GO
[workspace go];
...
// Update name of a cue, assuming you have a cue object
cue.name = @"New name";
QLKWorkspace exposes higher level methods so you don't have to directly deal with formatting the correct message and address. However, there may be API calls that the workspace class doesn't currently support, and you can use the lower-level methods to manually send a message.
NSString *address = [NSString stringWithFormat:@"/workspace/%@/cue_id/%@/name", workspace.uniqueID, cue.uid];
[workspace sendMessage:@"New Name" toAddress:address];
There is also working demo project that shows how you might hook all of this together to find servers on the network, show their workspaces, connect to a workspace, and finally fetch and display all the cues. Open QLabKit.xcodeproj
and run the QLabKitDemo
project to learn more.
QLabKit 0.0.5 requires macOS 11+ or iOS 15+.
- Adds support for connecting to QLab v5. See
-[QLKWorkspace connectWithPasscode:completion:]
and the QLab OSC dictionary for more. - Adds support for unified code formatting of QLabKit source code using clang-format.
- GLKit.framework is no longer a required dependency (deprecated by Apple in macOS 10.15 and iOS 13). Use new type
QLKQuaternion
to replaceGLKQuaternion
. - CocoaLumberjack is no longer added as a dependency when building with CocoaPods. It is unneeded since the socket classes that include CocoaLumberjack
DDLog.h
have logging disabled by default.
QLKMessageHandlerBlock
is replaced withQLKMessageReplyBlock
, which adds astatus
parameter. Note that thedata
parameter is now properly annotated as nullable.- The string values of certain video geometry keys are updated for compatibility with QLab v5.0 and later. For legacy values when connecting to QLab v3 or v4, use the constants that begin with
QLKOSCV4*
. QLKCue.h includes compatibility macros for synonomous keys across multiple versions of QLab.
- Delegate methods are renamed to include the
QLKClient
object as a parameter. - Adds
shouldEncryptConnectionsForClient:
to optionally enable an encrypted OSC connection when connected to QLab v5.0 and later.
- Adds
liveColor
convenience getter to fetch the v5 live cue color. - Adds
auditionPreview
convenience method. Requires QLab v5.0 or later. - The QLKImage property
icon
is replaced with NSStringiconName
. This avoids loading an image for each QLKCue object created. Instead, a string suitable for passing toimageNamed:
is cached, and the image is only loaded when the image file is needed. As such, the #defineQLKImage
is no longer needed and is removed. - The
userInfo
dictionary for theQLKCueListDidChangePlaybackPositionIDNotification
now includes the unique ID(s) of the old and/or new playback position cues. TheNSKeyValueChangeOldKey
entry, if present, contains theuniqueID
string of the previous playback position cue. TheNSKeyValueChangeNewKey
entry, if present, contains theuniqueID
string of the current playback position cue. - Patches in QLab 5 are greatly expanded. They have more descriptive patch list OSC getter names (i.e. not just
/patchList
) and are now methods of the respective settings that manage a given patch type. As such, thepatchName
getter and@"patchDescription"
helper insidepropertyForKey:
no longer cover enough cases to be useful and have been removed. Going forward when connecting to QLab 5, get the patch index or patch unique ID from the cue and cross-reference with the patch list fetched from the appropriate settings. For backward compatibility with QLab 3 and 4, get both the patch index and patch list from the cue and cross-reference with each other.
- Adds a new class and NSString category used to compare version number strings.
- The
compare:
method of this class compares the "build" number of each version if needed. Use theQLKVersionNumber
methodcompare:ignoreBuild:
to optionally ignore the build number when comparing two versions. - The
QLKQLabWorkspaceVersion
class is deprecated and will be removed in a future release in favor of usingQLKVersionNumber
.
- Adds
auditionGo
andauditionPreviewCue:
convenience methods. Both require QLab v5.0 or later. - When connected to QLab v4 and later, calling
fetchDisplayAndGeometryForCue:
no longer fetches properties related to the list of workspace video outputs or properties of a given output. Instead, the workspace populates itsvideoOutputsList
property with the reply payload from/settings/video/stages
(v5.0+) or/settings/video/surfaces
(v4.x). This avoids duplicate and/or stale data from being stored in all video cues. Use new QLKWorkpace methodsstageDictForStageID:
(v5.0+) orsurfaceDictForSurfaceID:
(v4.x) to get a dictionary of values for that video output ID. ThevideoOutputsList
array is kept up-to-date by the workspace in response to Video settings update notifications. - The helper method
addressForWildcardNumber:action:
is deprecated because it is unused in QLabKit and only generates/cue/*
-prefixed addresses, whereas wildcards can be also be used in addresses with the/cue_id/*
prefix.
QLabKit 0.0.4 requires macOS 10.9+ or iOS 8.4+.
- Fixes the class method
cueTypeIsAudio:
so that it now correctly returnsNO
for the following cue types: Fade, Camera, Text/Titles. - Fixes
panicCue:
so that theQLKOSCIsPanickingKey
property is only updated when connected to QLab 4.0 or later (which is the minimum QLab version that supports the/isPanicking
OSC method). - Adds convenience getters
isOverridden
,isBroken
,isTailingOut
, andisPanicking
which also take into account the property values of child cues in Group, Cue List, and Cue Cart cues. - When using
propertyForKey:
to get the quaternion of a Video or Fade cue, the value is no longer transformed to NSValue and is now returned as an array of NSNumbers (representing the X, Y, Z, and W components of the quaternion). The convenience propertyquaternion
remains unchanged and gets/sets aGLKQuaternion
struct, and the new convenience settersetQuaternion:tellQLab:
also accepts aGLKQuaternion
struct.
- Colors and colorspaces are now more consistent between iOS and macOS.
- The
name
property and the class method+[QLKColor colorWithName:]
now return an empty QLKColor object for undefined color names. Subclasses or categories can override thename
property to define additional colors.
- Fixes an issue where replies from individual OSC getter methods (i.e. calls from
cue:valueForKey:block:
) were not being processed. - The internal F53OSCClient used by QLKClient now performs its TCP/UDP communication with QLab on a background thread. F53OSCClient ensures that its F53OSCClientDelegate methods continue to be called on the main thread. However, be aware that custom implementations of GCDAsyncSocketDelegate or GCDAsyncUdpSocketDelegate methods in any subclasses of QLKClient or F53OSCClient will now be called on a background thread and must dispatch back to the main thread when necessary.
- Adds QLKClientDelegate methods:
workspaceDisconnected
(required) which is now called when a QLKClient receives an OSC/update/workspace/{id}/disconnect
message from QLab. This replaces the previous behavior in whichclientConnectionErrorOccurred
was called. This new delegate method now makes it possible for a client app to distinguish between the QLab workspace proactively notifying the client it should disconnect (e.g. because the QLab workspace was closed) and some other issue with the network connection. Connection errors continue to trigger a call toclientConnectionErrorOccurred
.clientShouldDisconnectOnError
(optional) - If the delegate implements this method and returnsNO
, the QLKClient will no longer immediately disconnect after a connection error is reported by its internal F53OSCClient. Instead, the delegate is responsible for disconnecting and tearing down the client at some point in the future. If this method returnsYES
or is not implemented, the QLKClient will behave as before and immediately calldisconnect
in response to a connection error.lightDashboardUpdated:
(optional) - When connected to QLab 4.2 or later, this notifies the delegate when the state of the Light Dashboard has updated.preferencesUpdated:
(optional) - When connected to QLab 4.2 or later, this notifies the delegate when certain application preferences in QLab are updated, namelyliveFadePreview
. To respond to changes made to workspace-level settings, continue to useworkspaceSettingsUpdated:
.
- QLKWorkspace now posts a
QLKWorkspaceDidDisconnectNotification
only after receiving an affirmative/disconnect
update message from QLab. QLKWorkspace also no longer posts a redundantQLKWorkspaceDidDisconnectNotification
when disconnecting itself from QLab. All other connection errors, e.g. failed attempts to connect due to an incorrect OSC passcode or other network problems, post aQLKWorkspaceConnectionErrorNotification
so that observers can evaluate whether to attempt to reconnect or not. - The
connect
method is deprecated because it does not support connecting to workspaces with an OSC passcode. Instead, useconnectWithPasscode:completion:
and passnil
for the passcode parameter to connect to a workspace whosehasPasscode
property isNO
. - The method
connectWithPasscode:completion:
now caches the passcode in the workspace only upon a successful connection. - Adds a property
attemptToReconnect
which QLKWorkspace uses in its implementation of the new QLKClientDelegate methodclientShouldDisconnectOnError
. After receiving a connection error notification, QLKWorkspace uses the value of theattemptToReconnect
property to determine whether to attempt to reestablish a connection to the current workspace or to disconnect immediately. - The methods
startHeartbeat
andstopHeartbeat
are now public and can be used to actively monitor the network connection. When the heartbeat is running, a network timeout will cause the workspace to post aQLKWorkspaceConnectionErrorNotification
. - Adds two new methods
deferFetchingPropertiesForCue:
andresumeFetchingPropertiesForCue:
which can be used to reduce network traffic and significantly improve performance when connected to large workspaces. Selective use of these methods allows a workspace to consolidate update requests on a cue-by-cue basis, enabling a client app to send requests for data only when it is actually needed (e.g. when a cue is about to become visible in the UI). When a workspace is set to defer fetching the properties of a given cue, all QLKWorkspace methods prefixed withfetch
will cache the property key(s) from any request for that cue instead of sending an OSC message to QLab. When the workspace later is set to resume fetching the properties for that cue, a single/valuesForKeys
message will be sent to QLab requesting values for all previously-cached property keys, and allfetch
methods will once again transmit OSC requests for that cue immediately when called. - Adds a property
defaultDeferFetchingPropertiesForNewCues
. Set this property toYES
to cause the workspace to defer fetching properties for all new cues immediately upon creation. For example, this can be used to avoid flooding the network with OSC update requests when first connecting to QLab. The default for this property isNO
, meaning new cues created by the workspace will not defer fetching properties (the "resume" behavior). - Adds a
QLKWorkspaceDidUpdateLightDashboardNotification
which is posted by the QLKWorkspace implementation of QLKClientDelegate methodlightDashboardUpdated:
when connected to QLab 4.2 or later. - Adds a
QLKQLabDidUpdatePreferencesNotification
which is posted by the QLKWorkspace implementation of QLKClientDelegate methodpreferencesUpdated:
when connected to QLab 4.2 or later. - The methods
fetchChildrenForCue:block:
andfetchAudioLevelsForCue:block:
are deprecated because theirblock
parameters are not compatible with the new deferred property fetching mechanism. Instead, request these values usingcue:valueForKey:block:
with the keyschildren
andsliderLevels
, respectively. - The values of the following NSString constants are updated to follow the convention of the value matching the name. Any code that directly accesses the value of one of these constants, e.g. comparing to another string using
isEqual:
orisEqualToString:
, should update to use the new string values:QLKWorkspaceDidConnectNotification
QLKWorkspaceDidDisconnectNotification
QLKWorkspaceConnectionErrorNotification
QLabKit 0.0.3 requires macOS 10.9+ or iOS 8.4+.
- The QLKBrowserDelegate method
serverDidUpdateWorkspaces:
is renamed tobrowserServerDidUpdateWorkspaces:
to avoid collision with the QLKServerDelegate method of the same name. - The selector called by the NSTimer scheduled in
enableAutoRefreshWithInterval:
is changed from the public methodrefreshAllWorkspaces
to a new private method_refreshAllWorkspaces:
.
- Color values are updated to match the color scheme of QLab 4.
- Helper class methods
colorWithRed:green:blue:alpha:
andcolorWithWhite:alpha:
are deprecated in favor of specific values for NSColor/UIColor for better color matching between Mac and iOS. - The "type" property for the object returned by class method
defaultColor
now has the string value "default" instead of "none". startColor
andendColor
are deprecated since QLab 4 no longer displays cue colors with gradients. UselightColor
anddarkColor
instead.lightBlueColor
,panelColor
andnavBarColor
are no longer used in QLab 4 and are deprecated.
playbackPositionUpdated:
is changed tocueListUpdated:withPlaybackPositionID:
to allow updating the playback position individually for each cue list cue.
- Class method
iconForType:
now returns the string value "mic" for Mic cues to match QLab conventions. pushUpProperty:forKey:
is deprecated. UsesetProperty:forKey:tellQLab:
instead, with thetellQLab
parameter set toYES
.triggerPushDownPropertyForKey:
is deprecated. UsepullDownPropertyForKey:block:
instead.pushDownProperty:forKey:
is deprecated. UsesetProperty:forKey:tellQLab:
instead.- String constant
QLKCueHasNewDataNotification
is removed because the method which posted this notification is now deprecated. - The unique ID of each cue list cue's playback position can now be accessed using the
playbackPositionID
property and set usingsetPlaybackPositionID:tellQLab:
. View controllers should now observeQLKCueListDidChangePlaybackPositionIDNotification
notifications to respond to playback position changes on a per-cue list basis. - QLKCue property getters and setters are now thread-safe and updates are processed on a background queue.
QLKActiveCueListIdentifier
is renamed toQLKActiveCuesIdentifier
for clarity.QLKCueTypeMicrophone
is renamed toQLKCueTypeMic
to match QLab conventions.
- The property
browser
is removed. QLKBrowser now conforms toQLKServerProtocol
so an existing browser instance can now be set as the delegate of QLKServer instead of needing to have a dedicated browser property on the server itself.
uniqueId
property is renamed touniqueID
to unify capitalization of "ID".fetchMainPropertiesForCue:
is renamed tofetchDefaultCueListPropertiesForCue:
.- A
QLKCueUpdatedNotification
notification is now posted only when cue data has actually changed. - String constant
QLKWorkspaceDidChangePlaybackPositionNotification
is replaced byQLKCueListDidChangePlaybackPositionIDNotification
. (See QLKCue above.) - String constant
QLKWorkspaceDidUpdateCuesNotification
has been removed and is replaced withQLKCueUpdatedNotification
. Observers ofQLKCueUpdatedNotification
notifications can now inspect the notification object, which is the cue that was updated, and react accordingly. For example, the notification object will be the QLKWorkspaceroot
after adding a cue list to the workspace. Test the notification object cue forisCueList
to determine if the child cues of a particular cue list have changed. - All method parameters of type
QLKMessageHandlerBlock
are renamed to "block" to distinguish from the various styles of other "completion" blocks:cue:valueForKey:completion:
is renamed tocue:valueForKey:block:
.fetchCueListsWithCompletion:
is renamed tofetchCueListsWithBlock:
.fetchPlaybackPositionForCue:completion:
is renamed tofetchPlaybackPositionForCue:block:
.fetchChildrenForCue:completion:
is renamed tofetchChildrenForCue:block:
.fetchAudioLevelsForCue:completion:
is renamed tofetchAudioLevelsForCue:block:
.
- The "type" property for the
__root__
and__active__
cue list cues are changed fromQLKCueTypeGroup
toQLKCueTypeCueList
.
QLabKit © copyright 2014-2022 Figure 53, LLC.
QLabKit is licensed under the MIT license.