How best to slow down / queue outgoing OSC messages

I’ve made an extension that communicates with TouchOSC. Sometimes (like when it resets the interface) it sends up to 2500 OSC messages at once. Over even a direct ethernet connection (but not when connected directly via USB), some of these UDP messages get dropped. I wrote the code below to queue the messages and slow them down:

sendMsgIfConnected {arg ... args;
		if(isConnected,{
			sendQueue.add(args);
		},{
			("DanDKVTouchOSC couldn't send message to "++args[0]).error;
		});
		if(queueClockHasQueue.not,{
			queueClockHasQueue = true;
			SystemClock.sched(0,{
				if(sendQueue.size > 0,{
					touchOSC.sendMsg(*sendQueue.first);
					sendQueue.removeAt(0);
					0.0001;
				},{
					queueClockHasQueue = false;
					nil;
				});
			});
		});
	}

This works. My question is: is this a decent way to do this, or am I overlooking a more efficient / built-in way to slow down message emission? Alternatively, is there a way to send OSC messages via TCP from SC?

My question is: is this a decent way to do this,

It depends on your application. If you can identify specific methods/functions that send many messages at once, you can instead send the messages in smaller batches and wait in between.

For example, if you want so send a large Array of messages, you can do something like the following:

someMethod { |array|
    var batchSize = 64; // some value
    var waitTime = 0.05; // some value
    forkIfNeeded {
        array.do { |msg, i|
            if ((i % batchSize == 0) and: { i > 0 }) { waitTime.wait };
            // send msg
        }
    }
}

For a practical example, check out Buffer.sendCollection in the Class Library.


Alternatively, is there a way to send OSC messages via TCP from SC?

There is:

n = NetAddr("localhost", 40000).connect({ "disconnected!".postln });
n.sendMsg('/foo', 1, 2, 3);

As you probably know, TCP is stream based and, unlike UDP, does not have a notion of packets. This means that applications must frame their messages so that the receiving end knows when a new message starts. NetAddr.sendMsg (in TCP mode) does this by prepending the OSC message with the message size (as a big-endian 32-bit integer). For example, the message ['/foo' 1 2 3] is actually send like this over TCP:

[ 0, 0, 0, 28, 47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 105, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3 ]

However, this only works if the receiving end uses the same framing mechanism! NetAddr.sendMsg (in TCP mode) is intended to be used to communicate with scsynth or other sclang clients.

Now, what do you do if your remote application uses another framing technique (e.g. SLIP)? Fortunately, you can also send raw bytes over TCP with NetAddr.sendRaw. In this case, you would first need to convert the OSC message itself to raw bytes with Array.asRawOSC and then do the appropriate framing yourself before passing it to NetAddr.sendRaw.

@Spacechild1 Thanks so much for this super informative response. Can’t believe this is my first time learning that SC does have built-in TCP! Thanks also for the help on the batch queuing. Much appreciated.

1 Like