     // MARK: MIDI Property Accessors
     // TODO These should dispatch through midiClient instead of calling CoreMIDI directly
 
-    public func string(forProperty property: CFString) -> String? {
-        var unmanagedValue: Unmanaged<CFString>?
-        if MIDIObjectGetStringProperty(midiObjectRef, property, &unmanagedValue) == noErr {
-            return unmanagedValue?.takeUnretainedValue() as String?
-        }
-        else {
-            return nil
-        }
-    }
-
-    public func set(string: String?, forProperty property: CFString) {
-        if let value = string {
-            _ = MIDIObjectSetStringProperty(midiObjectRef, property, value as CFString)
-        }
-        else {
-            _ = MIDIObjectRemoveProperty(midiObjectRef, property)
-        }
-    }
-
-    public func int32(forProperty property: CFString) -> Int32? {
-        var value: Int32 = 0
-        if MIDIObjectGetIntegerProperty(midiObjectRef, property, &value) == noErr {
-            return value
-        }
-        else {
-            return nil
-        }
-    }
-
-    public func set(int32: Int32?, forProperty property: CFString) {
-        if let value = int32 {
-            _ = MIDIObjectSetIntegerProperty(midiObjectRef, property, value)
-        }
-        else {
-            _ = MIDIObjectRemoveProperty(midiObjectRef, property)
-        }
-    }
-
-    public func data(forProperty property: CFString) -> Data? {
-        var unmanagedValue: Unmanaged<CFData>?
-        if MIDIObjectGetDataProperty(midiObjectRef, property, &unmanagedValue) == noErr {
-            return unmanagedValue?.takeUnretainedValue() as Data?
-        }
-        else {
-            return nil
-        }
-    }
-
-    public func set(data: Data?, forProperty property: CFString) {
-        if let value = data {
-            _ = MIDIObjectSetDataProperty(midiObjectRef, property, value as CFData)
-        }
-        else {
-            _ = MIDIObjectRemoveProperty(midiObjectRef, property)
+    subscript(property: CFString) -> String? {
+        get {
+            var unmanagedValue: Unmanaged<CFString>?
+            if MIDIObjectGetStringProperty(midiObjectRef, property, &unmanagedValue) == noErr {
+                return unmanagedValue?.takeUnretainedValue() as String?
+            }
+            else {
+                return nil
+            }
+        }
+        set {
+            if let value = newValue {
+                _ = MIDIObjectSetStringProperty(midiObjectRef, property, value as CFString)
+            }
+            else {
+                _ = MIDIObjectRemoveProperty(midiObjectRef, property)
+            }
+        }
+    }
+
+    subscript(property: CFString) -> Int32? {
+        get {
+            var value: Int32 = 0
+            if MIDIObjectGetIntegerProperty(midiObjectRef, property, &value) == noErr {
+                return value
+            }
+            else {
+                return nil
+            }
+        }
+        set {
+            if let value = newValue {
+                _ = MIDIObjectSetIntegerProperty(midiObjectRef, property, value)
+            }
+            else {
+                _ = MIDIObjectRemoveProperty(midiObjectRef, property)
+            }
+        }
+    }
+
+    subscript(property: CFString) -> Data? {
+        get {
+            var unmanagedValue: Unmanaged<CFData>?
+            if MIDIObjectGetDataProperty(midiObjectRef, property, &unmanagedValue) == noErr {
+                return unmanagedValue?.takeUnretainedValue() as Data?
+            }
+            else {
+                return nil
+            }
+        }
+        set {
+            if let value = newValue {
+                _ = MIDIObjectSetDataProperty(midiObjectRef, property, value as CFData)
+            }
+            else {
+                _ = MIDIObjectRemoveProperty(midiObjectRef, property)
+            }
         }
     }
 


        // Immediately fetch the object's uniqueID, since it could become
        // inaccessible later, if the object is removed from CoreMIDI
        cachedUniqueID = int32(forProperty: kMIDIPropertyUniqueID) ?? 0
    }

    private var cachedUniqueID: MIDIUniqueID?
    public var uniqueID: MIDIUniqueID {
        get {
            switch cachedUniqueID {
            case .none:
                let value = int32(forProperty: kMIDIPropertyUniqueID) ?? 0
                cachedUniqueID = value
                return value
            case .some(let value):
                return value
            }
        }
        set {
            if cachedUniqueID != .some(newValue) {
                set(int32: newValue, forProperty: kMIDIPropertyUniqueID)
                cachedUniqueID = .none
            }
        }

import Foundation
import CoreMIDI

protocol CoreMIDIObjectWrapper: AnyObject, Equatable, Identifiable {

    var midiClient: SMClient { get }    // TODO This should refer to a protocol too
    var midiObjectRef: MIDIObjectRef { get }
