ZOSC(3)

๐ŸŽ€ ZOSC(3) ๐ŸŽ€

okay so bestie, welcome to the CZMQ Manual page for zosc, and I am so ready to walk you through this. grab a strawberry drink ๐Ÿ“, sit down, and let's get into it.


๐ŸŒธ NAME ๐ŸŒธ

zosc โ€” a whole class for working with Open Sound Control messages โœจ


๐Ÿ’– SYNOPSIS ๐Ÿ’–

๐Ÿšจ DRAFT ALERT ๐Ÿšจ this is a draft class, and it may change without notice. it's disabled in stable builds by default. if you use this in applications, please ask for it to be pushed to stable state. flip it on with --enable-drafts.

everything below lives inside this guard:

#ifdef CZMQ_BUILD_DRAFT_API

๐Ÿงš the constructors & destructor ๐Ÿงš

๐ŸŒŸ zosc_new

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_new (const char *address);

makes you a brand-new, totally empty OSC message with the address string you pass in. ๐Ÿซถ


๐ŸŒŸ zosc_fromframe

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_fromframe (zframe_t *frame);

builds a new OSC message out of a zframe. it takes ownership of the zframe, so likeโ€ฆ it's yours no more, bestie. ๐Ÿ’…


๐ŸŒŸ zosc_frommem

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_frommem (char *data, size_t size);

creates a new zosc message straight from memory. it takes ownership of the memory, and you call free on the data after construction. ๐Ÿง โœจ


๐ŸŒŸ zosc_create

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_create (const char *address, const char *format, ...);

creates a new OSC message from the format you give it plus the arguments. the format type tags are, and I quote, exactly these:

i - 32bit integer
h - 64bit integer
f - 32bit floating point number (IEEE)
d - 64bit (double) floating point number
s - string (NULL terminated)
t = timetag: an OSC timetag in NTP format (uint64_t)
S - symbol
c - char
m - 4 byte midi packet (8 digits hexadecimal)
T - TRUE (no value required)
F - FALSE (no value required)
N - NIL (no value required)
I - Impulse (for triggers) or INFINITUM (no value required)
b - binary blob

๐ŸŒŸ zosc_destroy

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT void
    zosc_destroy (zosc_t **self_p);

bye bye, OSC message. ๐Ÿ—‘๏ธ it's gone, it's done, it's destroyed.


๐ŸŒˆ getters & the format life ๐ŸŒˆ

๐ŸŒŸ zosc_size

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT size_t
    zosc_size (zosc_t *self);

hands you the chunk data size. ๐Ÿ“


๐ŸŒŸ zosc_data

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT byte *
    zosc_data (zosc_t *self);

returns the OSC chunk data. the caller does NOT own the data! read it, love it, don't free it. ๐Ÿ™ˆ


๐ŸŒŸ zosc_address

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT const char *
    zosc_address (zosc_t *self);

gives you the OSC address string. ๐Ÿ“ฎ


๐ŸŒŸ zosc_format

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT const char *
    zosc_format (zosc_t *self);

returns the OSC format of the message. and the type tags, once again, because we love a rerun:

i - 32bit integer
h - 64bit integer
f - 32bit floating point number (IEEE)
d - 64bit (double) floating point number
s - string (NULL terminated)
t = timetag: an OSC timetag in NTP format (uint64_t)
S - symbol
c - char
m - 4 byte midi packet (8 digits hexadecimal)
T - TRUE (no value required)
F - FALSE (no value required)
N - NIL (no value required)
I - Impulse (for triggers) or INFINITUM (no value required)
b - binary blob

๐ŸŒŸ zosc_append

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_append (zosc_t *self, const char *format, ...);

appends data to the OSC message. the format describes what needs to be appended. heads up: this essentially relocates all data! ๐Ÿซจ

the format type tags are:

