Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Madsen committed Mar 9, 2016
2 parents af8f7b3 + bf6d53f commit 40a016e
Showing 1 changed file with 33 additions and 37 deletions.
70 changes: 33 additions & 37 deletions Source/MIKMIDISequencer.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ @interface MIKMIDISequencer ()

@property (nonatomic, strong) NSMutableDictionary *pendingRecordedNoteEvents;

@property (nonatomic) MusicTimeStamp playbackOffset;
@property (nonatomic) MusicTimeStamp startingTimeStamp;

@property (nonatomic, strong) NSMapTable *tracksToDestinationsMap;
Expand Down Expand Up @@ -149,14 +148,25 @@ - (void)startPlayback
}

- (void)startPlaybackAtTimeStamp:(MusicTimeStamp)timeStamp
{
[self startPlaybackAtTimeStamp:timeStamp adjustForPreRollWhenRecording:YES];
}

- (void)startPlaybackAtTimeStamp:(MusicTimeStamp)timeStamp adjustForPreRollWhenRecording:(BOOL)adjustForPreRoll
{
MIDITimeStamp midiTimeStamp = MIKMIDIGetCurrentTimeStamp() + MIKMIDIClockMIDITimeStampsPerTimeInterval(0.001);
[self startPlaybackAtTimeStamp:timeStamp MIDITimeStamp:midiTimeStamp];
}

- (void)startPlaybackAtTimeStamp:(MusicTimeStamp)timeStamp MIDITimeStamp:(MIDITimeStamp)midiTimeStamp
{
[self startPlaybackAtTimeStamp:timeStamp MIDITimeStamp:midiTimeStamp adjustForPreRollWhenRecording:YES];
}

- (void)startPlaybackAtTimeStamp:(MusicTimeStamp)timeStamp MIDITimeStamp:(MIDITimeStamp)midiTimeStamp adjustForPreRollWhenRecording:(BOOL)adjustForPreRoll
{
if (self.isPlaying) [self stop];
if (adjustForPreRoll && self.isRecording) timeStamp -= self.preRoll;

NSString *queueLabel = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingFormat:@".%@.%p", [self class], self];
dispatch_queue_attr_t attr = DISPATCH_QUEUE_SERIAL;
Expand All @@ -172,10 +182,9 @@ - (void)startPlaybackAtTimeStamp:(MusicTimeStamp)timeStamp MIDITimeStamp:(MIDITi
self.processingQueue = queue;

dispatch_sync(queue, ^{
MusicTimeStamp startingTimeStamp = timeStamp + self.playbackOffset;
self.startingTimeStamp = startingTimeStamp;
self.startingTimeStamp = timeStamp;

Float64 startingTempo = [self.sequence tempoAtTimeStamp:startingTimeStamp];
Float64 startingTempo = [self.sequence tempoAtTimeStamp:timeStamp];
if (!startingTempo) startingTempo = kDefaultTempo;
[self updateClockWithMusicTimeStamp:timeStamp tempo:startingTempo atMIDITimeStamp:midiTimeStamp];
});
Expand Down Expand Up @@ -252,15 +261,14 @@ - (void)stopWithDispatchToProcessingQueue:(BOOL)dispatchToProcessingQueue
self.looping = NO;

MusicTimeStamp stopMusicTimeStamp = [clock musicTimeStampForMIDITimeStamp:stopTimeStamp];
_currentTimeStamp = (stopMusicTimeStamp <= self.sequenceLength + self.playbackOffset) ? stopMusicTimeStamp - self.playbackOffset : self.sequenceLength;
_currentTimeStamp = (stopMusicTimeStamp <= self.sequenceLength) ? stopMusicTimeStamp : self.sequenceLength;

[clock unsyncMusicTimeStampsAndTemposFromMIDITimeStamps];
};

dispatchToProcessingQueue ? dispatch_sync(self.processingQueue, stopPlayback) : stopPlayback();

