You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
2.9 KiB
92 lines
2.9 KiB
import Foundation
|
|
|
|
/// A screen identified across restarts and machines: by name first, position as fallback.
|
|
/// (CGDirectDisplayIDs differ between Macs and can change after a reboot.)
|
|
public struct ScreenRef: Codable, Hashable, Sendable {
|
|
public var name: String
|
|
/// Position in the system's screen list (0 = main screen).
|
|
public var index: Int
|
|
|
|
public init(name: String, index: Int) {
|
|
self.name = name
|
|
self.index = index
|
|
}
|
|
}
|
|
|
|
/// One NDI source fullscreen on one screen.
|
|
public struct VideoAssignment: Codable, Hashable, Sendable {
|
|
public var screen: ScreenRef
|
|
public var source: Source
|
|
|
|
public init(screen: ScreenRef, source: Source) {
|
|
self.screen = screen
|
|
self.source = source
|
|
}
|
|
}
|
|
|
|
/// One audio source on one output channel.
|
|
public struct AudioAssignment: Codable, Hashable, Sendable {
|
|
public enum SourceSpec: Codable, Hashable, Sendable {
|
|
case ndi(name: String)
|
|
/// Input device by UID; the name is the fallback when the UID changes (e.g. other USB port).
|
|
case input(uid: String, name: String)
|
|
}
|
|
|
|
public var source: SourceSpec
|
|
public var deviceUID: String
|
|
public var deviceName: String
|
|
public var channel: Int // 0-based
|
|
public var selection: ChannelSelection
|
|
public var gainDB: Float
|
|
public var muted: Bool
|
|
|
|
public init(source: SourceSpec, deviceUID: String, deviceName: String, channel: Int,
|
|
selection: ChannelSelection = .mono, gainDB: Float = 0, muted: Bool = false) {
|
|
self.source = source
|
|
self.deviceUID = deviceUID
|
|
self.deviceName = deviceName
|
|
self.channel = channel
|
|
self.selection = selection
|
|
self.gainDB = gainDB
|
|
self.muted = muted
|
|
}
|
|
|
|
/// Identity of the route (what goes where), without its settings.
|
|
public var key: String {
|
|
let sourceKey = switch source {
|
|
case .ndi(let name): "ndi:\(name)"
|
|
case .input(let uid, _): "input:\(uid)"
|
|
}
|
|
return "\(sourceKey)→\(deviceUID)#\(channel)"
|
|
}
|
|
}
|
|
|
|
/// Everything one Mac outputs.
|
|
public struct MachineConfig: Codable, Hashable, Sendable {
|
|
public var video: [VideoAssignment]
|
|
public var audio: [AudioAssignment]
|
|
|
|
public init(video: [VideoAssignment] = [], audio: [AudioAssignment] = []) {
|
|
self.video = video
|
|
self.audio = audio
|
|
}
|
|
|
|
public static let empty = MachineConfig()
|
|
}
|
|
|
|
/// A complete setup of the control Mac and its nodes, e.g. "OP-Live".
|
|
public struct Scene: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: UUID
|
|
public var name: String
|
|
public var control: MachineConfig
|
|
/// Node name (computer name) → what that node shows.
|
|
public var nodes: [String: MachineConfig]
|
|
|
|
public init(id: UUID = UUID(), name: String, control: MachineConfig = .empty, nodes: [String: MachineConfig] = [:]) {
|
|
self.id = id
|
|
self.name = name
|
|
self.control = control
|
|
self.nodes = nodes
|
|
}
|
|
}
|