BuildSoft.OscCore 1.2.1.1

dotnet add package BuildSoft.OscCore --version 1.2.1.1
NuGet\Install-Package BuildSoft.OscCore -Version 1.2.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BuildSoft.OscCore" Version="1.2.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BuildSoft.OscCore --version 1.2.1.1
#r "nuget: BuildSoft.OscCore, 1.2.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install BuildSoft.OscCore as a Cake Addin
#addin nuget:?package=BuildSoft.OscCore&version=1.2.1.1

// Install BuildSoft.OscCore as a Cake Tool
#tool nuget:?package=BuildSoft.OscCore&version=1.2.1.1

OscCore

A performance-oriented OSC (Open Sound Control) library for .NET.

Versions and Platforms

Releases are checked for compatibility with the latest release of these versions.

  • .NET Standard 2.1

Installation

Download & import the .nupkg from the Releases page.

I can also download from NuGet Package Manager. See nuget.org for the NuGet package latest version.

Usage

Receiving messages

Using Code

Adding address handlers

There are several different kinds of method that can be registered with a server via script.

Single method

You can register a single callback, to be executed on the server's background thread immediately when a message is received, by calling oscServer.TryAddMethod.

If you have no need to queue a method to be called on the main thread, you probably want this one.

var Server = OscServer.GetOrCreate(7000);

// add a single callback that reads message values at `/layers/1/opacity`
Server.TryAddMethod("/layers/1/opacity", ReadValues);

void ReadValues(OscMessageValues values)
{
    // call ReadElement methods here to extract values
}
Method pair

You can register a pair of methods to an address by calling oscServer.TryAddMethodPair.

These pairs consist of two methods, with the main thread one being optional.

  1. Runs on background thread, immediate execution, just like single methods
  2. Runs on main thread, queued on the next frame

This is useful for invoking events on the main thread, or any other use case that needs a main thread api. Read the message values in the first method.

var server = OscServer.GetOrCreate(7000);

float messageValue = 0.0f;

void ReadValues(OscMessageValues values) 
{
    messageValue = values.ReadFloatElement(0);
}

void MainThreadMethod() 
{
    // do something with MessageValue on the main thread
}

public CallbackPairExample()
{
    // add a pair of methods for the OSC address "/layers/2/color/red"
    server.TryAddMethodPair("/layers/2/color/red", ReadValues, MainThreadMethod);
}

Reading Message Values

Reading values from incoming messages is done on a per-element basis, using methods named like Read<Type>Element(int elementIndex).

Your value-reading methods will probably look something like this.

// these methods would be registered as background thread callbacks for an address
int ReadSingleIntMessage(OscMessageValues values)
{
    return values.ReadIntElement(0);
}

int ReadTripleFloatMessage(OscMessageValues values)
{
    float x  = values.ReadFloatElement(0);
    float y  = values.ReadFloatElement(1);
    float z  = values.ReadFloatElement(2);
}

Most data types offer an Unchecked version of the method that is slightly faster, and still safe to use if you know for sure what data type an element is.

double ReadUncheckedDoubleMessage(OscMessageValues values)
{
    return values.ReadFloat64ElementUnchecked(0);
}
Monitor Callbacks

If you just want to inspect message, you can add a monitor callback to be able to inspect every incoming message.

A monitor callback is an Action<BlobString, OscMessageValues>, where the blob string is the address.

Sending Messages

Using Code

OscWriter handles serialization of individual message elements.

OscClient wraps a writer and sends whole messages.

Sending of complex messages with multiple elements hasn't been abstracted yet - take a look at the methods in OscClient to see how to send any message you need.


OscClient Client = new OscClient("127.0.0.1", 7000);

// single a single float element message
Client.Send("/layers/1/opacity", 0.5f);

// send a blob
byte[] Blob = new byte[256];
Client.Send("/blobs", Blob, Blob.Length);

// send a string
Client.Send("/layers/3/name", "Textural");
Additional safety checks

Define OSCCORE_SAFETY_CHECKS in your project to have reads of message elements be bounds-checked. It amounts to making sure the element you asked for isn't beyond the number of elements in the message.

Protocol Support Details

All OSC 1.0 types, required and non-standard are supported.

The notable parts missing from the spec for the initial release are:

  • Matching incoming Address Patterns

    "A received OSC Message must be disptched to every OSC method in the current OSC Address Space whose OSC Address matches the OSC Message's OSC Address Pattern"

    Currently, an exact address match is required for incoming messages. If our address space has two methods:

    • /layer/1/opacity
    • /layer/2/opacity

    and we get a message at /layer/?/opacity, we should invoke both messages.

    Right now, we would not invoke either message. We would only invoke messages received at exactly one of the two addresses. This is the first thing that i think will be implemented after initial release - some of the other packages also lack this feature, and you can get far without it.

  • Syncing to a source of absolute time

    "An OSC server must have access to a representation of the correct current absolute time".

    I've implemented this, as a class that syncs to an external NTP server, but without solving clock sync i'm not ready to add it.

  • Respecting bundle timestamps

    "If the time represented by the OSC Time Tag is before or equal to the current time, the OSC Server should invoke the methods immediately... Otherwise the OSC Time Tag represents a time in the future, and the OSC server must store the OSC Bundle until the specified time and then invoke the appropriate OSC Methods."

    This is simple enough to implement, but without a mechanism for clock synchronization, it could easily lead to errors. If the sending application worked from a different time source than OscCore, events would happen at the wrong time.

Performance Details

Strings and Addresses

Every OSC message starts with an "address", specified as an ascii string.
It's perfectly reasonable to represent this address in C# as a standard string, which is how other libraries work.

However, because strings in C# are immutable & UTF16, every time we receive a message from the network, this now requires us to allocate a new string, and in the process expand the received ascii string's bytes (where each character is a single byte) to UTF16 (each char is two bytes).

OscCore eliminates both

  • string allocation
  • the need to convert ascii bytes to UTF16

This works through leveraging the BlobHandles package.
Incoming message's addresses are matched directly against their unmanaged ascii bytes.

This has two benefits

  • no memory is allocated when a message is received
  • takes less CPU time to parse a message
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on BuildSoft.OscCore:

Package Downloads
VRCOscLib

A OSC library for VRChat

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.1.1 719 12/24/2022
1.2.1 946 12/19/2022
1.2.0 289 12/19/2022
1.1.3 279 12/17/2022
1.1.2 480 12/9/2022
1.1.1 1,395 11/8/2022
1.1.0 738 9/20/2022
1.0.5 3,494 2/23/2022
1.0.4 432 2/23/2022
1.0.3 903 2/22/2022
1.0.2 916 2/21/2022
1.0.1 425 2/20/2022
1.0.0 422 2/20/2022