i - 32bit integer
h - 64bit integer
f - 32bit floating point number (IEEE)
d - 64bit (double) floating point number
s - string (NULL terminated)
t = timetag: an OSC timetag in NTP format (uint64_t)
S - symbol
c - char
m - 4 byte midi packet (8 digits hexadecimal)
T - TRUE (no value required)
F - FALSE (no value required)
N - NIL (no value required)
I - Impulse (for triggers) or INFINITUM (no value required)
b - binary blob

๐ŸŒŸ zosc_retr

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_retr (zosc_t *self, const char *format, ...);

retrieves the values described by the given format. important things, bestie, don't skip:

  • zosc_retr creates the objects, and the caller must destroy them when finished. ๐Ÿ‘‘
  • the supplied pointers do not need to be initialized.
  • returns 0 if successful, or -1 if it failed to retrieve a value โ€” and in that failure case, the pointers are not modified.
  • if an argument pointer is NULL, it skips that value.
  • want the full list of type tags for the format string? see the format method. ๐Ÿ‘€

๐ŸŒŸ zosc_dup

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_dup (zosc_t *self);

makes a copy of the message as a new chunk object. returns a fresh zosc_t, or null if there wasn't enough heap memory. if the chunk is null, it returns null. the caller owns the return value and must destroy it when done. ๐Ÿซถ


๐Ÿฆ‹ pack / unpack ๐Ÿฆ‹

๐ŸŒŸ zosc_pack

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zframe_t *
    zosc_pack (zosc_t *self);

transforms a zosc into a zframe that can be sent in a message. the caller owns the return value and must destroy it when done. ๐Ÿ“ฆ


๐ŸŒŸ zosc_packx

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zframe_t *
    zosc_packx (zosc_t **self_p);

also transforms a zosc into a zframe that can be sent in a message โ€” but this one takes ownership of the chunk. the caller owns the return value and must destroy it when done. โœจ


๐ŸŒŸ zosc_unpack

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT zosc_t *
    zosc_unpack (zframe_t *frame);

turns a zframe back into a zosc. the caller owns the return value and must destroy it when done. ๐Ÿ”„


๐Ÿ’œ debug & identity ๐Ÿ’œ

๐ŸŒŸ zosc_print

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT void
    zosc_print (zosc_t *self);

dumps the OSC message to stdout, for debugging and tracing. ๐Ÿ–จ๏ธ


๐ŸŒŸ zosc_is

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT bool
    zosc_is (void *self);

probes the object you hand it and reports back whether it looks like a zosc_t. vibe check for types. ๐Ÿ•ต๏ธโ€โ™€๏ธ


๐ŸŒธ walking the message ๐ŸŒธ

๐ŸŒŸ zosc_first

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT const void *
    zosc_first (zosc_t *self, char *type);

returns a pointer to the item at the head of the OSC data. it sets the given char argument to the type tag of the data. if the message is empty, it returns NULL and sets the given char to NULL. ๐ŸŽ€


๐ŸŒŸ zosc_next

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT const void *
    zosc_next (zosc_t *self, char *type);

returns the next item of the OSC message. if the list is empty, returns NULL. to move back to the start of the OSC message, call zosc_first (). ๐Ÿฆ‹


๐ŸŒŸ zosc_last

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT const void *
    zosc_last (zosc_t *self, char *type);

returns a pointer to the item at the tail of the OSC message. it sets the given char argument to the type tag of the data. if the message is empty, returns NULL. ๐ŸŒ™


โญ the pop girlies โญ

๐ŸŒŸ zosc_pop_int32

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_int32 (zosc_t *self, int *val);

sets the provided 32 bit integer from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. โœจ


๐ŸŒŸ zosc_pop_int64

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_int64 (zosc_t *self, int64_t *val);

sets the provided 64 bit integer from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. โœจ


๐ŸŒŸ zosc_pop_float

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_float (zosc_t *self, float *val);

sets the provided float from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. โœจ


๐ŸŒŸ zosc_pop_double

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_double (zosc_t *self, double *val);

