0

Motivation: I'm working on an app that makes several (client) phones read audio data from a (server) phone. The idea is that they must all play the song at the exact same time together.

Premise: I must figure out a way to make all phones start at a certain time stamp that is absolute (ie it's not relative to the use set clock of either phone etc..).. based on some research I figured the best way to do this is to use CFAbsoluteTimeGetCurrent(); The idea here is that I get the latency it takes the server to communicate with each phone (b/c GKSession is done serially not in parallel apparently), add that latency to the current time, and then have every phone play the song starting at that start time. This start time must be absolute, it cannot be relative.

Question: How can I represent a time in the future in a number, that can be later on used to construct a CFDateRef. (The reason it has to be expressed as a number is that I must be able to send it in a packet..)

Example: This is a bit of code that explains what I'm trying to achieve:

-(void)viewDidLoad
{
    _timer = [Timer new];
    [_timer setCurrentTimeAsReferencepoint];

    [self performSelector:@selector(performAction) withObject:NULL afterDelay:5];
}


-(void)performAction
{
    double diff = [_timer getTimeElapsedinAbsTime];
    double timeInFuture = diff + [Timer getCurTime];
    NSLog(@"this is time in future in abs terms %f",timeInFuture);

    CFDateRef futureDate = CFDateCreate(NULL, timeInFuture);
    CFDateRef dateNow = CFDateCreate(NULL, [Timer getCurTime]);

    Boolean keepTesting = true;

    while (keepTesting) {
        if (CFDateCompare(dateNow, futureDate,NULL) == 0)   // ie both times are equal
        {
            NSLog(@"now is the time!");
            keepTesting = false;
        } else {
            NSLog(@"now isn't the time.. skip");
        }
    }
}

in Timer.m:

-(void)setCurrentTimeAsReferencepoint
{
    _referencePoint = [[self class] getCurTime];
    NSLog(@"this is reference point %f",_referencePoint);  
}

+(double)getCurTime
{
    return (double)CFAbsoluteTimeGetCurrent();
}

// not used in the above code.. but used when i compare the time of the server phone with the client phone
+(double)getTimeDifference:(double)time1
                     time2:(double)time2
{    
    CFDateRef newDate = CFDateCreate(NULL, time2);
    CFDateRef oldDate = CFDateCreate(NULL, time1);

    CFTimeInterval difference = CFDateGetTimeIntervalSinceDate(newDate, oldDate);    

    NSLog(@"this is time difference %f",fabs(difference));
    CFRelease(oldDate); CFRelease(newDate); 

    // fabs = absolute value
    return fabs(difference);    
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
abbood
  • 23,101
  • 16
  • 132
  • 246
  • 1
    What if each iPhone had different times? – TheAmateurProgrammer Oct 06 '12 at 13:56
  • Concurrency issues are always a pain. What if each client read a date from the server at which it should play the sound packet, and just make it queue – Richard J. Ross III Oct 06 '12 at 13:58
  • @theamateurprogrammer well CFAbsoluteTimeGetCurrent() is not relative to the timing of the iphone.. It's absolute across the board.. At least that's my understanding – abbood Oct 06 '12 at 18:52
  • @Richard J. ROSS III that's exactly what I'm trying to do.. The question is to find the earliest date possible. But once identified.. How can I express it in an absolute term.. To avoid the problem mentioned in the first comment above? – abbood Oct 06 '12 at 18:56
  • @abbood And if you read the [docs](https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFTimeUtils/Reference/reference.html), `The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.` – TheAmateurProgrammer Oct 07 '12 at 00:49
  • @TheAmateurProgrammer your point is well taken.. I'll do some more testing in multiple devices to get to the bottom of this.. but one thing to keep in mind: not all of the apple doc's is 100% accurate.. i remember them saying something about AVAssetReader not being real time compatible.. i use it in real time just fine.. – abbood Oct 08 '12 at 06:33
  • @TheAmateurProgrammer it seems you are right.. if i use CFAbsoluteTimeGetCurrent(), it gives me two different results on two phones that have their clocks set up differently – abbood Oct 08 '12 at 11:31
  • @abbood Instead, I think you can grab the time from the internet using free services, though you'll have to be sure if the server you're getting time from is reliable, and the time it takes to get the time from the server. – TheAmateurProgrammer Oct 08 '12 at 11:59
  • @TheAmateurProgrammer can't do that.. as i'm assuming no internet access in my architecture.. instead i'm following instructions here http://stackoverflow.com/questions/389417/synchronization-of-clocks-between-two-remote-computers – abbood Oct 08 '12 at 12:29
  • @abbood I'm not very familiar with networking, but is it possible to just keep sending each client a packet every "x" interval and so that they are all in sync with each other. – TheAmateurProgrammer Oct 08 '12 at 12:52
  • @TheAmateurProgrammer it's not that simple. thing is there is no guarantee of a constant latency time of a packet delivery.. and if your'e looking for a highly accurate sync (ie <5ms) then there is very little tolerance for deviation – abbood Oct 08 '12 at 13:54

1 Answers1

0

It turns out it's simpler than I thought (note: getCurTime is defined in the question):

-(void)performAction
{
    double curTime = [Timer getCurTime];
    double timeInFuture = 2 + curTime;
    NSLog(@"this is time in future in abs terms %f and this is curTime %f",timeInFuture, curTime);

    CFDateRef futureDate = CFDateCreate(NULL, timeInFuture);
    CFDateRef dateNow = CFDateCreate(NULL, curTime);

    Boolean keepTesting = true;

    while (keepTesting) {
        dateNow = CFDateCreate(NULL, [Timer getCurTime]);
        if (CFDateCompare(dateNow, futureDate,NULL) >= 0)   // ie both times are equal or we've 
                                                            // gone past the future date time
        { 
            NSLog(@"now is the time!");
            keepTesting = false;
            return;
        } else {
            NSLog(@"now isn't the time.. skip");
        }
    }
}
abbood
  • 23,101
  • 16
  • 132
  • 246
  • this solution solves only half the question.. unfortunately the premise is wrong.. please see the discussion below the quesiton – abbood Oct 08 '12 at 14:20