ObjeKit

Swift DSL for Max SDK (Unofficial)

WIP Swift Version License Platform Build Status docs

๐Ÿ“ฆ Overview

This project defines a compact syntax layer for integrating Swift with the Max/MSP C API. It enables the development of Max external objects using Swift by providing a structured interface that maps Swift constructs to Maxโ€™s runtime model.

Applications:

  • Rapid prototyping of Max objects within an existing Swift codebase.
  • Reuse of Swift logic or libraries in Max/MSP externals without translating to C.

Trade-offs:

  • Introduces a dynamic dispatch layer between Swift and the Max API, which may impact performance in real-time signal processing (DSP) paths.

Requires

Requires explicit initialization to bridge Swift memory management and object lifecycles with Maxโ€™s C-based runtime. ## ๐Ÿงช Quick Start

โœ… Installation

  • add package to the project

See

see Examples folder for some demos


๐Ÿ‘พ Define a Max Object

Here’s a minimal example of a Max object with one inlet, one outlet showing basic functionality:

import ObjeKit

class ObjeKitTest_Simple : MaxObject {
    static var className: String { "okt_simple"}

    required init() {
        MaxRuntime.post("init: okt_simple")
    }

    deinit {
        MaxRuntime.post("deinit: okt_simple")
    }

    // MARK: - state objects

    @MaxState
    var floatValue : Double = 0

    @MaxState
    var intValue : CLong = 0

    @MaxState
    var listValue : MaxList = []

    @MaxOutput()
    var directOutlet

    @MaxIOBuilder
    var io: any MaxIOComponent {

        // MARK: - builder:  methods

        Inlet() {
            self.floatValue += 1
            MaxRuntime.post("bang! incremented value: \(self.floatValue)")
        }

        Inlet() { (value : Double) in
            self.floatValue = value
            MaxRuntime.post("float: \(value)")
        }

        Inlet() { (value : CLong) in
            self.intValue = value
            MaxRuntime.post("int: \(value)")
        }

        Inlet(name:"method_one")
        {
            MaxRuntime.post("method_one called")
            self.directOutlet.bang()
        }

        Inlet(name:"method_any") { value in
            MaxRuntime.post("method_any called with value: \(value)")
            self.listValue = value
        }

        Inlet() { (value:[MaxValue]) in
            MaxRuntime.post("received list: \(value)")
        }

        // MARK: - builder:  Outlets
        Outlet(.index(0)) { self.$floatValue }
        Outlet { self.$intValue }
        Outlet { self.$listValue }
    }

}


@_cdecl("ext_main")
public func ext_main(_ r: UnsafeMutableRawPointer) {
    MaxDispatcher.setup(ObjeKitTest_Simple.self)
}

SwiftUI integration example:

import ObjeKit
import SwiftUI

struct ObjectUI: View {
    @ObservedObject var floatValue: MaxStateObserver<Double>
    @ObservedObject var intValue: MaxStateObserver<CLong>

    var sliderBinding: Binding<Double> {
        Binding<Double>(
            get: { Double(intValue.value) },
            set: { intValue.value = Int($0.rounded()) } // round or floor depending on needs
        )
    }

    var body: some View {
        VStack {
            Text("Test Object UI")
                .padding()
            Slider(value: $floatValue.value, in: 0...10, label: { Text("Float Value") }, minimumValueLabel: { Text("0") }, maximumValueLabel: { Text("10") })
            Slider(value: sliderBinding, in: 0...127, label: { Text("Int Value") }, minimumValueLabel: { Text("0") }, maximumValueLabel: { Text("127") } )

        }.padding()
    }
}

// MARK: -

@_cdecl("ext_main")
public func ext_main(_ r: UnsafeMutableRawPointer) {
    MaxDispatcher.setup(ObjeKitTest_Attributes.self)
}

/// small class to display window
class ObjeKitTest_Attributes: MaxObject {
    static var className: String { "okt_ui" }

    required init() {
        MaxRuntime.post("init: okt_ui")
    }

    deinit {
        MaxRuntime.post("deinit: okt_ui")
    }

    lazy var window = CustomWindow("Test Window") { ObjectUI(floatValue: MaxStateObserver(self.$floatValue),
                                                             intValue: MaxStateObserver(self.$intValue)) }

    @MaxState
    var intValue: CLong = 0

    @MaxState
    var floatValue: Double = 0

    @MaxIOBuilder
    var io: any MaxIOComponent {
        Inlet(name: "show") { self.window.showWindow() }
        Inlet(name: "hide") { self.window.hideWindow() }

        Inlet { (v: CLong) in (v > 0) ? self.window.showWindow() : self.window.hideWindow() }

        Inlet { (v: Double) in self.floatValue = v }

        Outlet { self.$intValue }
        Outlet { self.$floatValue }
    }
}

Status

โš ๏ธ Work In Progress โ€” This project is currently experimental and not ready for production use.

Version number: 0.0.3 - Proof of concept

โœ… Inlets
๐Ÿ”„ Outlets
๐Ÿ”„ MaxOutput - generic output
โœ… Arguments
๐Ÿ”„ Attributes

๐Ÿ”„ Tests

โŒ DSP
โŒ Jitter


Max and Max/MSP are trademarks of Cycling ‘74. This project is not affiliated with, endorsed by, or sponsored by Cycling '74.