sets the provided double from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. โœจ


๐ŸŒŸ zosc_pop_string

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_string (zosc_t *self, char **val);

sets the provided string from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. the caller owns the string! ๐Ÿซถ


๐ŸŒŸ zosc_pop_char

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_char (zosc_t *self, char *val);

sets the provided char from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. โœจ


๐ŸŒŸ zosc_pop_bool

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_bool (zosc_t *self, bool *val);

sets the provided boolean from the type tag in the message. important: booleans are not represented in the data in the message, only in the type tag. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. ๐Ÿ’€


๐ŸŒŸ zosc_pop_midi

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT int
    zosc_pop_midi (zosc_t *self, uint32_t *val);

sets the provided 4 bytes (unsigned 32bit int) from the value at the current cursor position in the message. if the type tag at the current position doesn't correspond, it fails and returns -1. returns 0 on success. ๐ŸŽน


๐Ÿงš self test ๐Ÿงš

๐ŸŒŸ zosc_test

โš ๏ธ * Draft method, for development use, may change without warning *
CZMQ_EXPORT void
    zosc_test (bool verbose);

the self test of this class. ๐Ÿงช


and that's a wrap on the guard:

#endif // CZMQ_BUILD_DRAFT_API
๐Ÿ“ psst โ€” the source is asking for you: Please add '@interface' section in './../src/zosc.c'.

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

zosc โ€” work with Open Sound Control messages ๐ŸŒˆ

๐Ÿ“ also from the source: Please add @discuss section in ./../src/zosc.c.

๐ŸŽ€ EXAMPLE ๐ŸŽ€

okay bestie this is the whole vibe, straight from zosc_test method. it's long, it's chaotic, it's iconic, and we're going through it together piece by piece. grab a snack. ๐Ÿ“

first up: heap packets only, because that's how we roll.

// we need to have packets on the heap
char *p1 = (char *)malloc(40);
memcpy(p1, oscpacket, 40);
zosc_t *self = zosc_frommem(p1, 40);
assert (self);
assert ( zosc_is( self ));
assert(streq ( zosc_address(self), "/sample/address"));
assert(streq ( zosc_format(self), "iTfs"));
// check value
uint32_t test = 0;
bool b = false;
float f = 0.f;
char *s = "BLA";
zosc_retr(self, "iTfs", &test, &b, &f, &s);
assert(test == 1);
assert(b);
assert(fabsf(f - 3.14f) < FLT_EPSILON); // float equal
assert(streq(s, "hello"));
zstr_free(&s);

zosc_destroy (&self);

not the float comparison using FLT_EPSILON instead of == ๐Ÿ˜ญ that's the correct way to do it, no cap.


next: frames!! ๐Ÿฆ‹

zframe_t *frame = zframe_new(oscpacket, 40);
assert(frame);
assert(zframe_size(frame) == 40);
zosc_t *fosc = zosc_fromframe(frame);
assert (fosc);
assert(streq ( zosc_address(fosc), "/sample/address"));
assert(streq ( zosc_format(fosc), "iTfs"));
// test skipping retrieving values
zosc_retr(fosc, "iTfs", NULL, NULL, NULL, NULL);
char *fstr = "";
zosc_retr(fosc, "iTfs", NULL, NULL, NULL, &fstr);
assert( streq(fstr, "hello") );
zstr_free(&fstr);
zosc_destroy (&fosc);

the way that you can pass NULL for the args you don't care about and it just skips them โ€” lowkey obsessed.


now we're duplicating and checking values. and yeah, zosc_dup(NULL) gives you NULL back, we test that too because we're thorough like that. ๐Ÿ’…

