Loading...
Searching...
No Matches
Basics

## Data Structures

struct | o2_msg_data
| data part of an O2 message More...
struct | o2_message
| an O2 message container More...
struct | o2_blob
| The structure for binary large object. More...
union | o2_arg
| union of all O2 parameter types More...

## Macros

#define | O2_MALLOC(x) o2_dbg_malloc(x, __FILE__, __LINE__)
#define | O2_FREE(x) o2_dbg_free(x, __FILE__, __LINE__)
#define | O2_CALLOC(n, s) o2_dbg_calloc((n), (s), __FILE__, __LINE__)
#define | MSG_DATA_LENGTH(m) (((int32_t *) &((m)->timestamp))[-1])
#define | WORD_ALIGN_PTR(p) ((char *) (((size_t) (p)) & ~3))
| get the type string from o2_msg_data_ptr
#define | O2_MSG_TYPES(msg) WORD_ALIGN_PTR((msg)->address + strlen((msg)->address) + 4) + 1;
#define | o2_send(path, time, ...)
| Construct and send O2 message with best effort protocol.
#define | o2_send_cmd(path, time, ...)
| Construct and send an O2 message reliably.

## Typedefs

typedef double | o2_time
| O2 timestamps are doubles representing seconds since the approximate start time of the application.
typedef struct o2_msg_data | o2_msg_data
| data part of an O2 message
typedef struct o2_msg_data * | o2_msg_data_ptr
typedef struct o2_message | o2_message
| an O2 message container
typedef struct o2_message * | o2_message_ptr
typedef struct o2_blob | o2_blob
| The structure for binary large object.
typedef struct o2_blob * | o2_blob_ptr
typedef enum o2_type * | o2_type_ptr
typedef union o2_arg * | o2_arg_ptr
typedef void(* | o2_method_handler) (const o2_msg_data_ptr msg, const char *types, o2_arg_ptr *argv, int argc, void *user_data)
| callback function to receive an O2 message
typedef o2_time(* | o2_time_callback) (void *rock)
| signature for callback that defines the master clock

## Enumerations

enum | o2_type { O2_INT32 = 'i' , O2_FLOAT = 'f' , O2_STRING = 's' , O2_BLOB = 'b' , O2_ARRAY_START = '[' , O2_ARRAY_END = ']' , O2_INT64 = 'h' , O2_TIME = 't' , O2_DOUBLE = 'd' , O2_SYMBOL = 'S' , O2_CHAR = 'c' , O2_MIDI = 'm' , O2_TRUE = 'T' , O2_FALSE = 'F' , O2_NIL = 'N' , O2_INFINITUM = 'I' , O2_BOOL = 'B' , O2_VECTOR = 'v' }
| An enumeration of the O2 message types. More...

## Functions

void * | o2_dbg_malloc (size_t size, const char *file, int line)
void | o2_dbg_free (void *obj, const char *file, int line)
void * | o2_dbg_calloc (size_t n, size_t s, const char *file, int line)
| allocate memory
int | o2_initialize (const char *application_name)
| Start O2.
int | o2_memory (void *((*malloc)(size_t size)), void((*free)(void *)))
| Tell O2 how to allocate/free memory.
o2_time | o2_set_discovery_period (o2_time period)
| Set discovery period.
int | o2_hub (const char *ipaddress, int port)
| Connect to a hub.
int | o2_get_address (const char **ipaddress, int *port)
| Get IP address and TCP connection port number.
int | o2_service_new (const char *service_name)
| Add a service to the current application.
int | o2_tap (const char *tappee, const char *tapper)
| copy messages from one service to another
int | o2_service_free (const char *service_name)
| Remove a local service.
int | o2_method_new (const char *path, const char *typespec, o2_method_handler h, void *user_data, int coerce, int parse)
| Add a handler for an address.
int | o2_poll ()
| Process current O2 messages.
int | o2_run (int rate)
| Run O2.
int | o2_status (const char *service)
| Check the status of the service.
int | o2_roundtrip (double *mean, double *min)
| Get network round-trip information.
int | o2_clock_set (o2_time_callback gettime, void *rock)
| Provide a time reference to O2.
int | o2_message_send (o2_message_ptr msg)
| Send an O2 message. (See also macros o2_send and o2_send_cmd).
o2_time | o2_time_get ()
| Get the estimated synchronized global O2 time.
o2_time | o2_local_time ()
| Get the real time using the local O2 clock.
const char * | o2_error_to_string (int i)
| Return text representation of an O2 error.
int | o2_finish ()
| release the memory and shut down O2.
int | o2_osc_port_new (const char *service_name, int port_num, int tcp_flag)
| Create a port to receive OSC messages.
int | o2_osc_port_free (int port_num)
| Remove a port receiving OSC messages.
int | o2_osc_delegate (const char *service_name, const char *ip, int port_num, int tcp_flag)
| Create a service that forwards O2 messages to an OSC server.
uint64_t | o2_osc_time_offset (uint64_t offset)
| Set the OSC time offset.
int | o2_schedule (o2_sched_ptr scheduler, o2_message_ptr msg)

