Clock Difference

In order to synchronise synthesis events sent to servers running on two different machines, it is necessary to know the difference between their two clocks and compensate for this in the time argument of sendMsg or sendBundle. Here is one method of finding this difference.

These classes must be compiled in the usual way before use.

Beware, the following contains overrides to other core classes, so please check the full document before using.

(back to Code Pool)

/*

// nonprivate@cylob.com - 5 March 2007

// HOW TO USE: // execute these one by one on both machines

x = C2_ClockDifference.new; x.setup;

// then enter i.p. of remote machine, within a second or so you'll get the result // ... this is important, it is not an instant process!

y = "192.168.2.21";		// the i.p. to check out x.calcClockDifference(y);

// the results get stored in an identity dictionary (called id) for each i.p. investigated // if nothing is received back then nothing gets added to the dictionary

x.id.postln;

// after the result has been returned, you can do this: (remember to turn the i.p. into a symbol to do so)

x.id.at(y.asSymbol).postln;



C2_ClockDifference {

var listener, sender; var remoteAddr, masterAddr; var <id; // Listener is LocalClient // Sender is Client // function is ClientFunc calcClockDifference { arg remoteIP; if( NetAddr.testAddr(remoteIP) == true,			{			remoteAddr = NetAddr(remoteIP, 57120);			// send to the remoteAddr			sender = Client(\clockDiffSender, remoteAddr);			sender.send(\sendClockDiffMsg, Process.elapsedTime, NetAddr.myIP);			},			{			["clock difference - IP is not accessable"].postln;			}		); }	stopListener { ["C2 clock difference ... listener has been stopped"].postln; listener.stop; }

setup { id = IdentityDictionary.new; listener = LocalClient.default; listener.start; // this stuff gets executed on the remote server // it's here because another computer might interrogate this one as the remote server ClientFunc(			\sendClockDiffMsg,			{			arg inProcessTimeSync, sendToIP;			var time, getAccurateTime, whenDate, whenProcess, thisProcessTimeSync, sender;				["clock difference ... instruction has been received and will be sent back to IP " ++ sendToIP.asString].postln;				sender = Client(\clockDiffReturn, NetAddr(sendToIP.asString, 57120));				thisProcessTimeSync = Process.elapsedTime;				getAccurateTime = Routine({ var flag = 0, start; start = Date.localtime.second; while(						{flag == 0},						{						if (start != Date.localtime.second, {							flag = 1; whenDate = Date.localtime; whenProcess = Process.elapsedTime; sender.send(\returnClockDiffResult, inProcessTimeSync, thisProcessTimeSync, 							whenProcess, whenDate.hour, whenDate.minute, whenDate.second, NetAddr.myIP); }						);						0.00001.wait;						}					); });				SystemClock.play(getAccurateTime);

}		);		// this stuff gets executed here, it is triggered by the remote server		ClientFunc( \returnClockDiffResult, {			arg processTimeSyncHere, processTimeSyncThere, inProcessTime, inHour, inMinute, inSecond, receivedFromIP; var time, getAccurateTime, whenDate, whenProcess, thereAll, hereAll, serverDelay; if(receivedFromIP.asString == remoteAddr.hostname.asString,				{				thereAll = [inHour, inMinute, inSecond].deepCopy;				getAccurateTime = Routine({ var flag = 0, start; start = Date.localtime.second; while(						{flag == 0},						{						if (start != Date.localtime.second, {							flag = 1; whenDate = Date.localtime; whenProcess = Process.elapsedTime; hereAll = [whenDate.hour, whenDate.minute, whenDate.second]; serverDelay = C2_DateMaths.workOutSchedDelay(								inProcessTime, thereAll, processTimeSyncThere, 								whenProcess, hereAll, processTimeSyncHere							); // cut it some slack serverDelay = serverDelay - 0.001; }						);						0.00001.wait;						}					); ["C2_ClockDifference: server delay has been calculated: it is " ++ serverDelay.asString].postln; id.put(receivedFromIP.asSymbol, serverDelay); });				SystemClock.play(getAccurateTime);				},				{				["C2_ClockDifference: server delay has NOT been calculated ... received result from unexpected IP"].postln;				}			); }		);	}

}

C2_DateMaths {

*addSeconds { arg additionValueInSeconds, hms; var temp, leftoverSeconds; var hour, minute, second; hour = hms.at(0); minute = hms.at(1); second = hms.at(2); // hours temp = additionValueInSeconds.div(3600); hour = hour + temp; if (hour < 0, {hour = hour + 24}); if (hour > 23, {hour = hour - 24}); additionValueInSeconds = additionValueInSeconds - (temp * 3600); // minutes temp = additionValueInSeconds.div(60); minute = minute + temp; if (minute < 0, 			{			minute = minute + 60; hour = hour - 1;			if (hour < 0, {hour = hour + 24});			}		); if (minute > 59, 			{			minute = minute - 60; hour = hour + 1;			if (hour > 23, {hour = hour - 24});			}		); additionValueInSeconds = additionValueInSeconds - (temp * 60); // seconds temp = additionValueInSeconds.div(1); second = second + temp; if (second < 0, 			{			second = second + 60; minute = minute - 1;			if (minute < 0, {				minute = minute + 60; hour = hour - 1; if (hour < 0, {hour = hour + 24}); }			);			}		);		if (second > 59, 			{			second = second - 60; minute = minute + 1;			if (minute > 59, {				minute = minute - 60; hour = hour + 1; if (hour > 23, {hour = hour - 24}); }			);			}		);		additionValueInSeconds = additionValueInSeconds - temp; leftoverSeconds = additionValueInSeconds; ^[hour, minute, second + leftoverSeconds] }	*subtractHmsOutputSeconds { arg hms1, hms2; var outHours, outMinutes, outSeconds; outHours = hms1.at(0) - hms2.at(0); outMinutes = hms1.at(1) - hms2.at(1); outSeconds = hms1.at(2) - hms2.at(2); outSeconds = (outHours * 3600) + (outMinutes * 60) + outSeconds; ^outSeconds

}		*workOutSchedDelay { arg that_ptDated, that_date, that_ptArb, this_ptDated, this_date, this_ptArb; var that_dateArb, this_dateArb; var x, schedDelay; x = that_ptDated - that_ptArb; that_dateArb = C2_DateMaths.addSeconds(x.neg, that_date); x = this_ptDated - this_ptArb; this_dateArb = C2_DateMaths.addSeconds(x.neg, this_date); schedDelay = C2_DateMaths.subtractHmsOutputSeconds(that_dateArb, this_dateArb);

^schedDelay }	}

// INCLUDING OVERRIDES ALSO USEFUL FOR OTHER THINGS // bits and pieces adapted and / or stolen from things posted to sc users list

+ NetAddr {

*new { arg hostname, port=0; var addr; addr = if (hostname.notNil,			{ 				try 				{ hostname.gethostbyname } 				{ 				"*** CMS_Overrides: NetAddr ***".postln;				("server " ++ hostname ++ " has not been found, so i.p. will be set to 0.0.0.0").postln;				"******************************".postln;				hostname = "0.0.0.0"; 				hostname.gethostbyname 				}			},			{ 			0 			}		); ^super.newCopyArgs(addr, port, hostname); }	*testAddr { arg addr; var result = true; try { addr.gethostbyname } { result = false } ^result; }

*myIP { var j, k, res; res = Pipe.findValuesForKey("ifconfig", "inet"); ^res !? {			res = res.reject(_ == "127.0.0.1"); // remove loopback device ip			if(res.size > 1) { warn("the first of those devices were chosen: " ++ res) }; res[0] };	}

}

+ Pipe { *do { arg commandLine, func; var line, pipe = this.new(commandLine, "r"), i=0; {			line = pipe.getLine; while { line.notNil } { func.value(line, i); i = i + 1; line = pipe.getLine; }		}.protect { pipe.close };

}	*findValuesForKey { arg commandLine, key, delimiter=$ ; var j, k, indices, res, keySize; key = key ++ delimiter; keySize = key.size; Pipe.do(commandLine, { |l|			indices = l.findAll(key);			indices !? {				indices.do { |j|					j = j + keySize;					while { l[j] == delimiter } { j = j + 1 };					k = l.find(delimiter.asString, offset:j) ?? { l.size } - 1;					res = res.add(l[j..k])				};			};		}); ^res }

}