Overview

Messages and bundles

An OpenSoundControl message is an address and some values: /freq 440. A bundle is a list of messages and bundles. Everything here is one or the other: a value is a message, a program is a bundle, a function is a bundle stored under a name, and so is the machine that runs them.

In OSC a bundle’s elements have no names, so a bundle with a name is a message whose payload holds the bundle as a blob, and the message carries the address. That is what a register is, and why /_s is an address. The same shape has a typetag of its own inside a message, |, a bundle laid out like a blob.

The machine and its registers

A machine is five registers — bundles with names, inside the machine’s own bundle:

Those five are what a program runs on, and what travels when a machine moves. libose adds two beside them: a lookup cache, /_0, and the fault register, /_h, which holds why the machine last stopped. Every other register belongs to the host or module that made it, and says so in its name. The host register, /_x, holds what the host bound — its capabilities, as addresses. osen keeps its own instructions, the ones a program’s bundle is lowered into, in /os, and on the hosts that load it the reference as data in /_q, one entry per name.

A name is looked up in the register its first segment names, then in that register of each machine outside this one, then in the environment, then in the host register; the first place that has it answers. An inner machine that finds nothing asks its outer.

Because every register is a bundle, the whole machine is one valid OSC bundle at every moment; nothing is serialised on the way out because nothing was ever in another form. Hosts and modules can add registers of their own. The registers has the naming rule and how an address’s first segment selects one; the C reference’s machine page has the run loop.

Two languages

osen is the language people write; the machine runs ose, its instruction language, one message per instruction, and osen is a module that turns source into it. Every host runs both. Side by side:

  ose osen
what it is the machine’s instruction language the language people write
made of OSC messages, one per instruction OSC bundles with a syntax
bind a name /@ :
read a name /$ the bare name
call /! before an address (args) /o/fn
comment /#/ #
separator one message per element , — a newline is whitespace
files .ose .osen

The same thing, twice. Binding 10 to /foo and reading it back, in ose — three instructions, each an OSC message:

/,/i/10        push the integer 10
/@/foo         assign it to /foo
/$/foo         look /foo up again

and in osen:

/foo : 10,
/foo             →  : 10

The osen form is not shorthand for the other. It is source text that the osen module turns into a bundle, which the machine then runs; /foo : 10 never becomes an instruction, it becomes a bundle. An analogy that fits: ose is to osen roughly as assembly is to C. Everyone porting the machine needs the instruction set written down; almost nobody writing programs looks at it.

Addresses are the interface

A host binds what it can do — pins and WiFi, WebAudio, inlets and outlets, Python functions, UDP and files — into its host register as addresses. A program calls a capability on another host the way it calls one on its own. What each host binds: command line, Max, browser, microcontroller, Python.

What travels

A program is a bundle of messages the machine has not run yet. A running machine is a bundle holding its registers, the unrun program among them. Either can be sent. A program that arrives is run there; a machine that arrives continues where it was, and finds the far host’s capabilities in that host’s own host register, which stays where it is.

A register a program makes carries two flags, /flag/local and /flag/temp, read and set by name:

(1024, "/xx") /o/make/register, ("/xx") /o/register/flags
( /flag/local : 0, /flag/temp : 0 )

A register marked local stays behind when the machine leaves, as the host register does; setting the flag answers how many of the names it was given the host understood:

(1024, "/xx") /o/make/register, ("/xx", (/flag/local : 1)) /o/register/flags/set
: 1

How each host takes what you send, and what carries it: Between hosts.

Loops

A program can ask the host to run a body again and again:

/n : 0,
({ /n : (/n, 1) /o/add, (/n) /o/println }, 200, "/count") /continue/every

The body runs every 200 milliseconds in a machine of its own, and the caller’s environment becomes that machine’s state, so /n counts up from one period to the next. Each period is due a whole number of periods after the first, so a loop keeps its beat however long a body takes. The name is optional; a loop sent again under the same name replaces the one running, so an edited program leaves one loop, not two. ("/count") /continue/stop ends it and answers how many loops of that name stopped; () /continue/stop ends the one due soonest. Inside a body, (500) /continue/after pauses the loop for that long and then finishes the period.

A loop is a value the host keeps in a slot of its queue, and the slot reads like anything else in a register:

/q0/name
: "/count"
/q0/every
: 200

() /continue/list answers one bundle per slot: the loop’s name, its period, its size, and its state, waiting, running, held or parked. ("/count") /continue/hold makes a loop run only when ("/count") /continue/now says so, and /continue/release gives it its clock back. ("/count", "host:port") /continue/copy sends a copy of the loop, its state included, to another host, where it is filed under the same name and carries on from its count; the original runs on here. ("/count", 33, "/step") /continue/bind binds a name in a running loop’s own environment, read from its next period.

A loop whose period fails stops at the step that failed and is parked: the slot keeps the machine as it was at the fault, with no due time, and its state says so. A copy of it goes anywhere a machine goes; fixed, it comes back with /o/machine/resume, and the loop carries on from the step that failed with its count intact. Between hosts shows the round trip, and the view draws every node’s loops as cards.

The hosts that keep a queue say so on their pages.

The layers

host unix.o.se max.o.se web.o.se mcu.o.se py.o.se osekit lookup, types, module loading libose the machine ose, its instruction language OSC: the state and the wire o.se.osen osen, the language people write o.se.stdlib o.se.udp o.se.tcp o.se.time

A host is a program with the machine inside it, through the kit; a module is a library the host loads or links in, and what it provides lands in the machine as addresses.

Where this comes from

Asoma grew out of odot, a library of Max objects for OpenSoundControl made at CNMAT, the Center for New Music and Audio Technologies at UC Berkeley, where OpenSoundControl itself was made.

The ideas it sits beside: mobile code, in Telescript and Obliq; Erlang’s processes and its location transparency; the Smalltalk image, a running system saved as one thing; and durable execution, a program whose state outlives the process running it.