## Variables

o2_arg_ptr | o2_got_start_array
o2_arg_ptr | o2_got_end_array
int | o2_stop_flag
| set this flag to stop o2_run()
const char * | o2_application_name
int | o2_clock_is_synchronized
| A variable indicating that the clock is the master or is synchronized to the master.
o2_sched | o2_gtsched
| Scheduler that schedules according to global (master) clock time.
o2_sched | o2_ltsched
| Scheduler that schedules according to local clock time.
o2_sched_ptr | o2_active_sched
| Current scheduler.

## Detailed Description

## Macro Definition Documentation

## o2_send

#define o2_send | ( |  | path,
|  |  | time,
|  |  | ...
| ) |  |

Construct and send O2 message with best effort protocol.

Normally, this constructs and sends an O2 message via UDP. If the destination service is reached via some other network protocol (e.g. Bluetooth), the message is delivered in the lowest latency protocol available, with no guaranteed delivery.

Parameters

  path | an address pattern
  time | when to dispatch the message, 0 means right now. In any case, the message is sent to the receiving service as soon as possible. If the message arrives early, it will be held at the service and dispatched as soon as possible after the indicated time.
  typestring | the type string for the message. Each character indicates one data item. Type codes are as in OSC. Some O2 type codes are not supported (if needed, create a message and use the appropriate o2_add_... function. Allowed type characters are those in "ifsbhtdScmTFNIB".
  ... | the data of the message. There is one parameter for each character in the typestring.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_send_cmd

#define o2_send_cmd | ( |  | path,
|  |  | time,
|  |  | ...
| ) |  |

Construct and send an O2 message reliably.

Normally, this constructs and sends an O2 message via TCP. If the destination service is reached via some other network protocol (e.g. Bluetooth), the message is delivered using the most reliable protocol available. (Thus, this call is considered a "hint" rather than an absolute requirement.)

Parameters

  path | an address pattern
  time | when to dispatch the message, 0 means right now. In any case, the message is sent to the receiving service as soon as possible. If the message arrives early, it will be held at the service and dispatched as soon as possible after the indicated time.
  typestring | the type string for the message. Each character indicates one data item. Type codes are defined by o2_type.
  ... | the data of the message. There is one parameter for each character in the typestring.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## WORD_ALIGN_PTR

#define WORD_ALIGN_PTR | ( |  | p | ) | ((char *) (((size_t) (p)) & ~3))

get the type string from o2_msg_data_ptr

Type strings begin with the comma (",") character, which is skipped

## Typedef Documentation

## o2_blob

typedef struct o2_blob o2_blob

The structure for binary large object.

A blob can be passed in an O2 message using the 'b' type. Created by calls to o2_blob_new().

## o2_message

typedef struct o2_message o2_message

an O2 message container

Note: This struct represents an O2 message that is stored on the heap. The length field must preceded data with no padding (see o2_msg_data declaration and the note that precedes it). To make sure there is no padding between length and data, we force the next pointer to occupy 8 bytes even if this is a 32-bit machine by making it part of a union with an 8-byte int64_t field named "pad_if_needed."