self.processingQueue = NULL;
self.playbackOffset = 0;
self.playing = NO;
self.recording = NO;
}
Expand All @@ -272,9 +280,8 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
MIKMIDIClock *clock = self.clock;

MIKMIDISequence *sequence = self.sequence;
MusicTimeStamp playbackOffset = self.playbackOffset;
MusicTimeStamp loopStartTimeStamp = self.loopStartTimeStamp + playbackOffset;
MusicTimeStamp loopEndTimeStamp = self.effectiveLoopEndTimeStamp + playbackOffset;
MusicTimeStamp loopStartTimeStamp = self.loopStartTimeStamp;
MusicTimeStamp loopEndTimeStamp = self.effectiveLoopEndTimeStamp;
MusicTimeStamp fromMusicTimeStamp = [clock musicTimeStampForMIDITimeStamp:fromMIDITimeStamp];
MusicTimeStamp calculatedToMusicTimeStamp = [clock musicTimeStampForMIDITimeStamp:toMIDITimeStamp];
BOOL isLooping = (self.shouldLoop && calculatedToMusicTimeStamp > loopStartTimeStamp && loopEndTimeStamp > loopStartTimeStamp);
Expand All @@ -290,9 +297,9 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
Float64 overrideTempo = self.tempo;

if (!overrideTempo) {
NSArray *sequenceTempoEvents = [sequence.tempoTrack eventsOfClass:[MIKMIDITempoEvent class] fromTimeStamp:MAX(fromMusicTimeStamp - playbackOffset, 0) toTimeStamp:toMusicTimeStamp - playbackOffset];
NSArray *sequenceTempoEvents = [sequence.tempoTrack eventsOfClass:[MIKMIDITempoEvent class] fromTimeStamp:MAX(fromMusicTimeStamp, 0) toTimeStamp:toMusicTimeStamp];
for (MIKMIDITempoEvent *tempoEvent in sequenceTempoEvents) {
NSNumber *timeStampKey = @(tempoEvent.timeStamp + playbackOffset);
NSNumber *timeStampKey = @(tempoEvent.timeStamp);
allEventsByTimeStamp[timeStampKey] = [NSMutableArray arrayWithObject:tempoEvent];
tempoEventsByTimeStamp[timeStampKey] = tempoEvent;
}
Expand Down Expand Up @@ -326,7 +333,6 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
}

// Get other events

