Permalink
Browse files

Disable tempo sending outside of running timeline

  • Loading branch information...
michaeltyson committed Feb 5, 2015
1 parent 3fa25ba commit c666cc4baeb3974375cc78fac9c300a7714d2ad0
View
@@ -137,6 +137,26 @@ - (void)testSpontaneousStart {
}
- (void)testStartWithoutPrecedingTempoTicks {
uint64_t startTime = SECurrentTimeInHostTicks();
_sender.tempo = 125;
[_sender startAtTime:startTime];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
XCTAssertTrue(_receiver.clockRunning);
XCTAssertEqualWithAccuracy([_receiver timelinePositionForTime:startTime], 0, SESecondsToHostTicks(1.0e-6));
XCTAssertEqual(_observer.observations.count, 3);
XCTAssertEqualObjects(_observer.observations[0], @"receivingTempo");
XCTAssertEqualObjects(_observer.observations[1], @"tempo");
XCTAssertEqualObjects(_observer.observations[2], @"clockRunning");
XCTAssertEqual(_observer.notifications.count, 3);
XCTAssertEqualObjects(((NSNotification*)_observer.notifications[0]).name, SEMIDIClockReceiverDidStartTempoSyncNotification);
XCTAssertEqualObjects(((NSNotification*)_observer.notifications[1]).name, SEMIDIClockReceiverDidChangeTempoNotification);
XCTAssertEqualObjects(((NSNotification*)_observer.notifications[2]).name, SEMIDIClockReceiverDidStartNotification);
XCTAssertEqual(_receiver.error, 0);
}
- (void)testStartWithOffset {
_sender.tempo = 125;
double initialTimelinePosition = 10;
@@ -603,6 +603,7 @@
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
"TEST_RIG_BUILD=1",
);
INFOPLIST_FILE = "Tests/TheSpectacularSyncEngineTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
@@ -142,15 +142,15 @@ void SEMIDIClockReceiverReceivePacketList(__unsafe_unretained SEMIDIClockReceive
continue;
}
#ifdef DEBUG_ALL_MESSAGES
#ifdef DEBUG_ALL_MESSAGES
NSLog(@"%llu: %@",
timestamp,
packet->data[0] == SEMIDIMessageClockStart ? @"Start" :
packet->data[0] == SEMIDIMessageClockStop ? @"Stop" :
packet->data[0] == SEMIDIMessageContinue ? @"Continue" :
packet->data[0] == SEMIDIMessageSongPosition ? @"Song Position" :
packet->data[0] == SEMIDIMessageClock ? @"Clock" : @"Other message");
#endif
#endif
switch ( packet->data[0] ) {
case SEMIDIMessageClockStart:
@@ -352,9 +352,9 @@ void SEMIDIClockReceiverReceivePacketList(__unsafe_unretained SEMIDIClockReceive
}
if ( reportUpdate ) {
#ifdef DEBUG_LOGGING
#ifdef DEBUG_LOGGING
NSLog(@"Tempo is now %lf (was %lf)", tempo, THIS->_tempo);
#endif
#endif
THIS->_tempo = tempo;
THIS->_sampleCountSinceLastTempoUpdate = 0;
@@ -670,7 +670,7 @@ static void SESampleBufferIntegrateSample(SESampleBuffer *buffer, uint64_t sampl
}
}
#ifdef DEBUG_LOGGING
#ifdef DEBUG_LOGGING
// Diagnosis logging
if ( sample < 1e9 ) {
// Tick interval
@@ -694,7 +694,7 @@ static void SESampleBufferIntegrateSample(SESampleBuffer *buffer, uint64_t sampl
SEHostTicksToSeconds(buffer->standardDeviation),
((double)buffer->standardDeviation / (double)buffer->mean) * 0.5 * 100.0);
}
#endif
#endif
}
@@ -24,10 +24,14 @@ extern "C" {
* changes.
*
* To use it, initialise it with an object that implements SEMIDIClockSenderInterface,
* used to send outgoing messages, and provide a tempo via the tempo property. The sender
* will immediately begin sending ticks to sync the tempo. Optionally set the
* timelinePosition property to cue playback to a particular position in your app's
* timeline. Call startAtTime: to start the clock and begin advancing the timeline.
* used to send outgoing messages. Then provide a tempo via the tempo property, and
* call startAtTime: to start the clock and begin advancing the timeline.
*
* Note that, due to the general lack of acceptable support for Song Position and
* Continue messages in apps and some hardware, use of the timeline position facilities
* of this class may have no effect in receivers with a limited implementation.
* Furthermore, for related technical reasons this class will only send clock ticks
* when the clock is started.
*/
@interface SEMIDIClockSender : NSObject
@@ -44,27 +48,19 @@ extern "C" {
* Start clock
*
* The clock will be started. Make sure you have provided a tempo first, via the
* tempo property, ideally well in advance of starting the clock in order to provide
* ample time for convergence.
* tempo property.
*
* If you pass a zero timestamp (recommended), this method will determine the next safe
* timestamp for the start action, within the next few tens of milliseconds, and will
* return this timestamp to you. You should wait to start the timeline in your own app
* until this time is reached, to ensure sync.
*
* If you pass a non-zero timestamp that is within around a hundred milliseconds of the
* current time, you may experience some sync discrepancies for a little while after
* the remote clock is started. This is due to interference from scheduled ticks that
* this class has sent in advance, in order to overcome system congestion and latency.
* Pass a timestamp at which to apply the start, to achieve the best sync.
*
* If you are starting the clock anywhere but the beginning of your app's timeline,
* be sure to first assign a value to the timelinePosition property to cue playback
* position.
* position. Note that due to generally poor support of Song Position/Continue in
* receivers, this may have no effect, however.
*
* @param applyTime The global timestamp at which to start the clock, in host ticks,
* or zero (recommended). See mach_absolute_time, or SECurrentTimeInHostTicks
* @return The timestamp at which the start will occur. Your app should wait until this
* time before starting the local clock.
* or zero. See mach_absolute_time, or SECurrentTimeInHostTicks
* @return The timestamp at which the start will occur. If you passed zero for applyTime,
* your app should wait until this time before starting the local clock.
*/
-(uint64_t)startAtTime:(uint64_t)applyTime;
@@ -89,6 +85,9 @@ extern "C" {
* use while the clock is running (not recommended by the MIDI standard, but sometimes
* unavoidable, such as while continuously looping over a region).
*
* Note that due to generally poor support of Song Position/Continue in receivers, this may
* have no effect.
*
* It is recommended that you limit resolution to 16th notes (0.25). If you do so, and
* you provide a zero apply timestamp (recommended), then this method will automatically
* pick the soonest apply time that will result in a safe on-the-beat transition to the new
@@ -130,9 +129,12 @@ extern "C" {
/*!
* The current position in the timeline (in beats)
*
* Use this property to cue playback to the given the timeline position, in beats
* Assign a value to this property to cue playback to the given the timeline position, in beats
* (that is, quarter notes - use SESecondsToBeats to convert from seconds, if necessary).
*
* Note that due to generally poor support of Song Position/Continue in receivers, this may
* have no effect.
*
* Important: The MIDI standard recommends that the timeline position only be changed while
* the clock is stopped, to avoid sync problems. When the clock is stopped, setting this
* property will cue the playback position for when the clock is started using startAtTime:.
@@ -151,14 +153,8 @@ extern "C" {
/*!
* The current tempo (beats per minute)
*
* Use this property to assign a new tempo; it will immediately be synced to the
* remote side by adjusting the rate at which clock ticks are transmitted.
*
* Note that depending on the remote implementation, convergence to the new tempo
* may not be immediate, so it is recommended that you provide a tempo as far in
* advance of starting the clock as possible.
*
* If you set this value to zero, the sender will cease sending clock messages.
* Use this property to assign a tempo - be sure to assign a tempo prior to starting the
* clock.
*/
@property (nonatomic) double tempo;
@@ -15,6 +15,12 @@
static const double kThreadPriority = 0.8; // Priority of the sender thread
static const int kMaxPendingMessages = 10; // Size of pending message buffer
// #define SUPPORT_TEMPO_TICKS_OUTSIDE_TIMELINE // Uncomment to enable sending tempo ticks when clock is stopped
#if defined(TEST_RIG_BUILD) && !defined(SUPPORT_TEMPO_TICKS_OUTSIDE_TIMELINE)
#define SUPPORT_TEMPO_TICKS_OUTSIDE_TIMELINE
#endif
@interface SEMIDIClockSenderThread : NSThread
@property (nonatomic, weak) SEMIDIClockSender * sender;
@end
@@ -70,6 +76,14 @@ -(void)stop {
self.started = NO;
}
#ifndef SUPPORT_TEMPO_TICKS_OUTSIDE_TIMELINE
if ( _thread ) {
// Stop the thread
[_thread cancel];
self.thread = nil;
}
#endif
}
-(uint64_t)setActiveTimelinePosition:(double)timelinePosition atTime:(uint64_t)applyTime {
@@ -121,6 +135,7 @@ -(void)setTempo:(double)tempo {
_tempo = tempo;
}
#ifdef SUPPORT_TEMPO_TICKS_OUTSIDE_TIMELINE
if ( tempo != 0.0 && !_thread ) {
// Start the thread which will send out the ticks - in a moment, in case clock is started next
[self performSelector:@selector(startThread) withObject:nil afterDelay:0.0];
@@ -129,6 +144,7 @@ -(void)setTempo:(double)tempo {
[_thread cancel];
self.thread = nil;
}
#endif
}
-(uint64_t)startOrSeekWithPosition:(double)timelinePosition atTime:(uint64_t)applyTime startClock:(BOOL)start {

0 comments on commit c666cc4

Please sign in to comment.