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 run on macOS, iOS, Linux, and Windows: Apple platforms and Windows use FlyingFox for socket I/O, while Linux uses IORingSwift.

Platform TCP UDP WS client WS server Local Mach
macOS
iOS
Linux
Windows

The WebSocket client (WS client) uses Apple's URLSessionWebSocketTask and is therefore available on Apple platforms only. On Windows, datagram (UDP) endpoints are unavailable because FlyingSocks does not implement the sendmsg-based message API there, and Unix-domain (Local) sockets are not yet supported. Mach port transport is Darwin-only.

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 TCP, UDP, and WebSocket service types. Devices can also be registered manually for direct connection without DNS-SD.
  • WebSocket transport: Ocp1FlyingFoxConnection provides client-side WebSocket connectivity on Apple platforms using URLSessionWebSocketTask.
  • Mach port transport: Ocp1MachPortConnection provides fast local IPC between processes on Darwin using Mach ports.
  • 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, Unix domain socket, and Mach port (Darwin) endpoints concurrently.
  • TLS-secured TCP (ocasec): PSK (AES70 baseline) and X.509 certificate credentials on both Apple and Linux, with optional mTLS and TLS 1.3 external PSK. See Documentation/TLS.md.

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