NSMutableArray *nonMutedTracks = [[NSMutableArray alloc] init];
NSMutableArray *soloTracks = [[NSMutableArray alloc] init];
for (MIKMIDITrack *track in sequence.tracks) {
Expand All @@ -340,8 +346,8 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
NSArray *tracksToPlay = soloTracks.count != 0 ? soloTracks : nonMutedTracks;

for (MIKMIDITrack *track in tracksToPlay) {
MusicTimeStamp startTimeStamp = MAX(fromMusicTimeStamp - playbackOffset - track.offset, 0);
MusicTimeStamp endTimeStamp = toMusicTimeStamp - playbackOffset - track.offset;
MusicTimeStamp startTimeStamp = MAX(fromMusicTimeStamp - track.offset, 0);
MusicTimeStamp endTimeStamp = toMusicTimeStamp - track.offset;
NSArray *events = [track eventsFromTimeStamp:startTimeStamp toTimeStamp:endTimeStamp];
if (track.offset != 0) {
// Shift events by offset
Expand All @@ -356,7 +362,7 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam

id<MIKMIDICommandScheduler> destination = events.count ? [self commandSchedulerForTrack:track] : nil; // only get the destination if there's events so we don't create a destination endpoint if not needed
for (MIKMIDIEvent *event in events) {
NSNumber *timeStampKey = @(event.timeStamp + playbackOffset);
NSNumber *timeStampKey = @(event.timeStamp);
NSMutableArray *eventsAtTimeStamp = allEventsByTimeStamp[timeStampKey] ? allEventsByTimeStamp[timeStampKey] : [NSMutableArray array];
[eventsAtTimeStamp addObject:[MIKMIDIEventWithDestination eventWithDestination:destination event:event]];
allEventsByTimeStamp[timeStampKey] = eventsAtTimeStamp;
Expand All @@ -365,7 +371,7 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam

// Get click track events
for (MIKMIDIEventWithDestination *destinationEvent in [self clickTrackEventsFromTimeStamp:fromMusicTimeStamp toTimeStamp:toMusicTimeStamp]) {
NSNumber *timeStampKey = @(destinationEvent.event.timeStamp + playbackOffset);
NSNumber *timeStampKey = @(destinationEvent.event.timeStamp);
NSMutableArray *eventsAtTimesStamp = allEventsByTimeStamp[timeStampKey] ? allEventsByTimeStamp[timeStampKey] : [NSMutableArray array];
[eventsAtTimesStamp addObject:destinationEvent];
allEventsByTimeStamp[timeStampKey] = eventsAtTimesStamp;
Expand Down Expand Up @@ -413,7 +419,7 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
}
} else if (!self.isRecording) { // Don't stop automatically during recording
MIDITimeStamp systemTimeStamp = MIKMIDIGetCurrentTimeStamp();
if ((systemTimeStamp > actualToMIDITimeStamp) && ([clock musicTimeStampForMIDITimeStamp:systemTimeStamp] >= self.sequenceLength + playbackOffset)) {
if ((systemTimeStamp > actualToMIDITimeStamp) && ([clock musicTimeStampForMIDITimeStamp:systemTimeStamp] >= self.sequenceLength)) {
[self stopWithDispatchToProcessingQueue:NO];
}
}
Expand Down Expand Up @@ -447,12 +453,7 @@ - (void)scheduleEventWithDestination:(MIKMIDIEventWithDestination *)destinationE
command = [MIKMIDICommand commandFromChannelEvent:(MIKMIDIChannelEvent *)event clock:clock];
}

// Adjust command time stamp to account for our playback offset.
if (command) {
MIKMutableMIDICommand *adjustedCommand = [command mutableCopy];
adjustedCommand.midiTimestamp += [clock midiTimeStampsPerMusicTimeStamp:self.playbackOffset];
[self scheduleCommands:@[adjustedCommand] withCommandScheduler:destination];
}
if (command) [self scheduleCommands:@[command] withCommandScheduler:destination];
}

- (void)sendAllPendingNoteOffsWithMIDITimeStamp:(MIDITimeStamp)offTimeStamp
Expand Down Expand Up @@ -528,7 +529,6 @@ - (void)resumeRecording
- (void)prepareForRecordingWithPreRoll:(BOOL)includePreRoll
{
self.pendingRecordedNoteEvents = [NSMutableDictionary dictionary];
if (includePreRoll) self.playbackOffset = self.preRoll;
self.recording = YES;
}

Expand All @@ -537,9 +537,7 @@ - (void)recordMIDICommand:(MIKMIDICommand *)command
if (!self.isRecording) return;

MIDITimeStamp midiTimeStamp = command.midiTimestamp;

MusicTimeStamp playbackOffset = self.playbackOffset;
MusicTimeStamp musicTimeStamp = [self.clock musicTimeStampForMIDITimeStamp:midiTimeStamp] - playbackOffset;
MusicTimeStamp musicTimeStamp = [self.clock musicTimeStampForMIDITimeStamp:midiTimeStamp];

MIKMIDIEvent *event;
if ([command isKindOfClass:[MIKMIDINoteOnCommand class]]) { // note On
Expand Down Expand Up @@ -648,18 +646,17 @@ - (NSMutableArray *)clickTrackEventsFromTimeStamp:(MusicTimeStamp)fromTimeStamp
MIDINoteMessage tockMessage = metronome.tockMessage;

MIKMIDISequence *sequence = self.sequence;
MusicTimeStamp playbackOffset = self.playbackOffset;
MIKMIDITimeSignature timeSignature = [sequence timeSignatureAtTimeStamp:fromTimeStamp - playbackOffset];
MIKMIDITimeSignature timeSignature = [sequence timeSignatureAtTimeStamp:MAX(fromTimeStamp, 0)];
NSMutableArray *timeSignatureEvents = [[sequence.tempoTrack eventsOfClass:[MIKMIDIMetaTimeSignatureEvent class]
fromTimeStamp:MAX(fromTimeStamp - playbackOffset, 0)
toTimeStamp:toTimeStamp] mutableCopy];
fromTimeStamp:MAX(fromTimeStamp, 0)
toTimeStamp:MAX(toTimeStamp, 0)] mutableCopy];

MusicTimeStamp clickTimeStamp = floor(fromTimeStamp);
while (clickTimeStamp <= toTimeStamp) {
if (clickTrackStatus == MIKMIDISequencerClickTrackStatusEnabledOnlyInPreRoll && clickTimeStamp >= self.startingTimeStamp) break;
if (clickTrackStatus == MIKMIDISequencerClickTrackStatusEnabledOnlyInPreRoll && clickTimeStamp >= self.startingTimeStamp + self.preRoll) break;

MIKMIDIMetaTimeSignatureEvent *event = [timeSignatureEvents firstObject];
if (event && event.timeStamp - playbackOffset <= clickTimeStamp) {
if (event && event.timeStamp <= clickTimeStamp) {
timeSignature = (MIKMIDITimeSignature) { .numerator = event.numerator, .denominator = event.denominator };
[timeSignatureEvents removeObjectAtIndex:0];
}
Expand All @@ -668,7 +665,7 @@ - (NSMutableArray *)clickTrackEventsFromTimeStamp:(MusicTimeStamp)fromTimeStamp
NSInteger adjustedTimeStamp = clickTimeStamp * timeSignature.denominator / 4.0;
BOOL isTick = !((adjustedTimeStamp + timeSignature.numerator) % (timeSignature.numerator));
MIDINoteMessage clickMessage = isTick ? tickMessage : tockMessage;
MIKMIDINoteEvent *noteEvent = [MIKMIDINoteEvent noteEventWithTimeStamp:clickTimeStamp - playbackOffset message:clickMessage];
MIKMIDINoteEvent *noteEvent = [MIKMIDINoteEvent noteEventWithTimeStamp:clickTimeStamp message:clickMessage];
[clickEvents addObject:[MIKMIDIEventWithDestination eventWithDestination:metronome event:noteEvent]];
}

Expand Down Expand Up @@ -737,8 +734,7 @@ - (MusicTimeStamp)currentTimeStamp
MIKMIDIClock *clock = self.clock;
if (clock.isReady) {
MusicTimeStamp timeStamp = [clock musicTimeStampForMIDITimeStamp:MIKMIDIGetCurrentTimeStamp()];
MusicTimeStamp playbackOffset = self.playbackOffset;
_currentTimeStamp = MAX(((timeStamp <= self.sequenceLength + playbackOffset) ? timeStamp - playbackOffset : self.sequenceLength), self.startingTimeStamp);
_currentTimeStamp = MAX(((timeStamp <= self.sequenceLength) ? timeStamp : self.sequenceLength), self.startingTimeStamp);
}
return _currentTimeStamp;
}
Expand All @@ -749,7 +745,7 @@ - (void)setCurrentTimeStamp:(MusicTimeStamp)currentTimeStamp
BOOL isRecording = self.isRecording;
[self stop];
if (isRecording) [self prepareForRecordingWithPreRoll:NO];
[self startPlaybackAtTimeStamp:currentTimeStamp];
[self startPlaybackAtTimeStamp:currentTimeStamp adjustForPreRollWhenRecording:NO];
} else {
_currentTimeStamp = currentTimeStamp;
}
Expand Down

0 comments on commit 40a016e

Please sign in to comment.