// to the heap, otherwise we can't destroy
char *p2 = (char *)malloc(48);
memcpy(p2, testpack, 48);
zosc_t *testmsg = zosc_frommem(p2, 48);
assert( testmsg );
assert(streq ( zosc_address(testmsg), "/test"));
assert(streq ( zosc_format(testmsg), "ihTfds" ) );
// test duplicate
zosc_t *testmsgdup = zosc_dup(NULL);
assert( testmsgdup == NULL );
testmsgdup = zosc_dup(testmsg);
assert( testmsgdup );
assert(streq ( zosc_address(testmsgdup), "/test"));
assert(streq ( zosc_format(testmsgdup), "ihTfds" ) );
zosc_destroy(&testmsgdup);
// check value
int itest = -1;
int64_t ltest = -1;
bool btest = false;
float ftest = -1.f;
double dtest = -1.;
char *stest;
const char *format = zosc_format(testmsg);
zosc_retr(testmsg, format, &itest, &ltest, &btest, &ftest, &dtest, &stest);
assert( itest == 2 );
assert( ltest == 1844674407370 );
assert( btest );
assert( fabsf(ftest - 3.14f) < FLT_EPSILON ); // float equal
assert( fabs(dtest - 3.1415926535897932) < DBL_EPSILON );
assert( streq(stest, "hello") );
zstr_free(&stest);
zosc_destroy(&testmsg);

constructing messages!!! ๐Ÿซถ this is the part where we make stuff.

// test constructing messages
int64_t prez = 3;
zosc_t* conm = zosc_create("/construct", "iihfdscF", -2,2, prez, 3.14,6.283185307179586, "greetings", 'q');
assert(conm);
assert(streq(zosc_address(conm), "/construct"));
assert(streq(zosc_format(conm), "iihfdscF"));
if (verbose) zosc_print(conm);
int x,y;
int64_t z;
float zz;
double zzz;
char *ss = "aliens";
char q = 'z';
bool Ftest = true;
zosc_retr(conm, "iihfdscF", &x, &y, &z, &zz, &zzz, &ss, &q, &Ftest);
assert( x==-2 );
assert( y==2 );
assert( z==3 );
assert( fabsf(zz-3.14f) < FLT_EPSILON );
assert( fabs(zzz-6.283185307179586) < DBL_EPSILON );
assert( streq( ss, "greetings") );
assert( q == 'q');
assert( !Ftest );
zstr_free(&ss);

ngl the Ftest = true then assert( !Ftest ) is such a plot twist. the bool got flipped by the round trip. iykyk.


iterating! โœจ walk through every type tag one by one, and watch the 'h' case deliberately fail with int32 first (that's the // this will fail line, it's supposed to return -1) before succeeding with int64.

// test iterating
char type = '0';
const void *data = zosc_first(conm, &type);
while ( data )
{
    if (verbose)
        zsys_info("type tag is %c", type);

    switch (type)
    {
    case('i'):
    {
        int32_t test = 9;
        int rc = zosc_pop_int32(conm, &test);
        assert(rc == 0);
        assert( test == -2 || test == 2);
        break;
    }
    case('h'):
    {
        int32_t fail = 9;
        int rc = zosc_pop_int32(conm, &fail); // this will fail
        assert(rc == -1);
        int64_t test = 9;
        rc = zosc_pop_int64(conm, &test);
        assert(rc == 0);
        assert( test == 3 );
        break;
    }
    case('f'):
    {
        float flt_v = 0.f;
        int rc = zosc_pop_float(conm, &flt_v);
        assert(rc == 0);
        assert( fabsf(flt_v-3.14f) < FLT_EPSILON );
        break;
    }
    case 'd':
    {
        double dbl_v = 0.;
        int rc = zosc_pop_double(conm, &dbl_v);
        assert(rc == 0);
        assert( fabs(dbl_v-6.283185307179586) < DBL_EPSILON );
        break;
    }
    case 's':
    {
        char *str;
        int rc = zosc_pop_string(conm, &str);
        assert(rc == 0);
        assert(streq(str, "greetings"));
        zstr_free(&str);
        break;
    }
    case 'c':
    {
        char chr;
        int rc = zosc_pop_char(conm, &chr);
        assert(rc == 0);
        assert(chr == 'q');
        break;
    }
    case 'F':
    {
        bool bl;
        int rc = zosc_pop_bool(conm, &bl);
        assert(rc == 0);
        assert( !bl );
        break;
    }
    default:
        assert(0);
    }
    data = zosc_next(conm, &type);
}