Note that o2_messages are on the heap and can be allocated, scheduled, sent, and freed. In contrast, o2_msg_data structures are contained within o2_messages and are passed to method handlers, but cannot be allocated, scheduled, sent, or freed. They are always the data field of a containing o2_message.

## o2_method_handler

typedef void(* o2_method_handler) (const o2_msg_data_ptr msg, const char *types, o2_arg_ptr *argv, int argc, void *user_data)

callback function to receive an O2 message

Parameters

  msg | The full message in host byte order.
  types | If you set a type string in your method creation call, then this type string is provided here. If you did not specify a string, types will be the type string from the message (without the initial ','). If parse_args and coerce_flag were set in the method creation call, types will match the types in argv, but not necessarily the type string or types in msg.

  argv

  An array of o2_arg types containing the values, e.g. if the first argument of the incoming message is of type 'f' then the value will be found in argv[0]->f. (If parse_args was not set in the method creation call, argv will be NULL.) For vectors, specified in types by the sequence "vi", "vh", "vf", or "vd", there will be one pointer in argv pointing to a vector description (the v field in o2_arg). For arrays, there are no pointers corresponding to '[' or ']' in the types string; but there is one pointer in argv for each array element.

  argc | The number of arguments received. (This is valid even if parse_args was not set in the method creation call.) This is the length of argv. Vectors count as one, array elements count as one each, and arrays themselves are not represented. For example, an empty array ("[]") in the type string adds nothing to the argc count or argv vector.
  user_data | This contains the user_data value passed in the call to the method creation call.

## o2_msg_data

typedef struct o2_msg_data o2_msg_data

data part of an O2 message

This data type is used to pass o2 message data to message handlers. It appears many other times in the code. You should NEVER allocate or free an o2_msg_data struct. Instead, create a message using o2_send_start(), o2_add_*(), and o2_message_finish() to get an o2_message_ptr. Within the o2_message, the data field is an o2_msg_data structure. We would use o2_message everywhere instead of o2_msg_data, but bundles can contain multiple o2_msg_data structures without the extra baggage contained in an o2_message.

Note: it is assumed that an o2_msg_data struct is always preceded by a 32-bit length. Ideally, length should therefore be in this struct, but then the compiler might add padding to put the timestamp on an 8-byte alignment. This could be solved with a pack pragma, but that is not standard C. To be safe and portable, I decided to just leave length out of the struct. The macro MSG_DATA_LENGTH can be used to access the length field.

## o2_time_callback

typedef o2_time(* o2_time_callback) (void *rock)

signature for callback that defines the master clock

See o2_clock_set() for details.

## Enumeration Type Documentation

## o2_type

enum o2_type

An enumeration of the O2 message types.

Enumerator
O2_INT32 | 32 bit signed integer.
O2_FLOAT | 32 bit IEEE-754 float.
O2_STRING | NULL terminated string (Standard C).
O2_BLOB | Binary Large OBject (BLOB) type.
O2_ARRAY_START | Start array or tuple.
O2_ARRAY_END | End array or tuple.
O2_INT64 | 64 bit signed integer.
O2_TIME | OSC time type.
O2_DOUBLE | 64 bit IEEE-754 double.
O2_SYMBOL | Used in systems distinguish strings and symbols.
O2_CHAR | 8bit char variable (Standard C).
O2_MIDI | 4 byte MIDI packet.
O2_TRUE | Symbol representing the value True.
O2_FALSE | Symbol representing the value False.
O2_NIL | Symbol representing the value Nil.
O2_INFINITUM | Symbol representing the value Infinitum.
O2_BOOL | Boolean value returned as either 0 or 1.
O2_VECTOR | Prefix to indicate a vector.

## Function Documentation

## o2_clock_set()

int o2_clock_set | ( | o2_time_callback | gettime,
|  | void * | rock
| ) |  |

Provide a time reference to O2.

