Skip to content

PADL/SwiftOCA

Repository files navigation

SwiftOCA

SwiftOCA is a pure Swift implementation of the AES70 control protocol, principally used for remote control of professional audio devices.

The package consists of three libraries:

All APIs are async-safe and support both macOS and Linux: on macOS, FlyingFox is used for socket I/O, and on Linux, IORingSwift.

Platform TCP UDP client UDP server WS client WS server Local
macOS
Linux

Features

Controller (SwiftOCA)

  • Device discovery: OcaConnectionBroker discovers AES70 devices via DNS-SD/Bonjour (using NetServiceBrowser on Apple platforms, or libdns_sd on Linux), with support for both TCP and UDP service types. Devices can also be registered manually for direct connection without DNS-SD.
  • Property observation: @OcaProperty and @OcaBoundedProperty wrappers expose property changes as AsyncSequence streams, enabling reactive UI updates.
  • JSON serialization: read the full state of any remote object or block tree as a JSON-compatible dictionary via jsonObject.
  • Automatic reconnection: optionally reconnect when a connection drops or a device's IP address changes via mDNS, with configurable options for subscription refresh and object cache retention.

Device (SwiftOCADevice)

  • Full AES70 device implementation: host actuators, sensors, blocks, matrices, managers, and agents.
  • @OcaDeviceProperty: property wrapper that manages local state and notifies connected controllers on changes.
  • Block and matrix containers: OcaBlock and OcaMatrix for organizing objects into hierarchical or grid-based topologies.
  • JSON serialization/deserialization: persist and restore device state via serialize/deserialize and the parameter dataset API.
  • Multiple transport endpoints: run TCP, UDP, WebSocket, and Unix domain socket endpoints concurrently.

SwiftOCAUI

  • Automatic view dispatch: the OcaView protocol and OcaDetailView select specialized views based on OCA class type.
  • Pre-built actuator views: gain slider (log-scaled), mute toggle, polarity switch, pan/balance knob, boolean and float actuators.
  • Sensor views: level meter with PPM ballistics (color-coded bar graph), identification sensor, and generic sensor displays.
  • Block navigation: drill-down sidebar for hierarchical blocks; grid layout for leaf blocks; matrix navigation support.
  • Bonjour discovery view: ready-made device browser view for listing and connecting to discovered devices.

Examples

Sample code can be found in Examples:

  • OCADevice — a sample AES70 device with a gain control, boolean actuator matrix, and multiple transport endpoints.
  • OCABrowser — a macOS SwiftUI app that discovers devices via Bonjour and provides a navigable block browser with specialized control views.
  • OCABrokerTest — a command-line tool that discovers devices and auto-connects as they appear.

ocacli is a command-line OCA controller that is implemented using SwiftOCA. SwiftOCA is also compatible with third-party OCA controllers such as AES70Explorer.

A Flutter wrapper is available here.

OCABrowser

Walking the device tree

Connect to a device and recursively print the role path of every object:

import SwiftOCA

let connection = try await Ocp1TCPConnection(deviceAddress: deviceAddress)
try await connection.connect()

for actionObject in try await connection.rootBlock.resolveActionObjectsRecursive()
  .compactMap({ $0.memberObject as? OcaOwnable }) {
  try? await print("- \(actionObject.rolePathString)")
}

try? await connection.disconnect()

Observing property changes

Subscribe to a gain property and react to changes:

import SwiftOCA

let connection = try await Ocp1TCPConnection(deviceAddress: deviceAddress)
try await connection.connect()

let gain = try await connection.resolve(object: OcaGain.self, objectNumber: gainONo)
for try await value in gain.$gain {
  print("gain changed to \(value) dB")
}

Hosting a device

Create an AES70 device with a gain control and serve it over TCP:

import SwiftOCADevice

let device = OcaDevice.shared
try await device.initializeDefaultObjects()

let gain = try await OcaGain(
  objectNumber: 10020,
  role: "Main Gain",
  deviceDelegate: device
)

let endpoint = try await Ocp1FlyingSocksStreamDeviceEndpoint(address: listenAddress)
try await endpoint.run()

Discovering devices with OcaConnectionBroker

Use OcaConnectionBroker to discover devices on the network and connect automatically:

import SwiftOCA

let broker = await OcaConnectionBroker(
  connectionOptions: Ocp1ConnectionOptions(flags: [
    .automaticReconnect,
    .refreshSubscriptionsOnReconnection,
  ])
)

for try await event in await broker.events {
  switch event.eventType {
  case .deviceAdded:
    try await broker.connect(device: event.deviceIdentifier)
    print("connected to \(event.deviceIdentifier.name)")
  case .deviceRemoved:
    print("lost \(event.deviceIdentifier.name)")
  default:
    break
  }
}

License

Apache License 2.0. See LICENSE.md.

Luke Howard lukeh@lukktone.com

About

Swift AES70/OCA client/server

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages