Skip to content

Commit

Permalink
Merge pull request #138 from mixedinkey-opensource/Issue99
Browse files Browse the repository at this point in the history
Adds support to MIKMIDISequencer for muted, solo, and offset properties of MIKMIDITrack.
  • Loading branch information
armadsen committed Mar 8, 2016
2 parents 0e287cc + 836bba8 commit 667cbab
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Source/MIKMIDINoteEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) Float32 duration;

/**
* The time stamp at the end of the notes duration.
* The time stamp at the end of the notes duration. This is simply the event's timeStamp + duration.
*/
@property (nonatomic, readonly) MusicTimeStamp endTimeStamp;

Expand Down
28 changes: 27 additions & 1 deletion Source/MIKMIDISequencer.m
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,34 @@ - (void)processSequenceStartingFromMIDITimeStamp:(MIDITimeStamp)fromMIDITimeStam
}

// Get other events

NSMutableArray *nonMutedTracks = [[NSMutableArray alloc] init];
NSMutableArray *soloTracks = [[NSMutableArray alloc] init];
for (MIKMIDITrack *track in sequence.tracks) {
NSArray *events = [track eventsFromTimeStamp:MAX(fromMusicTimeStamp - playbackOffset, 0) toTimeStamp:toMusicTimeStamp - playbackOffset];
if (track.isMuted) continue;

[nonMutedTracks addObject:track];
if (track.solo) { [soloTracks addObject:track]; }
}

// Never play muted tracks. If any non-muted tracks are soloed, only play those. Matches MusicPlayer behavior
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;
NSArray *events = [track eventsFromTimeStamp:startTimeStamp toTimeStamp:endTimeStamp];
if (track.offset != 0) {
// Shift events by offset
NSMutableArray *shiftedEvents = [NSMutableArray array];
for (MIKMIDIEvent *event in events) {
MIKMutableMIDIEvent *shiftedEvent = [event mutableCopy];
shiftedEvent.timeStamp += track.offset;
[shiftedEvents addObject:shiftedEvent];
}
events = shiftedEvents;
}

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);
Expand Down
18 changes: 8 additions & 10 deletions Source/MIKMIDITrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,30 +185,28 @@ NS_ASSUME_NONNULL_BEGIN
/**
* A MIDI track’s start time in terms of beat number. By default this value is 0.
*
* This property can be observed using Key Value Observing.
* This can be used to offset playback by MIKMIDISequencer of individual tracks in a sequence.
*
* @note This property is not yet used by MIKMIDISequencer (Issue #99). If this property
* is required for your playback situation, you should stick with MIKMIDIPlayer in the meantime.
* This property can be observed using Key Value Observing.
*/
@property (nonatomic) MusicTimeStamp offset;

/**
* Whether or not the MIDI track is muted.
* Whether or not the MIDI track is muted. Muted tracks are not played by MIKMIDISequencer.
*
* This property can be observed using Key Value Observing.
*
* @note This property is not yet used by MIKMIDISequencer (Issue #99). If this property
* is required for your playback situation, you should stick with MIKMIDIPlayer in the meantime.
*/
@property (nonatomic, getter = isMuted) BOOL muted;

/**
* Whether or not the MIDI track is soloed.
*
* This property can be observed using Key Value Observing.
* If a track is muted, it is not played by MIKMIDISequencer, and its solo status is ignored.
* If any non-muted tracks in a sequence have this property set to YES, MIKMIDISequencer will only play those,
* and will not play the non-solo tracks. If this is set to NO for all non-muted tracks, then
* all non-muted tracks will be played.
*
* @note This property is not yet used by MIKMIDISequencer (Issue #99). If this property
* is required for your playback situation, you should stick with MIKMIDIPlayer in the meantime.
* This property can be observed using Key Value Observing.
*/
@property (nonatomic, getter = isSolo) BOOL solo;

Expand Down
82 changes: 59 additions & 23 deletions Source/MIKMIDITrack.m
Original file line number Diff line number Diff line change
Expand Up @@ -616,51 +616,87 @@ - (NSInteger)trackNumber
return (NSInteger)trackNumber;
}

@synthesize offset = _offset;

- (MusicTimeStamp)offset
{
MusicTimeStamp offset = 0;
UInt32 offsetLength = sizeof(offset);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_OffsetTime, &offset, &offsetLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return offset;
if (_offset != 0) return _offset;

if (self.musicTrack) {
MusicTimeStamp offset = 0;
UInt32 offsetLength = sizeof(offset);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_OffsetTime, &offset, &offsetLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return offset;
} else {
return 0;
}
}

- (void)setOffset:(MusicTimeStamp)offset
{
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_OffsetTime, &offset, sizeof(offset));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
_offset = offset;

if (self.musicTrack) {
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_OffsetTime, &offset, sizeof(offset));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
}
}

@synthesize muted = _muted;

- (BOOL)isMuted
{
Boolean isMuted = FALSE;
UInt32 isMutedLength = sizeof(isMuted);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_MuteStatus, &isMuted, &isMutedLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return isMuted ? YES : NO;
if (_muted) return YES;

if (self.musicTrack) {
Boolean isMuted = FALSE;
UInt32 isMutedLength = sizeof(isMuted);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_MuteStatus, &isMuted, &isMutedLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return isMuted ? YES : NO;
} else {
return NO;
}
}

- (void)setMuted:(BOOL)muted
{
Boolean mutedBoolean = muted ? TRUE : FALSE;
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_MuteStatus, &mutedBoolean, sizeof(mutedBoolean));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
_muted = muted;

if (self.musicTrack) {
Boolean mutedBoolean = muted ? TRUE : FALSE;
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_MuteStatus, &mutedBoolean, sizeof(mutedBoolean));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
}
}

@synthesize solo = _solo;

- (BOOL)isSolo
{
Boolean isSolo = FALSE;
UInt32 isSoloLength = sizeof(isSolo);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_SoloStatus, &isSolo, &isSoloLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return isSolo ? YES : NO;
if (_solo) return YES;

if (self.musicTrack) {
Boolean isSolo = FALSE;
UInt32 isSoloLength = sizeof(isSolo);
OSStatus err = MusicTrackGetProperty(self.musicTrack, kSequenceTrackProperty_SoloStatus, &isSolo, &isSoloLength);
if (err) NSLog(@"MusicTrackGetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
return isSolo ? YES : NO;
} else {
return NO;
}
}

- (void)setSolo:(BOOL)solo
{
Boolean soloBoolean = solo ? TRUE : FALSE;
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_SoloStatus, &soloBoolean, sizeof(soloBoolean));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
_solo = solo;

if (self.musicTrack) {
Boolean soloBoolean = solo ? TRUE : FALSE;
OSStatus err = MusicTrackSetProperty(self.musicTrack, kSequenceTrackProperty_SoloStatus, &soloBoolean, sizeof(soloBoolean));
if (err) NSLog(@"MusicTrackSetProperty() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__);
}
}

+ (NSSet *)keyPathsForValuesAffectingLength
Expand Down

0 comments on commit 667cbab

Please sign in to comment.