Exactly one process per O2 application should provide a master clock. All other processes synchronize to the master. To become the master, call o2_clock_set().

The time reported by the gettime function will be offset to match the current local time so that local time continues to increase smoothly. You cannot force O2 time to match an external absolute time, but once o2_clock_set() is called, the difference between the time reference and O2's local time (as reported by o2_local_time()) will be fixed.

Parameters

  gettime | function to get the time in units of seconds. The reference may be operating system time, audio system time, MIDI system time, or any other time source. The times returned by this function must be non-decreasing and must increase by one second per second of real time to close approximation. The value may be NULL, in which case a default time reference will be used.

@parm rock an arbitrary value that is passed to the gettime function. This may be need to provide context. Use NULL if no context is required.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_dbg_calloc()

void * o2_dbg_calloc | ( | size_t | n,
|  | size_t | s,
|  | const char * | file,
|  | int | line
| ) |  |

allocate memory

O2 allows you to provide custom heap implementations to avoid priority inversion or other real-time problems. Normally, you should not need to explicitly allocate memory since O2 functions are provided to allocate, construct, and deallocate messages, but if you need to allocate memory, especially in an O2 message handler callback, i.e. within the sphere of O2 execution, you should use O2_MALLOC, O2_FREE, and O2_CALLOC.

free memory allocated by O2_MALLOC

allocate and zero memory (see O2_MALLOC)

## o2_error_to_string()

const char * o2_error_to_string | ( | int | i | ) |

Return text representation of an O2 error.

Parameters

  i | error number returned from some O2 function

Returns
  return the error message as a string

## o2_finish()

int o2_finish | ( |  | ) |

release the memory and shut down O2.

Close all sockets, free all memory, and restore critical variables so that O2 behaves as if it was never initialized.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_get_address()

int o2_get_address | ( | const char ** | ipaddress,
|  | int * | port
| ) |  |

Get IP address and TCP connection port number.

Before calling o2_hub(), you need to know the IP address and TCP connection port of another process. This call will retrieve the information, but the mechanism to transfer this information to another O2 process (or all of them) must be implemented outside of O2. (If the local network allows UDP broadcast and all hosts are on the local network, then you do not need this function or o2_hub(). Instead, let the discovery protocol exchange process addresses automatically.)

Parameters

  ipaddress | is a pointer that will be set to either NULL (on failure) or a string of the form "128.2.10.6". The string should not be modified, and the string will be freed by O2 if o2_finish() is called.
  port | will be set to a pointer to the O2 TCP connection port (or NULL on failure).

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_hub()

int o2_hub | ( | const char * | ipaddress,
|  | int | port
| ) |  |

Connect to a hub.

A "hub" is an O2 process that shares discovery information with other processes. This is an alternate form of discovery that is completely compatible with the broadcast-based discovery protocol, except (1) you do not need broadcast messages to communicate with a hub, (2) you do need the hub's IP address and port number. If the IP and port number can be shared, e.g. through a server or online database with a fixed address, you can work with networks that disallow broadcast, and you can connect across networks (which will not work with O2's normal discovery protocol if broadcast messages are not delivered across networks). To use a hub, you call o2_hub() with the hub's IP address and port. All O2 processes are effectively hubs with no clients, and o2_hub() simply connects to the hub as a client. The hub will then send discovery messages for all current and future O2 processes that are discovered, either through the normal discovery protocol or by connecting with the o2_hub() call.

After o2_hub() is called, discovery broadcasting is stopped, so if o2_hub() fails to connect to another process, you will only discover more processes if they initiate the exchange. You can use o2_hub() specifically to disable broadcast-based discovery by passing NULL as the ipaddress parameter.

You can call o2_hub() multiple times. Each time potentially makes a remote process become a hub for this local process. This might result in duplicate messages when new processes join the O2 application, but duplicate messages are ignored.

Parameters

  ipaddress | the IP address of the hub or NULL
  port | the port number of the hub's TCP port

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_initialize()

int o2_initialize | ( | const char * | application_name | ) |

