Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ let package = Package(
name: "Swiftmapper",
dependencies: ["libmapper"],
),
.executableTarget(name: "Demo", dependencies: ["Swiftmapper"]),
.systemLibrary(
name: "libmapper",
pkgConfig: "libmapper",
Expand Down
48 changes: 48 additions & 0 deletions Snippets/maps.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Swiftmapper

// snippet.init
let graph = MapperGraph();
let device = MapperDevice("MyDevice", withGraph: graph);
while !device.ready {
graph.poll(andBlockFor: 10);
}

let signal: MapperSignal<Float> = device.createSignal("MySignal", .In);

// snippet.searchSetup
let devices = graph.getDevices();
var targetSignal: GenericSignal? = nil;

// snippet.searchLoop
for dev in devices {
let signals = dev.getSignals(inDirection: .Out);

for sig in signals {
let type = sig.getSignalType();
let name: String = sig.getProperty(withId: .Name)!;

if type == Float.self && name == "expr" {
targetSignal = sig;
break;
}
}

if targetSignal != nil {
break;
}
}

// snippet.searchEnd
if targetSignal == nil {
fatalError("Could not find a signal to map!");
}

// snippet.map
let map = MapperMap(from: targetSignal!, to: signal);
map.setProperty(withId: .Expression, to: "y=x+1");
map.push();

while !map.ready {
graph.poll(andBlockFor: 10)
}
// map is now established
62 changes: 0 additions & 62 deletions Sources/Demo/main.swift

This file was deleted.

49 changes: 49 additions & 0 deletions Sources/Swiftmapper/Docs.docc/Creating-Maps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Creating Maps
Learn how to locate other devices and create maps with them

## Locating Other Signals

To create a map, you first need two signal references. Since most of the time you'll be mapping to signals that aren't in the same process, you'll have to search the graph for them.

To do this, we'll use a ``MapperGraph`` object. We'll also be creating a device, so we can save some memory by letting the device re-use the ``MapperGraph`` for network communication by passing it to ``MapperDevice/init(_:withGraph:)``

@Snippet(path: "maps", slice: "init")

> Devices created with a graph should not be polled directly, always poll the ``MapperGraph`` object instead.

Now that we have our device and signal, we can start looking for another signal on the shared graph. In this example, we're going to map the first scalar floating point signal named "expr".

First, since signals are owned by devices we need to search every device on the graph. We can use function ``MapperGraph/getDevices()`` to do this:

@Snippet(path: "maps", slice: "searchSetup")

Next, we need to loop through each device and its signals. We can skip some work by specifying we're only looking for outgoing signals in ``MapperDevice/getSignals(inDirection:)``

@Snippet(path: "maps", slice: "searchLoop")

> ``GenericSignal/getSignalType()`` will return the equivalent Swift primitive metatype for the signal type, but if the signal's length is greater than one it will return the metatype of
> an array of that primitive instead.
>
> For example, if we were instead looking for a float vector signal with a length of three, the following code would be appropriate:
> ```swift
> // ...
> let length: Int = sig.getProperty(withName: .Length)!;
> if type == [Float].self && length == 3 && name == "expr" {
> // ...
> }
> ```

## Creating Maps

Now that we've found our signal, we can create a map using it!

The initializer ``MapperMap/init(from:to:)-(GenericSignal,_)`` lets you create a simple map with two endpoints. After initializing the object, you can set the expression via ``MapperObject/setProperty(withId:to:publish:)`` and publish the map using ``MapperObject/push()``. You can wait for ``MapperMap/ready`` to become true to indicate the map has become successfully established.

@Snippet(path: "maps", slice: "map")

### Map Ownership

Since maps are not "owned" by any single device, this presents challenges with the automatic resource management provided by Swiftmapper's wrappers. By default any map you create locally will
be marked as owned, and will be deleted once the Swift object is deinitialized. This may not be desirable in many cases, and so you may use the method ``MapperMap/forget()`` to release ownership of the map.

``MapperMap`` references obtained by querying the graph are unowned by default, but you can take ownership of them via ``MapperMap/take()`` if you would like their lifetime to be bound to the wrapper object. Once the wrapper's reference count reaches zero and Swift calls `deinit` the map will be removed from the graph.
1 change: 1 addition & 0 deletions Sources/Swiftmapper/Docs.docc/Swiftmapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A library for connecting things

### Maps & Objects
- <doc:Working-With-Properties>
- <doc:Creating-Maps>
- ``MapperMap``
- ``MapperObject``

Expand Down
5 changes: 3 additions & 2 deletions Sources/Swiftmapper/Objects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum MapperNamedProperty: Int32 {
case Id = 0x0800
case Expression = 0x0600
case NumInstances = 0x1200
case MapperType = 0x2400
}

/// Common interface for Libmapper object wrappers
Expand Down Expand Up @@ -62,7 +63,7 @@ extension MapperObject {
/// - withId: An identifier for a specific named property
/// - to: The new value
/// - publish: Whether this value should be replicated to other devices
public func setProperty<T: MappableType>(withId: MapperNamedProperty, to: T, publish: Bool = true) {
public func setProperty<T: MapperType>(withId: MapperNamedProperty, to: T, publish: Bool = true) {
var copy = to;
copy.withUnsafeRawPointer { ptr in
mpr_obj_set_prop(getHandle(), .init(UInt32(withId.rawValue)), nil, to.length(), T.asMapperType(), ptr, publish ? 1 : 0)
Expand All @@ -74,7 +75,7 @@ extension MapperObject {
/// - withName: The string identifier of a custom property
/// - to: The new value
/// - publish: Whether this value should be replicated to other devices
public func setProperty<T: MappableType>(withName: String, to: T, publish: Bool = true) {
public func setProperty<T: MapperType>(withName: String, to: T, publish: Bool = true) {
var copy = to;
copy.withUnsafeRawPointer { ptr in
withName.withCString {str in
Expand Down
28 changes: 27 additions & 1 deletion Sources/Swiftmapper/Signal.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
import libmapper

public protocol GenericSignal: MapperObject {

/// Get the signal status flags
///
/// - Parameter forInstance: The instance to get flag information for, or nil for the default
func getStatus(forInstance: UInt64?) -> SignalStatus

/// Get the signal type
///
/// If the signal has a length greater than 1, it will return the type of an array of the primitive type
func getSignalType() -> (any MappableType.Type)?
}

public extension GenericSignal {
func getSignalType() -> (any MappableType.Type)? {
let mpr: mpr_type? = getProperty(withId: .MapperType);
if mpr == nil {
return nil;
}
let len: Int32 = getProperty(withId: .Length)!;
let vector = len > 0;

switch Int(mpr!) {
case MPR_INT32:
return vector ? [Int32].self : Int32.self;
case MPR_DBL:
return vector ? [Double].self : Double.self;
case MPR_FLT:
return vector ? [Float].self : Float.self;
default:
return nil;
}
}
}

public enum MapperSignalDirection: UInt32 {
Expand Down
10 changes: 10 additions & 0 deletions Sources/Swiftmapper/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,14 @@ extension String: MapperType {
public static func fromRawPointer(ptr: UnsafeRawPointer, length: Int) -> String {
return String(cString: ptr.assumingMemoryBound(to: CChar.self))
}
}

extension mpr_type: MapperType {
public mutating func withUnsafeRawPointer(body: (UnsafeRawPointer) -> ()) {
body(&self)
}

public static func asMapperType() -> mpr_type {
return .init(UInt8(MPR_TYPE))
}
}
Loading