that default: assert(0); is giving "unknown type tag? absolutely not, we die" energy ๐Ÿ’€


appending!! ๐Ÿ”ฅ shove two more values on the end and re-read the whole thing with the extended format string.

// test append
int64_t preal = -80000;
zosc_append(conm, "ih", -200, preal);
int ax,ay;
int64_t az;
float azz;
double azzz;
char *ass = "aliens";
char aq = 'z';
bool aFtest = true;
int ai = 0;
int64_t al = 0;
zosc_retr(conm, "iihfdscFih", &ax, &ay, &az, &azz, &azzz, &ass, &aq, &aFtest, &ai, &al);
assert( ax==-2 );
assert( ay==2 );
assert( az==3 );
assert( fabsf(azz-3.14f) < FLT_EPSILON );
assert( fabs(azzz-6.283185307179586) < DBL_EPSILON );
assert( streq( ass, "greetings") );
assert( aq == 'q');
assert( !aFtest );
assert( ai==-200 );
assert( al==-80000 );
zstr_free(&ass);

and finally, yeeting it over UDP. ๐ŸŒˆ there's a ZMQ_DGRAM branch and a plain-socket fallback branch, and a __WINDOWS__ variant inside that, because of course there is.

#ifdef ZMQ_DGRAM
zsock_t *dgrams = zsock_new_dgram("udp://*:*");
assert (dgrams);
zframe_t *t = zosc_pack(conm);
zstr_sendm( dgrams, "127.0.0.1:7777" );
zframe_send( &t, dgrams, 0);
zclock_sleep(10); // slow down, otherwise we've cleaned up before async send
zsock_destroy( &dgrams );
#else
int sockfd;
struct sockaddr_in     servaddr;

// Creating socket file descriptor
if ( (sockfd = (int)socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
    zsys_error("socket creation failed");
}

memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(7777);
servaddr.sin_addr.s_addr = INADDR_ANY;
#ifdef __WINDOWS__
sendto(sockfd, (const char *)zosc_data(conm), (int)zosc_size(conm),
        0, (const struct sockaddr *) &servaddr,
            sizeof(servaddr));
#else
sendto(sockfd, zosc_data(conm), zosc_size(conm),
        0, (const struct sockaddr *) &servaddr,
            sizeof(servaddr));
#endif
#endif
zosc_destroy(&conm);
#ifdef __WINDOWS__
zsys_shutdown();
#endif

note the little zclock_sleep(10) with the comment // slow down, otherwise we've cleaned up before async send โ€” that's not decorative bestie, that's load-bearing. do not delete it. ๐Ÿ™ˆ


๐Ÿซถ AUTHORS ๐Ÿซถ

The czmq manual was written by the authors in the AUTHORS file.


๐ŸŒˆ RESOURCES ๐ŸŒˆ

Main web site:

Report bugs to the email <zeromq-dev@lists.zeromq.org[1]>


Copyright (c) the Contributors as noted in the AUTHORS file. This file is part of CZMQ, the high-level C binding for 0MQ: http://czmq.zeromq.org. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. LICENSE included with the czmq distribution.


๐Ÿ“ NOTES ๐Ÿ“

  1. zeromq-dev@lists.zeromq.org mailto:zeromq-dev@lists.zeromq.org

and that's the whole page bestie, you made it to the end ๐ŸŽ‰ this was CZMQ 4.2.1, dated 01/16/2026, and honestly? she ate. go forth and encode some OSC. ๐Ÿ’–โœจ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from