Start O2.

If O2 has not been initialized, it is created and intialized. O2 will begin to establish connections to other instances with a matching application name.

Parameters

  application_name | the name of the application. O2 will attempt to discover other processes with a matching application name, ignoring all processes with non-matching names.

Returns
  O2_SUCCESS if success, O2_FAIL if an error occurs, #O2_RUNNING if already running, O2_BAD_NAME if `application_name` is NULL.

## o2_local_time()

o2_time o2_local_time | ( |  | ) |

Get the real time using the local O2 clock.

Returns
  the local time in seconds

## o2_memory()

int o2_memory | ( | void * | (*malloc)(size_t size),
|  | void((*free)(void *)) |
| ) |  |

Tell O2 how to allocate/free memory.

In many C library implementations, the standard implementation of free() must lock a data structure. This can lead to priority inversion if O2 runs at an elevated priority. Furthermore, the standard `malloc()` and `free()` do not run in constant (real) time. To avoid these problems, you can provide an alternate heap implementation for O2 by calling this function before calling o2_initialize(). For example, the provided functions can implement a private heap for the thread running O2.

Parameters

  malloc | a function pointer that behaves like standard `malloc()`
  free | a function pointer that behaves like standard `free()`

Returns
  O2_SUCCESS if succeed, O2_FAIL if not.

## o2_message_send()

int o2_message_send | ( | o2_message_ptr | msg | ) |

Send an O2 message. (See also macros o2_send and o2_send_cmd).

Parameters

  msg | points to an O2 message.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

After the call, the `msg` parameter is "owned" by O2, which will free it. Therefore, do not free msg after calling o2_message_send().

## o2_method_new()

int o2_method_new | ( | const char * | path,
|  | const char * | typespec,
|  | o2_method_handler | h,
|  | void * | user_data,
|  | int | coerce,
|  | int | parse
| ) |  |

Add a handler for an address.

Parameters

  path | the address including the service name. If the address is only the service name with no trailing slash, the handler will match any message to the service. Addresses should not conflict: An address should not match another address, and for every pair of addresses X and Y, X/ should not be a prefix of Y.
  typespec | the types of parameters, use "" for no parameters and NULL for no type checking
  h | the handler
  user_data | pointer saved and passed to handler
  coerce | is true if you want to allow automatic coercion of types. Coercion is only enabled if both coerce and parse are true.
  parse | is true if you want O2 to construct an argv argument vector to pass to the handle

Returns
  O2_SUCCESS if succeed, O2_FAIL if not.

## o2_osc_delegate()

int o2_osc_delegate | ( | const char * | service_name,
|  | const char * | ip,
|  | int | port_num,
|  | int | tcp_flag
| ) |  |

Create a service that forwards O2 messages to an OSC server.

Parameters

  service_name | The o2 service name without a '/' prefix.
  ip | The ip address of the osc server.
  port_num | The port number of the osc server.
  tcp_flag | Send OSC message via TCP protocol, in which case port_num is the TCP server port, not a connection.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

If `tcp_flag` is set, a TCP connection will be established with the OSC server. When the created service receives any O2 messages, it will send the message to the OSC server. If the incoming message has a timestamp for some future time, the message will be held until that time, then sent to the OSC server. (Ideally, O2 could convert the message to an OSC timestamped bundle and send it immediately to achieve precise forward-synchronous timing, but this requires clock synchronization with the OSC server, which is normally unimplemented.)

If this is a tcp connection, close it by calling o2_service_free().

## o2_osc_port_free()

int o2_osc_port_free | ( | int | port_num | ) |

Remove a port receiving OSC messages.

This removes a port created by o2_osc_port_new(). If you want to remove the corresponding service, you must also call o2_service_free() with the service name.

Parameters

  port_num | The port number that receives OSC messages.

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_osc_port_new()

int o2_osc_port_new | ( | const char * | service_name,
|  | int | port_num,
|  | int | tcp_flag
| ) |  |

Create a port to receive OSC messages.

