diff --git a/Source/MIKMIDIDeviceManager.m b/Source/MIKMIDIDeviceManager.m index 4acde537..41ac3298 100644 --- a/Source/MIKMIDIDeviceManager.m +++ b/Source/MIKMIDIDeviceManager.m @@ -381,11 +381,10 @@ - (NSArray *)virtualSources { return [self.internalVirtualSources copy]; } - (void)addInternalVirtualSourcesObject:(MIKMIDISourceEndpoint *)source { - NSUInteger index = [self.internalVirtualSources indexOfObject:source]; - if (index == NSNotFound) return; - [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualSources"]; - [self.internalVirtualSources removeObjectAtIndex:index]; - [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualSources"]; + NSUInteger index = [self.internalVirtualSources count]; + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualSources"]; + [self.internalVirtualSources insertObject:source atIndex:index]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualSources"]; } - (void)removeInternalVirtualSourcesObject:(MIKMIDISourceEndpoint *)source @@ -403,11 +402,10 @@ - (NSArray *)virtualDestinations { return [self.internalVirtualDestinations copy - (void)addInternalVirtualDestinationsObject:(MIKMIDIDestinationEndpoint *)destination { - NSUInteger index = [self.internalVirtualDestinations indexOfObject:destination]; - if (index == NSNotFound) return; - [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualDestinations"]; - [self.internalVirtualDestinations removeObjectAtIndex:index]; - [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualDestinations"]; + NSUInteger index = [self.internalVirtualDestinations count]; + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualDestinations"]; + [self.internalVirtualDestinations insertObject:destination atIndex:index]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"virtualDestinations"]; } - (void)removeInternalVirtualDestinationsObject:(MIKMIDIDestinationEndpoint *)destination diff --git a/Source/MIKMIDISequence.m b/Source/MIKMIDISequence.m index 70ad0ce5..26578e41 100644 --- a/Source/MIKMIDISequence.m +++ b/Source/MIKMIDISequence.m @@ -44,19 +44,19 @@ @implementation MIKMIDISequence + (instancetype)sequence { - return [[self alloc] init]; + return [[self alloc] init]; } - (instancetype)init { - MusicSequence sequence; - OSStatus err = NewMusicSequence(&sequence); - if (err) { - NSLog(@"NewMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); - return nil; - } - - return [self initWithMusicSequence:sequence error:NULL]; + MusicSequence sequence; + OSStatus err = NewMusicSequence(&sequence); + if (err) { + NSLog(@"NewMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + return nil; + } + + return [self initWithMusicSequence:sequence error:NULL]; } + (instancetype)sequenceWithFileAtURL:(NSURL *)fileURL error:(NSError **)error; @@ -76,7 +76,8 @@ - (instancetype)initWithFileAtURL:(NSURL *)fileURL error:(NSError **)error; - (instancetype)initWithFileAtURL:(NSURL *)fileURL convertMIDIChannelsToTracks:(BOOL)convertMIDIChannelsToTracks error:(NSError **)error { - NSData *data = [NSData dataWithContentsOfURL:fileURL options:0 error:error]; + NSData *data = [NSData dataWithContentsOfURL:fileURL options:0 error:error]; + if (!data) return nil; return [self initWithData:data convertMIDIChannelsToTracks:convertMIDIChannelsToTracks error:error]; } @@ -97,24 +98,24 @@ - (instancetype)initWithData:(NSData *)data error:(NSError **)error - (instancetype)initWithData:(NSData *)data convertMIDIChannelsToTracks:(BOOL)convertMIDIChannelsToTracks error:(NSError **)error { - error = error ? error : &(NSError *__autoreleasing){ nil }; + error = error ?: &(NSError *__autoreleasing){ nil }; - MusicSequence sequence; - OSStatus err = NewMusicSequence(&sequence); - if (err) { - NSLog(@"NewMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + MusicSequence sequence; + OSStatus err = NewMusicSequence(&sequence); + if (err) { + NSLog(@"NewMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; - return nil; - } - + return nil; + } + MusicSequenceLoadFlags flags = convertMIDIChannelsToTracks ? kMusicSequenceLoadSMF_ChannelsToTracks : 0; err = MusicSequenceFileLoadData(sequence, (__bridge CFDataRef)data, kMusicSequenceFile_MIDIType, flags); - if (err) { - NSLog(@"MusicSequenceFileLoadData() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + if (err) { + NSLog(@"MusicSequenceFileLoadData() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; - return nil; - } - + return nil; + } + return [self initWithMusicSequence:sequence error:error]; } @@ -176,14 +177,14 @@ - (void)dealloc NSArray *tracks = self.internalTracks; self.internalTracks = nil; // Unregister for KVO [self setCallBackBlock:^(MIKMIDITrack *t, MusicTimeStamp ts, const MusicEventUserData *ud, MusicTimeStamp ts2, MusicTimeStamp ts3) {}]; - + for (MIKMIDITrack *track in tracks) { OSStatus err = MusicSequenceDisposeTrack(_musicSequence, track.musicTrack); if (err) NSLog(@"MusicSequenceDisposeTrack() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); } - - OSStatus err = DisposeMusicSequence(_musicSequence); - if (err) NSLog(@"DisposeMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + + OSStatus err = DisposeMusicSequence(_musicSequence); + if (err) NSLog(@"DisposeMusicSequence() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); } #pragma mark - Sequencer Synchronization @@ -191,7 +192,7 @@ - (void)dealloc - (void)dispatchSyncToSequencerProcessingQueueAsNeeded:(void (^)())block { if (!block) return; - + MIKMIDISequencer *sequencer = self.sequencer; if (sequencer) { [sequencer dispatchSyncToProcessingQueueAsNeeded:block]; @@ -206,7 +207,7 @@ - (MIKMIDITrack *)addTrackWithError:(NSError **)error { __block MIKMIDITrack *track = nil; error = error ?: &(NSError *__autoreleasing){ nil }; - + [self dispatchSyncToSequencerProcessingQueueAsNeeded:^{ MusicTrack musicTrack; OSStatus err = MusicSequenceNewTrack(self.musicSequence, &musicTrack); @@ -217,11 +218,11 @@ - (MIKMIDITrack *)addTrackWithError:(NSError **)error *error = [NSError MIKMIDIErrorWithCode:MIKMIDISequenceAddTrackFailedErrorCode userInfo:userInfo]; return; }; - + track = [MIKMIDITrack trackWithSequence:self musicTrack:musicTrack]; [self insertObject:track inInternalTracksAtIndex:[self.internalTracks count]]; }]; - + return track; } @@ -233,67 +234,67 @@ - (MIKMIDITrack *)addTrack - (BOOL)removeTrack:(MIKMIDITrack *)track { if (!track) return NO; - + __block BOOL success = NO; - + [self dispatchSyncToSequencerProcessingQueueAsNeeded:^{ OSStatus err = MusicSequenceDisposeTrack(self.musicSequence, track.musicTrack); if (err) return NSLog(@"MusicSequenceDisposeTrack() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); - + NSInteger index = [self.internalTracks indexOfObject:track]; if (index != NSNotFound) [self removeObjectFromInternalTracksAtIndex:index]; success = YES; }]; - - return success; + + return success; } #pragma mark - File Saving - (BOOL)writeToURL:(NSURL *)fileURL error:(NSError *__autoreleasing *)error { - return [self.dataValue writeToURL:fileURL options:NSDataWritingAtomic error:error]; + return [self.dataValue writeToURL:fileURL options:NSDataWritingAtomic error:error]; } #pragma mark - Callback static void MIKSequenceCallback(void *inClientData, MusicSequence inSequence, MusicTrack inTrack, MusicTimeStamp inEventTime, const MusicEventUserData *inEventData, MusicTimeStamp inStartSliceBeat, MusicTimeStamp inEndSliceBeat) { - MIKMIDISequence *self = (__bridge MIKMIDISequence *)inClientData; - if (!self.callBackBlock) return; - - UInt32 trackIndex; - OSStatus err = MusicSequenceGetTrackIndex(inSequence, inTrack, &trackIndex); - if (err) { - NSLog(@"MusicSequenceGetTrackIndex() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); - return; - } - - MIKMIDITrack *track = self.tracks[trackIndex]; - if (track && self.callBackBlock) { - self.callBackBlock(track, inEventTime, inEventData, inStartSliceBeat, inEndSliceBeat); - } + MIKMIDISequence *self = (__bridge MIKMIDISequence *)inClientData; + if (!self.callBackBlock) return; + + UInt32 trackIndex; + OSStatus err = MusicSequenceGetTrackIndex(inSequence, inTrack, &trackIndex); + if (err) { + NSLog(@"MusicSequenceGetTrackIndex() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + return; + } + + MIKMIDITrack *track = self.tracks[trackIndex]; + if (track && self.callBackBlock) { + self.callBackBlock(track, inEventTime, inEventData, inStartSliceBeat, inEndSliceBeat); + } } #pragma mark - Looping - (MusicTimeStamp)equivalentTimeStampForLoopedTimeStamp:(MusicTimeStamp)loopedTimeStamp { - MusicTimeStamp length = self.length; - - if (loopedTimeStamp > length) { - NSInteger numTimesLooped = (NSInteger)(loopedTimeStamp / length); - loopedTimeStamp -= (length * numTimesLooped); - } - - return loopedTimeStamp; + MusicTimeStamp length = self.length; + + if (loopedTimeStamp > length) { + NSInteger numTimesLooped = (NSInteger)(loopedTimeStamp / length); + loopedTimeStamp -= (length * numTimesLooped); + } + + return loopedTimeStamp; } #pragma mark - Tempo - (NSArray *)tempoEvents { - return [self.tempoTrack eventsOfClass:[MIKMIDITempoEvent class] fromTimeStamp:0 toTimeStamp:kMusicTimeStamp_EndOfTrack]; + return [self.tempoTrack eventsOfClass:[MIKMIDITempoEvent class] fromTimeStamp:0 toTimeStamp:kMusicTimeStamp_EndOfTrack]; } - (BOOL)setOverallTempo:(Float64)bpm @@ -302,7 +303,7 @@ - (BOOL)setOverallTempo:(Float64)bpm [self.tempoTrack removeAllEvents]; if ([self.tempoTrack.events count]) return NO; [self.tempoTrack addEvents:timeSignatureEvents]; - return [self setTempo:bpm atTimeStamp:0]; + return [self setTempo:bpm atTimeStamp:0]; } - (BOOL)setTempo:(Float64)bpm atTimeStamp:(MusicTimeStamp)timeStamp @@ -313,13 +314,13 @@ - (BOOL)setTempo:(Float64)bpm atTimeStamp:(MusicTimeStamp)timeStamp - (Float64)tempoAtTimeStamp:(MusicTimeStamp)timeStamp { - __block Float64 tempo = 0; - + __block Float64 tempo = 0; + [self dispatchSyncToSequencerProcessingQueueAsNeeded:^{ NSArray *events = [self.tempoTrack eventsOfClass:[MIKMIDITempoEvent class] fromTimeStamp:0 toTimeStamp:timeStamp]; tempo = [[events lastObject] bpm]; }]; - + return tempo; } @@ -359,7 +360,7 @@ - (MIKMIDITimeSignature)timeSignatureAtTimeStamp:(MusicTimeStamp)timeStamp if (event) { result.numerator = event.numerator; result.denominator = event.denominator; - } +} return result; } @@ -367,7 +368,7 @@ - (MIKMIDITimeSignature)timeSignatureAtTimeStamp:(MusicTimeStamp)timeStamp - (NSString *)description { - return [NSString stringWithFormat:@"%@ tempo track: %@ tracks: %@", [super description], self.tempoTrack, self.tracks]; + return [NSString stringWithFormat:@"%@ tempo track: %@ tracks: %@", [super description], self.tempoTrack, self.tracks]; } #pragma mark - KVO @@ -378,7 +379,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; return; } - + if ([self.internalTracks containsObject:object] && ([keyPath isEqualToString:@"length"] || [keyPath isEqualToString:@"offset"])) { [self updateLengthDefinedByTracks]; @@ -387,12 +388,12 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N - (void)updateLengthDefinedByTracks { - MusicTimeStamp length = 0; - for (MIKMIDITrack *track in self.tracks) { - MusicTimeStamp trackLength = track.length + track.offset; - if (trackLength > length) length = trackLength; - } - + MusicTimeStamp length = 0; + for (MIKMIDITrack *track in self.tracks) { + MusicTimeStamp trackLength = track.length + track.offset; + if (trackLength > length) length = trackLength; + } + self.lengthDefinedByTracks = length; } @@ -443,11 +444,11 @@ + (BOOL)automaticallyNotifiesObserversOfTracks { return NO; } - (NSArray *)tracks { __block NSArray *tracks; - + [self dispatchSyncToSequencerProcessingQueueAsNeeded:^{ tracks = self.internalTracks; }]; - + return tracks ?: @[]; } @@ -460,11 +461,11 @@ + (NSSet *)keyPathsForValuesAffectingLength - (MusicTimeStamp)length { __block MusicTimeStamp length = 0; - + [self dispatchSyncToSequencerProcessingQueueAsNeeded:^{ length = (_length == MIKMIDISequenceLongestTrackLength) ? self.lengthDefinedByTracks : _length; }]; - + return length; } @@ -482,22 +483,22 @@ + (NSSet *)keyPathsForValuesAffectingDurationInSeconds - (Float64)durationInSeconds { - Float64 duration = 0; - OSStatus err = MusicSequenceGetSecondsForBeats(self.musicSequence, self.length, &duration); - if (err) NSLog(@"MusicSequenceGetSecondsForBeats() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); - return duration; + Float64 duration = 0; + OSStatus err = MusicSequenceGetSecondsForBeats(self.musicSequence, self.length, &duration); + if (err) NSLog(@"MusicSequenceGetSecondsForBeats() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + return duration; } - (NSData *)dataValue { - CFDataRef data; - OSStatus err = MusicSequenceFileCreateData(self.musicSequence, kMusicSequenceFile_MIDIType, 0, 0, &data); - if (err) { - NSLog(@"MusicSequenceFileCreateData() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); - return nil; - } - - return (__bridge_transfer NSData *)data; + CFDataRef data; + OSStatus err = MusicSequenceFileCreateData(self.musicSequence, kMusicSequenceFile_MIDIType, 0, 0, &data); + if (err) { + NSLog(@"MusicSequenceFileCreateData() failed with error %@ in %s.", @(err), __PRETTY_FUNCTION__); + return nil; + } + + return (__bridge_transfer NSData *)data; } - (MIKMIDISequencer *)sequencer { return _sequencer; } @@ -523,9 +524,9 @@ - (instancetype)initWithData:(NSData *)data - (void)setDestinationEndpoint:(MIKMIDIDestinationEndpoint *)destinationEndpoint { NSLog(@"%s is deprecated. You should update your code to avoid calling this method. Use MIKMIDISequencer's API instead.", __PRETTY_FUNCTION__); - for (MIKMIDITrack *track in self.tracks) { - track.destinationEndpoint = destinationEndpoint; - } + for (MIKMIDITrack *track in self.tracks) { + track.destinationEndpoint = destinationEndpoint; + } } - (BOOL)getTempo:(Float64 *)bpm atTimeStamp:(MusicTimeStamp)timeStamp