Max

What it is

Two externals, o.se and o.se.gui, and a couple of dozen Max objects written in the language itself and kept in code/: o.se.o.route, o.se.o.pack, o.se.o.collect, o.se.o.if, o.se.o.var, the set operations (o.se.o.union, o.se.o.intersection, o.se.o.difference), and others. Each one named after an object in CNMAT’s odot library, o.route, o.pack and the rest, is a drop-in replacement for it, written in osen; the o.se. in front is only how the loader finds it, so the two sets coexist in one Max installation. An object is a program, so it can be read, changed and reloaded without a compiler.

Get it running

Needs the Xcode command line tools and the Max SDK, which is vendored as source/max-sdk-base; not Xcode itself.

git clone --recursive https://github.com/asomadev/max.o.se
cd max.o.se/source
make            # modules, externals, then copies the .so files into ../misc

Inside a clone of the asoma workspace, the same host is hosts/max.o.se: make -C hosts/max.o.se/source.

osen and the standard library are modules the objects load. make LINKED=1 compiles them into the externals themselves instead, and LTO=1 optimises that as one. Either way an object loads osen before anything else and keeps its vocabulary for as long as it lives.

The build writes the two externals into externals/ and the modules into misc/, and the clone is laid out as a Max package: externals/, help/, patchers/, init/, and the objects in code/. Put the clone, or a link to it, in the Packages folder of your Max, inside Documents, and start Max. Nothing else on the machine is touched. Source and releases: github.com/asomadev/max.o.se.

You have it when help/o.se.maxhelp opens and its [o.se] answers a bang with the time.

First program

Open help/o.se.maxhelp in Max. It puts an [o.se] object in a patch, sends it a message of instructions, and shows a bang making the object answer with the time. An [o.se] object takes instructions as messages, last to first; an object named o.se.<name> loads <name>.ose from the search path when it is created, so o.se.o.route loads o.route.ose.

What it adds

Into the host register, as /o/ verbs a program calls like the language’s own:

And as instructions: /makeinlet, /makeoutlet, /outlet, /outlet/left, /outletatoms, /post, /maxobj, and a second kind of inlet and outlet for machines (/makeinlet/machine, /makeoutlet/machine, /outlet/machine).

Functions across a cord, and across a network

A bound function is a pointer, and a pointer means something only in the process that made it; it also carries the name it was bound under, and the name is what can travel. Between o.se objects on a patch cord, functions pass as they are. Anything that goes through a network, or holds a packet for later, has to carry them as names, and four objects mark that boundary: o.se.udpsend writes every pointer as its name and sends; o.se.udpreceive checks an arrival with the wire’s rules and refuses, and reports, one it cannot read; o.se.disarm writes pointers as names after anything else from outside; o.se.rearm resolves them here again, as a cache. A function written as its name works anyway: it resolves where it is applied. o.se.serve runs whatever is wired to it, so put o.se.udpreceive or o.se.disarm in front of it wherever a network is involved.

A patch as a node

[o.se.node 7401] makes a patch a node on the network: it answers the census, takes loops dropped on it from the view or sent from a prompt, and runs them on a Max clock, four at a time. Inside it is o.se.world with a receiver and two senders, one to the census and one for loops sent on; [o.se o.se.world /@/here/port 7401 /@/here/id kitchen] is the object on its own. It takes OSC only, so a Max message is the message of the same name: /!/continue/hold /blink, /!/continue/now /blink, /!/continue/release /blink, /!/continue/list, /!/continue/stop /blink, /!/continue/bind /blink 33 /blink/blue; and bang runs one period of every loop, for a metro.

A name a loop calls that nothing binds goes out of the first outlet as a message, /rgb/pulse 0 150 0 300; a name it reads goes out of the second, and the rightmost inlet answers the read while the call is going on: an int, a float, a list, or a packet from o.se.o.pack. The patch keeps the value, in a [f] or a [zl.reg]; the object keeps nothing. A loop leaving for another node goes out of the third outlet as a machine, host and port first for [udpsend], or by /forward to udp://host:port.