OSC messages are converted to O2 messages and directed to the service. E.g. if the service is "maxmsp" and the message address is `/foo/x`, then the message is directed to and handled by `/maxmsp/foo/x`. If the #service_name does not exist at any time after calling o2_osc_port_new, incoming OSC messages will be dropped until the service is available again.

Parameters

  service_name | The name of the service to which messages are delivered
  port_num | Port number.
  tcp_flag | Be a TCP server for remote clients. Otherwise, use UDP

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_osc_time_offset()

uint64_t o2_osc_time_offset | ( | uint64_t | offset | ) |

Set the OSC time offset.

Parameters

  offset | the offset between (global) O2 time and OSC time

Returns
  the previous offset

O2 global time should start from 0.0 when the clock is started, whereas OSC time starts at 1 Jan 1900. The offset is the OSC time corresponding to O2 time 0.0. Equivalently, OSC_time = O2_time + offset.

## o2_poll()

int o2_poll | ( |  | ) |

Process current O2 messages.

Since O2 does not create a thread and O2 requires active processing to establish and maintain connections, the O2 programmer (user) should call o2_poll() periodically, even if not offering a service. o2_poll() runs a discovery protocol to find and connect to other processes, runs a clock synchronization protocol to establish valid time stamps, and handles incoming messages to all services. O2_poll() should be called at least 10 times per second. Messages can only be delivered during a call to o2_poll() so more frequent calls will generally lower the message latency as well as the accuracy of the clock synchronization (at the cost of greater CPU utilization). Human perception of timing jitter is on the order of 10ms, so polling rates of 200 to 1000 are advised in situations where rhythmic accuracy is expected.

Returns
  0 (O2_SUCCESS) if succeed, -1 (O2_FAIL) if not.

## o2_roundtrip()

int o2_roundtrip | ( | double * | mean,
|  | double * | min
| ) |  |

Get network round-trip information.

Returns
  If clock is synchronized, return O2_SUCCESS and set `*mean` to the mean round-trip time and `*min` to the minimum round-trip time of the last 5 (where 5 is the value of CLOCK_SYNC_HISTORY_LEN) clock sync requests. Otherwise, O2_FAIL is returned and `*mean` and `*min` are unaltered.

Note: You can get this information from a remote process by sending a message to `!ip:port/cs/rt`, where `ip:port` is the ip:port string for a process. (One way to get this is to call `o2_get_address` and construct a ip:port process name from the information returned. But then you can just call `o2_roundtrip` for the local process round trip information. For remote process names, you can create a handler for `/_o2/si`. The process name is provided whenever one of its services is created or otherwise changes status.) The type string for `!ip:port/cs/rt` is "s", and the parameter is an O2 address prefix. When the message is received, a reply is sent to an address formed by appending "/get-reply" to the address prefix. The reply message has the type string "sff", and the parameters are (1) the process ip:port name, (2) the mean of recent round trip times to the master clock, and (3) the minimum of recent round trip times. (The clock is set using the minimum, so this number is an upper bound on the clock skew for this process.

## o2_run()

int o2_run | ( | int | rate | ) |

Run O2.

Call o2_poll() at the rate (in Hz) indicated. Returns if a handler sets o2_stop_flag to non-zero.

## o2_schedule()

int o2_schedule | ( | o2_sched_ptr | scheduler,
|  | o2_message_ptr | msg
| ) |  |

/brief Schedule a message.

Rather than sending a message, messages can be directly scheduled. This is particulary useful if you want to schedule activity before clock synchronization is achieved. For example, you might want to poll every second waiting for clock synchronization. In that case, you need to use the local scheduler (o2_ltsched). o2_send() will use the global time scheduler (o2_gtsched), so your only option is to construct a message and call o2_schedule().

Parameters

  scheduler | a pointer to a scheduler (`&o2_ltsched` or `&o2_gtsched`)
  msg | a pointer to the message to schedule

The message is scheduled for delivery according to its timestamp (which is interpreted as local or global time depending on the scheduler).

The message is delivered immediately if the time is zero or less than the current time; however, to avoid unbounded recursion, messages scheduled within handlers are appended to a "pending messages" queue and delivered after the handler returns.

## o2_service_free()

int o2_service_free | ( | const char * | service_name | ) |

Remove a local service.

The #service_name corresponds to the parameter previously passed to o2_service_new or o2_osc_delegate. Note that if an OSC port forwards to this service (see o2_osc_port_new), the port remains open, but the OSC messages will be dropped. See o2_osc_port_free().

Parameters

  service_name | the name of the service

Returns
  #O2_SUCCSS if success, O2_FAIL if not.

## o2_service_new()

int o2_service_new | ( | const char * | service_name | ) |

Add a service to the current application.

Once created, services are "advertised" to other processes with matching application names, and messages are delivered accordingly. E.g. to handle messages addressed to "/synth/volume" you call

o2_service_new("synth");
o2_method_new("/synth/volume", "f", synth_volume_handler, NULL, NULL, TRUE);
o2_method_new
int o2_method_new(const char *path, const char *typespec, o2_method_handler h, void *user_data, int coerce, int parse)
Add a handler for an address.
o2_service_new
int o2_service_new(const char *service_name)
Add a service to the current application.

and define `synth_volume_handler` (see the type declaration for o2_method_handler and o2_method_new()) Normally, services should be unique across the application. If #service_name is already locally defined in this process (by a previous call to o2_service_new or o2_osc_delegate), this call will fail, returning O2_SERVICE_EXISTS. If matching service names are defined in two different processes, the process with the highest IP and port number (lexicographically) will provide the service. However, due to the distributed and asynchronous nature of O2, there may be some intervening time (typically a fraction of a second) during which a service is handled by two different processes. Furthermore, the switch to a new service provider could redirect a stream of messages, causing unexpected behavior in the application.

Parameters

  service_name | the name of the service

Returns
  O2_SUCCESS if success, O2_FAIL if not.

## o2_set_discovery_period()

o2_time o2_set_discovery_period | ( | o2_time | period | ) |

Set discovery period.

O2 discovery messages are broadcast periodically in case a new process has joined the application. The default period is 4 seconds. If there are N processes, each host will receive N/4 discovery messages per second. Since there are 5 discovery ports, each process will handle N/20 discovery messages per second, and a discovery message from any given process will be received every 20 seconds. (Note, however, that new processes send more frequently, sending 2 discovery messages to each of the 5 discovery port numbers within 2 seconds, so if messages are not dropped frequently, discovery of new processes will happen much faster than the worst-case 20 second polling period or even the 10 second expected wait.)

You can change the polling period from 4s by calling this function. The new polling period takes effect when the next discovery message is sent at the end of the current polling period.

Parameters

  period | the requested polling period; a minimum of 0.1s is enforced; 4s is the default (recommended).

Returns
  the previous polling period

## o2_status()

int o2_status | ( | const char * | service | ) |

Check the status of the service.

Parameters

  service | the name of the service

Returns
  - O2_FAIL if no service is found,
  - O2_LOCAL_NOTIME if the service is local but we have no clock sync yet,
  - O2_REMOTE_NOTIME if the service is remote but we have no clock sync yet,
  - O2_BRIDGE_NOTIME if service is attached by a non-IP link, but we have no clock sync yet (if the non-IP connection is not handled by this process, the service status will be O2_REMOTE_NOTIME),
  - O2_TO_OSC_NOTIME if service forwards to an OSC server but we have no clock sync yet (if the OSC connection is not handled by this process, the service status will be O2_REMOTE_NOTIME),
  - O2_LOCAL if service is local and we have clock sync,
  - O2_REMOTE if service is remote and we have clock sync,
  - O2_BRIDGE if service is handled locally by forwarding to an attached non-IP link, and we have clock sync. (If the non-IP connection is not local, the service status will be O2_REMOTE).
  - O2_TO_OSC if service is handled locally by forwarding to an OSC server and this process has clock sync. (If the OSC connection is not handled locally, the service status will be O2_REMOTE).
  Note that codes are carefully ordered to allow testing for categories:
  - to test if delivery is possible with a zero (immediate) timestamp, use `o2_status(service) > O2_FAIL`, `o2_status(service) >= 0`, or `o2_status(service) >= O2_LOCAL_NOTIME`.
  - to test if delivery is possible with a non-zero timestamp, use `o2_status(service) >= O2_LOCAL`. Note that status can change over time, e.g. the status of a remote service will be O2_FAIL until the service is discovered. It will then change to O2_REMOTE_NOTIME until both the sender and receiver achieve clock synchronization and share their synchronized status, and finally the status will become O2_REMOTE.

In the cases with no clock sync, it is safe to send an immediate message with timestamp = 0, but non-zero timestamps are meaningless because either the sending process has no way to obtain a valid timestamp or the receiver has no way to schedule delivery according to a timestamp.

Messages to services are dropped if the service has not been discovered. Timestamped messages (timestamp != 0) are dropped if the sender and receiver are not clock-synchronized. (`o2_status(service) >= O2_LOCAL`).

A special case is with `BRIDGE` and `OSC` services. In these cases, the O2 process offering the service can either schedule the messages locally, sending them according to the timestamp (and suffering some network latency), or if the destination process is synchronized, messages can be forwarded immediately for more precise scheduling at their final destination. O2 does not provide any way for clients/users to determine which of these methods is in effect, and in the case of messages being forwarded by an intermediary O2 process, the originator of the message cannot determine whether the service is offered by an O2 server on the local network, by an OSC server, or through a bridge to another network such as Bluetooth. The status at the originator will be simply O2_REMOTE or O2_REMOTE_NOTIME.

When the status of a service changes, a message is sent with address `!_o2/si`. The type string is "sis" and the parameters are (1) the service name, (2) the new status, and (3) the ip:port string of the process that offers (or offered) the service.

## o2_tap()

int o2_tap | ( | const char * | tappee,
|  | const char * | tapper
| ) |  |

copy messages from one service to another

Parameters

  tappee | the service to be tapped
  tapper | the service to which copies are sent

Returns
  O2_SUCCESS if success, O2_FAIL if not.

Create a new service named #tapper and have messages copied from #tappee. After this call, messages to tappee are copied, modified by replacing the service with tapper, and sent. The original message to tappee is also delivered.

## o2_time_get()

o2_time o2_time_get | ( |  | ) |

Get the estimated synchronized global O2 time.

This function returns a valid value either after you call o2_clock_set(), making the local clock the master clock for the O2 application, or after O2 has finished discovering and synchronizing with the master clock. Until then, -1 is returned.

The clock accuracy depends upon network latency, how often o2_poll() is called, and other factors, but

Returns
  the time in seconds, or -1 if global (master) time is unknown.

## Variable Documentation

## o2_active_sched

o2_sched_ptr o2_active_sched

extern

Current scheduler.

When a timed message is delivered by a scheduler, o2_active_sched is set to pount to the scheduler. A handler that constructs and schedules a message can use this pointer to continue using the same scheduler.

## o2_gtsched

o2_sched o2_gtsched

extern

Scheduler that schedules according to global (master) clock time.

Scheduling on this scheduler (including sending timed messages) will only work after clock synchronization is obtained. Until then, timed message sends will fail and attempts to o2_schedule() will fail.

## o2_ltsched

o2_sched o2_ltsched

extern

Scheduler that schedules according to local clock time.

It may be necessary to schedule events before clock synchronization with the master clock, or you may want to schedule local processing that ignores any changes in clock time or clock speed needed to stay synchronized with the master clock (even though these should be small). For example, O2 uses the local time scheduler to schedule the clock synchronization protocol, which of course must run before clock synchronization is obtained.

In these cases, you should schedule messages using o2_ltsched.

## o2_stop_flag

int o2_stop_flag

extern

set this flag to stop o2_run()

Some O2 applications will initialize and call o2_run(), which is a simple loop that calls o2_poll(). To exit the loop, set o2_stop_flag to TRUE