From 794f94bd731819182cad38bcd092d3ec61395b97 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Mon, 15 Aug 2016 13:40:11 +0300 Subject: [PATCH 01/15] Related issue https://github.com/tumtumtum/StreamingKit/issues/298 MP3 files do not have magic cookies https://www.google.ch/patents/US20090019087 so an attempt to retrieve magic cookie from mp3 file causes method to return before the converter was created. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 46affb4f..5a3b78ac 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2028,7 +2028,11 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd audioConverterAudioStreamBasicDescription = *asbd; - if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileAAC_ADTSType) + // Should probably be (self->currentlyReadingEntry.dataSource.audioFileTypeHint == kAudioFileAAC_ADTSType). + // Related issue https://github.com/tumtumtum/StreamingKit/issues/298 + // MP3 files do not have magic cookies https://www.google.ch/patents/US20090019087 + + if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileAC3Type) { status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); From 8dab69fcd52f68b7917ccb9b7cc3962b7476822d Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Mon, 15 Aug 2016 17:49:36 +0300 Subject: [PATCH 02/15] Type typo fix. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 5a3b78ac..4d1f7f74 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2032,7 +2032,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd // Related issue https://github.com/tumtumtum/StreamingKit/issues/298 // MP3 files do not have magic cookies https://www.google.ch/patents/US20090019087 - if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileAC3Type) + if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileMP3Type) { status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); From 0856c1d6c06097c67ba8dca1924913b4eaf81fa6 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Tue, 16 Aug 2016 11:52:17 +0300 Subject: [PATCH 03/15] Handling kAudioFileInvalidPacketOffsetError: stop file recording. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 4d1f7f74..e0d1d6bd 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2805,6 +2805,21 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio if (writeError) { NSLog(@"STKAudioPlayer:handleRecordingOfAudioPackets failed on AudioFileWritePackets with error \"" OSSTATUS_PRINTF_PLACEHOLDER "\"", OSSTATUS_PRINTF_VALUE(writeError)); + + /** + * kAudioFileInvalidPacketOffsetError + * + * @description + * + * A packet offset was past the end of the file, or not at the end of the file when a VBR format was written, + * or a corrupt packet size was read when the packet table was built. + */ + + if (writeError == 1885563711) { //kAudioFileInvalidPacketOffsetError (happens sometimes, probably on VBR) + + [self closeRecordAudioFile]; + break; + } } else { From e510e8ca236ab267256e0f1caff320188c7c1e19 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 17 Aug 2016 16:50:00 +0300 Subject: [PATCH 04/15] Added processedBytesSizeTotal property to dataSource so that we could find out if the track if fully recorded. --- StreamingKit/StreamingKit/STKAudioPlayer.h | 3 ++- StreamingKit/StreamingKit/STKAudioPlayer.m | 5 ++++- StreamingKit/StreamingKit/STKQueueEntry.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index a1ff3b53..3fdd6115 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -122,7 +122,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId; /// Raised when an item has finished buffering (may or may not be the currently playing item) /// This event may be raised multiple times for the same item if seek is invoked on the player --(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId; +/// If recording stream to a file only fully proccesed source represents correctly written file without skips +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId fullyProcessed:(BOOL)fullyProcessed; /// Raised when the state of the player has changed -(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState; /// Raised when an item has finished playing diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e0d1d6bd..24f03374 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1642,7 +1642,8 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn [self dispatchSyncOnMainThread:^ { - [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; + BOOL fullyProcessed = currentlyReadingEntry->audioDataByteCount == currentlyReadingEntry->processedBytesSizeTotal; + [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId fullyProcessed:fullyProcessed]; }]; pthread_mutex_lock(&playerMutex); @@ -2539,6 +2540,8 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte convertInfo.audioBuffer.mData = (void *)inputData; convertInfo.audioBuffer.mDataByteSize = numberBytes; convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; + + OSAtomicAdd32((int32_t)numberBytes, ¤tlyReadingEntry->processedBytesSizeTotal); if (packetDescriptionsIn && currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) { diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index f13d2772..fd61b51c 100755 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -29,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN volatile SInt64 lastFrameQueued; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; + volatile int processedBytesSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; double durationHint; } From 95d7717acd7c1e0eaf9baa6375973970b603b0f7 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 17 Aug 2016 19:37:08 +0300 Subject: [PATCH 05/15] Check total file length instead of audioData length, as ID3 tags might be missing. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 4 +--- StreamingKit/StreamingKit/STKDataSource.h | 1 + StreamingKit/StreamingKit/STKDataSource.m | 5 +++++ StreamingKit/StreamingKit/STKHTTPDataSource.m | 7 +++++++ StreamingKit/StreamingKit/STKQueueEntry.h | 1 - 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 24f03374..492466d2 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1642,7 +1642,7 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn [self dispatchSyncOnMainThread:^ { - BOOL fullyProcessed = currentlyReadingEntry->audioDataByteCount == currentlyReadingEntry->processedBytesSizeTotal; + BOOL fullyProcessed = currentlyReadingEntry.dataSource.length == [currentlyReadingEntry.dataSource bytesRead]; [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId fullyProcessed:fullyProcessed]; }]; @@ -2540,8 +2540,6 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte convertInfo.audioBuffer.mData = (void *)inputData; convertInfo.audioBuffer.mDataByteSize = numberBytes; convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; - - OSAtomicAdd32((int32_t)numberBytes, ¤tlyReadingEntry->processedBytesSizeTotal); if (packetDescriptionsIn && currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) { diff --git a/StreamingKit/StreamingKit/STKDataSource.h b/StreamingKit/StreamingKit/STKDataSource.h index 0a1e7168..0c722de2 100755 --- a/StreamingKit/StreamingKit/STKDataSource.h +++ b/StreamingKit/StreamingKit/STKDataSource.h @@ -62,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN -(void) seekToOffset:(SInt64)offset; -(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size; -(AudioFileTypeID) audioFileTypeHint; +-(SInt64) bytesRead; @end diff --git a/StreamingKit/StreamingKit/STKDataSource.m b/StreamingKit/StreamingKit/STKDataSource.m index f9c57a23..d579c53d 100644 --- a/StreamingKit/StreamingKit/STKDataSource.m +++ b/StreamingKit/StreamingKit/STKDataSource.m @@ -84,4 +84,9 @@ -(BOOL) supportsSeek return YES; } +-(SInt64) bytesRead +{ + return 0; +} + @end diff --git a/StreamingKit/StreamingKit/STKHTTPDataSource.m b/StreamingKit/StreamingKit/STKHTTPDataSource.m index 7af86867..eb475d00 100755 --- a/StreamingKit/StreamingKit/STKHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.m @@ -43,6 +43,7 @@ @interface STKHTTPDataSource() SInt64 seekStart; SInt64 relativePosition; SInt64 fileLength; + SInt64 bytesRead; int discontinuous; int requestSerialNumber; int prefixBytesRead; @@ -474,10 +475,16 @@ -(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size } relativePosition += read; + bytesRead += read; return read; } +-(SInt64) bytesRead +{ + return bytesRead; +} + -(void) open { return [self openForSeek:NO]; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index fd61b51c..f13d2772 100755 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -29,7 +29,6 @@ NS_ASSUME_NONNULL_BEGIN volatile SInt64 lastFrameQueued; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; - volatile int processedBytesSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; double durationHint; } From 5176d6a525387bc1fe7f386776edb2920a35794b Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 17 Aug 2016 20:01:42 +0300 Subject: [PATCH 06/15] added bytesRead method to AutoRecovering dataSource --- StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m | 5 +++++ StreamingKit/StreamingKit/STKDataSource.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index 8dfe3a8b..8472f6fd 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -385,6 +385,11 @@ -(void) dataSourceErrorOccured:(STKDataSource*)dataSource } } +-(SInt64) bytesRead +{ + return self.innerDataSource.bytesRead; +} + -(NSString*) description { return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position]; diff --git a/StreamingKit/StreamingKit/STKDataSource.h b/StreamingKit/StreamingKit/STKDataSource.h index 0c722de2..2c5166d3 100755 --- a/StreamingKit/StreamingKit/STKDataSource.h +++ b/StreamingKit/StreamingKit/STKDataSource.h @@ -50,6 +50,7 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) BOOL supportsSeek; @property (readonly) SInt64 position; @property (readonly) SInt64 length; +@property (readonly) SInt64 bytesRead; @property (readonly) BOOL hasBytesAvailable; @property (nonatomic, readwrite, assign) double durationHint; @property (readwrite, unsafe_unretained, nullable) id delegate; @@ -62,7 +63,6 @@ NS_ASSUME_NONNULL_BEGIN -(void) seekToOffset:(SInt64)offset; -(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size; -(AudioFileTypeID) audioFileTypeHint; --(SInt64) bytesRead; @end From 35006bf21ccda0efb6510051631a51f9e090c932 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Thu, 18 Aug 2016 12:47:17 +0300 Subject: [PATCH 07/15] Method to notify delegate if recording failed. --- StreamingKit/StreamingKit/STKAudioPlayer.h | 3 ++- StreamingKit/StreamingKit/STKAudioPlayer.m | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 3fdd6115..ad5b0d8f 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -135,7 +135,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn -(void) audioPlayer:(STKAudioPlayer*)audioPlayer logInfo:(NSString*)line; /// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop) -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems; - +/// Raised when stream recording fails +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFailToRecordQueueItemId:(NSObject*)queueItemId withStatus:(OSStatus)status; @end @interface STKAudioPlayer : NSObject diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 492466d2..5744f0d8 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2024,6 +2024,9 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd if (status) { NSLog(@"STKAudioPlayer failed to create a recording audio converter"); + if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { + [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; + } } } @@ -2134,6 +2137,9 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl); [self closeRecordAudioFile]; + if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { + [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:error]; + } } } } @@ -2816,8 +2822,11 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio * or a corrupt packet size was read when the packet table was built. */ - if (writeError == 1885563711) { //kAudioFileInvalidPacketOffsetError (happens sometimes, probably on VBR) + if (writeError == 1885563711) { //kAudioFileInvalidPacketOffsetError + if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { + [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; + } [self closeRecordAudioFile]; break; } From 9cf165bb2177e772a3dfb9af145801a4e0cc30e2 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 7 Sep 2016 19:21:11 +0300 Subject: [PATCH 08/15] Handle Osstatus -50. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 5744f0d8..29860765 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2822,14 +2822,9 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio * or a corrupt packet size was read when the packet table was built. */ - if (writeError == 1885563711) { //kAudioFileInvalidPacketOffsetError - - if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { - [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; - } - [self closeRecordAudioFile]; - break; - } + NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); + [self fireRecordingErrorWithStatus:status]; + break; } else { @@ -2840,6 +2835,8 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio else { NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); + [self fireRecordingErrorWithStatus:status]; + break; } if (status == 100) @@ -2850,6 +2847,14 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio } } +- (void)fireRecordingErrorWithStatus:(OSStatus)status +{ + if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { + [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; + } + [self closeRecordAudioFile]; +} + static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; From c8b4b2abbd6f391581d02823f04a7e580f6646dc Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Thu, 15 Sep 2016 17:28:39 +0300 Subject: [PATCH 09/15] closeRecordAudioFile failures logging. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 29860765..ddd41afc 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1865,14 +1865,22 @@ -(void) closeRecordAudioFile { if (recordAudioFileId) { - AudioFileClose(recordAudioFileId); - recordAudioFileId = NULL; + OSStatus status = AudioFileClose(recordAudioFileId); + if (status) { + NSLog(@"STKAudioPlayer ERROR on AudioFileClose(). OSStatus %lu", status); + } else { + recordAudioFileId = NULL; + } } if (recordAudioConverterRef) { - AudioConverterDispose(recordAudioConverterRef); - recordAudioConverterRef = nil; + OSStatus status = AudioConverterDispose(recordAudioConverterRef); + if (status) { + NSLog(@"STKAudioPlayer ERROR on AudioConverterDispose(). OSStatus %lu", status); + } else { + recordAudioConverterRef = NULL; + } } if (recordOutputBuffer) From 5f5cbe3cded068c4b1b5120d2129af6db46b2835 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 21 Sep 2016 13:18:14 +0300 Subject: [PATCH 10/15] Always close recording before notifying delegate that it failed. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index ddd41afc..f07c3feb 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1987,6 +1987,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd [self destroyAudioConverter]; BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil; + OSStatus didFailToRecordStatus = 0; if (isRecording) { @@ -2027,14 +2028,11 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd if (isRecording && !recordAudioConverterRef) { - status = AudioConverterNew(&canonicalAudioStreamBasicDescription, &recordAudioStreamBasicDescription, &recordAudioConverterRef); + didFailToRecordStatus = AudioConverterNew(&canonicalAudioStreamBasicDescription, &recordAudioStreamBasicDescription, &recordAudioConverterRef); - if (status) + if (didFailToRecordStatus) { NSLog(@"STKAudioPlayer failed to create a recording audio converter"); - if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { - [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; - } } } @@ -2131,7 +2129,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize); - error = AudioFileCreateWithURL( + didFailToRecordStatus = AudioFileCreateWithURL( (__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl), kAudioFileCAFType, &recordAudioStreamBasicDescription, @@ -2140,16 +2138,15 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd recordFilePacketPosition = 0; - if (error) + if (didFailToRecordStatus) { NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl); - - [self closeRecordAudioFile]; - if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { - [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:error]; - } } } + + if (didFailToRecordStatus) { + [self fireRecordingErrorWithStatus:didFailToRecordStatus]; + } } -(void) createOutputUnit @@ -2857,10 +2854,10 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio - (void)fireRecordingErrorWithStatus:(OSStatus)status { + [self closeRecordAudioFile]; if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; } - [self closeRecordAudioFile]; } static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) From 8dfbde744508c8f4a8cae07ccf252f1115e14fc9 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Wed, 21 Sep 2016 13:31:16 +0300 Subject: [PATCH 11/15] Close recording in lock. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index f07c3feb..05e310a6 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2145,7 +2145,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd } if (didFailToRecordStatus) { - [self fireRecordingErrorWithStatus:didFailToRecordStatus]; + [self fireRecordingErrorWithStatus:didFailToRecordStatus isInLock:YES]; } } @@ -2828,7 +2828,7 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio */ NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); - [self fireRecordingErrorWithStatus:status]; + [self fireRecordingErrorWithStatus:status isInLock:NO]; break; } else @@ -2840,7 +2840,7 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio else { NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); - [self fireRecordingErrorWithStatus:status]; + [self fireRecordingErrorWithStatus:status isInLock:NO]; break; } @@ -2852,9 +2852,18 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio } } -- (void)fireRecordingErrorWithStatus:(OSStatus)status +- (void)fireRecordingErrorWithStatus:(OSStatus)status isInLock:(BOOL)locked { + if (!locked) { + pthread_mutex_lock(&playerMutex); + } + [self closeRecordAudioFile]; + + if (!locked) { + pthread_mutex_unlock(&playerMutex); + } + if ([_delegate respondsToSelector:@selector(audioPlayer:didFailToRecordQueueItemId:withStatus:)]) { [_delegate audioPlayer:self didFailToRecordQueueItemId:currentlyPlayingEntry.queueItemId withStatus:status]; } From eca966bb4653cc47ec6f1c5993ccbc0fc9544c38 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Mon, 24 Oct 2016 12:21:48 +0300 Subject: [PATCH 12/15] clearQueueWithCompletion: method. --- StreamingKit/StreamingKit/STKAudioPlayer.h | 28 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 1922 +++++++++++--------- 2 files changed, 1030 insertions(+), 920 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index ad5b0d8f..1b3a6944 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -60,18 +60,18 @@ typedef NS_OPTIONS(NSInteger, STKAudioPlayerState) typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason) { - STKAudioPlayerStopReasonNone = 0, - STKAudioPlayerStopReasonEof, - STKAudioPlayerStopReasonUserAction, - STKAudioPlayerStopReasonPendingNext, - STKAudioPlayerStopReasonDisposed, - STKAudioPlayerStopReasonError = 0xffff + STKAudioPlayerStopReasonNone = 0, + STKAudioPlayerStopReasonEof, + STKAudioPlayerStopReasonUserAction, + STKAudioPlayerStopReasonPendingNext, + STKAudioPlayerStopReasonDisposed, + STKAudioPlayerStopReasonError = 0xffff }; typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode) { - STKAudioPlayerErrorNone = 0, - STKAudioPlayerErrorDataSource, + STKAudioPlayerErrorNone = 0, + STKAudioPlayerErrorDataSource, STKAudioPlayerErrorStreamParseBytesFailed, STKAudioPlayerErrorAudioSystemError, STKAudioPlayerErrorCodecError, @@ -92,13 +92,13 @@ typedef struct BOOL enableVolumeMixer; /// A pointer to a 0 terminated array of band frequencies (iOS 5.0 and later, OSX 10.9 and later) Float32 equalizerBandFrequencies[24]; - /// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger. + /// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger. UInt32 readBufferSize; /// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM) Float32 bufferSizeInSeconds; /// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds) Float32 secondsRequiredToStartPlaying; - /// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering) + /// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering) Float32 gracePeriodAfterSeekInSeconds; /// Number of seconds of decompressed audio required before playback resumes after a buffer underrun (Default is 5 seconds. Must be larger than bufferSizeinSeconds) Float32 secondsRequiredToStartPlayingAfterBufferUnderun; @@ -229,6 +229,14 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn /// The didCancelItems event will be raised for the items removed from the queue. -(void) clearQueue; +-(void) clearQueueWithCompletion:(void (^)())completionBlock; + +/// Dequeues the URL for playback and uses the NSURL as the queueItemID +-(void) dequeueURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock; + +/// Dequeues all items the URL for playback and uses the NSURL as the queueItemID +-(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock; + /// Pauses playback -(void) pause; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 05e310a6..baf7f48b 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -89,8 +89,8 @@ static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options) { options->secondsRequiredToStartPlayingAfterBufferUnderun = MIN(STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING_AFTER_BUFFER_UNDERRUN, options->bufferSizeInSeconds); } - - if (options->gracePeriodAfterSeekInSeconds == 0) + + if (options->gracePeriodAfterSeekInSeconds == 0) { options->gracePeriodAfterSeekInSeconds = MIN(STK_DEFAULT_GRACE_PERIOD_AFTER_SEEK_SECONDS, options->bufferSizeInSeconds); } @@ -125,33 +125,33 @@ static void NormalizeDisabledBuffers(STKAudioPlayerOptions* options) } #define CHECK_STATUS_AND_REPORT(call) \ - if ((status = (call))) \ - { \ - [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ - } +if ((status = (call))) \ +{ \ +[self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ +} #define CHECK_STATUS_AND_RETURN(call) \ - if ((status = (call))) \ - { \ - [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ - return;\ - } +if ((status = (call))) \ +{ \ +[self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ +return;\ +} #define CHECK_STATUS_AND_RETURN_VALUE(call, value) \ - if ((status = (call))) \ - { \ - [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ - return value;\ - } +if ((status = (call))) \ +{ \ +[self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ +return value;\ +} typedef enum { - STKAudioPlayerInternalStateInitialised = 0, + STKAudioPlayerInternalStateInitialised = 0, STKAudioPlayerInternalStateRunning = 1, STKAudioPlayerInternalStatePlaying = (1 << 1) | STKAudioPlayerInternalStateRunning, STKAudioPlayerInternalStateRebuffering = (1 << 2) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateStartingThread = (1 << 3) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateWaitingForData = (1 << 4) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateStartingThread = (1 << 3) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateWaitingForData = (1 << 4) | STKAudioPlayerInternalStateRunning, /* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */ STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning, STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning, @@ -167,31 +167,31 @@ static void NormalizeDisabledBuffers(STKAudioPlayerOptions* options) @interface STKFrameFilterEntry() { @public - NSString* name; - STKFrameFilter filter; + NSString* name; + STKFrameFilter filter; } @end @implementation STKFrameFilterEntry -(instancetype) initWithFilter:(STKFrameFilter)filterIn andName:(NSString*)nameIn { - if (self = [super init]) - { - self->filter = [filterIn copy]; - self->name = nameIn; - } - - return self; + if (self = [super init]) + { + self->filter = [filterIn copy]; + self->name = nameIn; + } + + return self; } -(NSString*) name { - return self->name; + return self->name; } -(STKFrameFilter) filter { - return self->filter; + return self->filter; } @end @@ -208,43 +208,43 @@ -(STKFrameFilter) filter @interface STKAudioPlayer() { - BOOL muted; - + BOOL muted; + UInt8* readBuffer; int readBufferSize; STKAudioPlayerInternalState internalState; - - Float32 volume; - Float32 peakPowerDb[2]; - Float32 averagePowerDb[2]; - - BOOL meteringEnabled; + + Float32 volume; + Float32 peakPowerDb[2]; + Float32 averagePowerDb[2]; + + BOOL meteringEnabled; BOOL equalizerOn; BOOL equalizerEnabled; STKAudioPlayerOptions options; - + NSMutableArray* converterNodes; - - AUGraph audioGraph; + + AUGraph audioGraph; AUNode eqNode; - AUNode mixerNode; + AUNode mixerNode; AUNode outputNode; - - AUNode eqInputNode; - AUNode eqOutputNode; - AUNode mixerInputNode; - AUNode mixerOutputNode; - + + AUNode eqInputNode; + AUNode eqOutputNode; + AUNode mixerInputNode; + AUNode mixerOutputNode; + AudioComponentInstance eqUnit; - AudioComponentInstance mixerUnit; - AudioComponentInstance outputUnit; - + AudioComponentInstance mixerUnit; + AudioComponentInstance outputUnit; + UInt32 eqBandCount; int32_t waitingForDataAfterSeekFrameCount; - + UInt32 framesRequiredToStartPlaying; UInt32 framesRequiredToPlayAfterRebuffering; - UInt32 framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying; + UInt32 framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying; STKQueueEntry* volatile currentlyPlayingEntry; STKQueueEntry* volatile currentlyReadingEntry; @@ -262,12 +262,12 @@ @interface STKAudioPlayer() AudioBuffer* pcmAudioBuffer; AudioBufferList pcmAudioBufferList; AudioConverterRef audioConverterRef; - + AudioStreamBasicDescription audioConverterAudioStreamBasicDescription; - BOOL deallocating; + BOOL deallocating; BOOL discontinuous; - NSArray* frameFilters; + NSArray* frameFilters; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; AudioFileStreamID audioFileStream; @@ -283,12 +283,12 @@ @interface STKAudioPlayer() UInt32 recordPacketSize; AudioStreamPacketDescription *recordPacketDescriptions; - void(^stopBackBackgroundTaskBlock)(); + void(^stopBackBackgroundTaskBlock)(); int32_t seekVersion; OSSpinLock seekLock; OSSpinLock currentEntryReferencesLock; - + pthread_mutex_t playerMutex; pthread_cond_t playerThreadReadyCondition; pthread_mutex_t mainThreadSyncCallMutex; @@ -310,16 +310,16 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte static void AudioFileStreamPropertyListenerProc(void* clientData, AudioFileStreamID audioFileStream, AudioFileStreamPropertyID propertyId, UInt32* flags) { - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; + STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - [player handlePropertyChangeForFileStream:audioFileStream fileStreamPropertyID:propertyId ioFlags:flags]; + [player handlePropertyChangeForFileStream:audioFileStream fileStreamPropertyID:propertyId ioFlags:flags]; } static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UInt32 numberPackets, const void* inputData, AudioStreamPacketDescription* packetDescriptions) { - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; + STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - [player handleAudioPackets:inputData numberBytes:numberBytes numberPackets:numberPackets packetDescriptions:packetDescriptions]; + [player handleAudioPackets:inputData numberBytes:numberBytes numberPackets:numberPackets packetDescriptions:packetDescriptions]; } @implementation STKAudioPlayer @@ -355,34 +355,34 @@ +(void) initialize .mBytesPerPacket = (bytesPerSample * 2) }; - outputUnitDescription = (AudioComponentDescription) - { - .componentType = kAudioUnitType_Output, + outputUnitDescription = (AudioComponentDescription) + { + .componentType = kAudioUnitType_Output, #if TARGET_OS_IPHONE - .componentSubType = kAudioUnitSubType_RemoteIO, + .componentSubType = kAudioUnitSubType_RemoteIO, #else - .componentSubType = kAudioUnitSubType_DefaultOutput, + .componentSubType = kAudioUnitSubType_DefaultOutput, #endif - .componentFlags = 0, - .componentFlagsMask = 0, - .componentManufacturer = kAudioUnitManufacturer_Apple - }; - - mixerDescription = (AudioComponentDescription) - { - .componentType = kAudioUnitType_Mixer, - .componentSubType = kAudioUnitSubType_MultiChannelMixer, - .componentFlags = 0, - .componentFlagsMask = 0, - .componentManufacturer = kAudioUnitManufacturer_Apple - }; - - nbandUnitDescription = (AudioComponentDescription) - { - .componentType = kAudioUnitType_Effect, - .componentSubType = kAudioUnitSubType_NBandEQ, - .componentManufacturer=kAudioUnitManufacturer_Apple - }; + .componentFlags = 0, + .componentFlagsMask = 0, + .componentManufacturer = kAudioUnitManufacturer_Apple + }; + + mixerDescription = (AudioComponentDescription) + { + .componentType = kAudioUnitType_Mixer, + .componentSubType = kAudioUnitSubType_MultiChannelMixer, + .componentFlags = 0, + .componentFlagsMask = 0, + .componentManufacturer = kAudioUnitManufacturer_Apple + }; + + nbandUnitDescription = (AudioComponentDescription) + { + .componentType = kAudioUnitType_Effect, + .componentSubType = kAudioUnitSubType_NBandEQ, + .componentManufacturer=kAudioUnitManufacturer_Apple + }; } -(STKAudioPlayerOptions) options { @@ -407,42 +407,42 @@ -(void) setInternalState:(STKAudioPlayerInternalState)value ifInState:(BOOL(^)(S { case STKAudioPlayerInternalStateInitialised: newState = STKAudioPlayerStateReady; - stopReason = STKAudioPlayerStopReasonNone; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateRunning: case STKAudioPlayerInternalStateStartingThread: case STKAudioPlayerInternalStatePlaying: - case STKAudioPlayerInternalStateWaitingForDataAfterSeek: + case STKAudioPlayerInternalStateWaitingForDataAfterSeek: newState = STKAudioPlayerStatePlaying; - stopReason = STKAudioPlayerStopReasonNone; + stopReason = STKAudioPlayerStopReasonNone; break; - case STKAudioPlayerInternalStatePendingNext: + case STKAudioPlayerInternalStatePendingNext: case STKAudioPlayerInternalStateRebuffering: case STKAudioPlayerInternalStateWaitingForData: newState = STKAudioPlayerStateBuffering; - stopReason = STKAudioPlayerStopReasonNone; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateStopped: newState = STKAudioPlayerStateStopped; break; case STKAudioPlayerInternalStatePaused: newState = STKAudioPlayerStatePaused; - stopReason = STKAudioPlayerStopReasonNone; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateDisposed: newState = STKAudioPlayerStateDisposed; - stopReason = STKAudioPlayerStopReasonUserAction; + stopReason = STKAudioPlayerStopReasonUserAction; break; case STKAudioPlayerInternalStateError: newState = STKAudioPlayerStateError; - stopReason = STKAudioPlayerStopReasonError; + stopReason = STKAudioPlayerStopReasonError; break; } - + OSSpinLockLock(&internalStateLock); - - waitingForDataAfterSeekFrameCount = 0; - + + waitingForDataAfterSeekFrameCount = 0; + if (value == internalState) { OSSpinLockUnlock(&internalStateLock); @@ -462,8 +462,8 @@ -(void) setInternalState:(STKAudioPlayerInternalState)value ifInState:(BOOL(^)(S internalState = value; - STKAudioPlayerState previousState = self.state; - + STKAudioPlayerState previousState = self.state; + if (newState != previousState && !deallocating) { self.state = newState; @@ -471,9 +471,9 @@ -(void) setInternalState:(STKAudioPlayerInternalState)value ifInState:(BOOL(^)(S OSSpinLockUnlock(&internalStateLock); dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self stateChanged:self.state previousState:previousState]; - }); + { + [self.delegate audioPlayer:self stateChanged:self.state previousState:previousState]; + }); } else { @@ -514,16 +514,16 @@ -(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn if (self = [super init]) { options = optionsIn; - - self->volume = 1.0; + + self->volume = 1.0; self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0; - + PopulateOptionsWithDefault(&options); NormalizeDisabledBuffers(&options); framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlaying; framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlayingAfterBufferUnderun; - framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.gracePeriodAfterSeekInSeconds; + framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.gracePeriodAfterSeekInSeconds; pcmAudioBuffer = &pcmAudioBufferList.mBuffers[0]; @@ -531,7 +531,7 @@ -(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * options.bufferSizeInSeconds) * canonicalAudioStreamBasicDescription.mBytesPerFrame; pcmAudioBufferList.mBuffers[0].mData = (void*)calloc(pcmAudioBuffer->mDataByteSize, 1); pcmAudioBufferList.mBuffers[0].mNumberChannels = 2; - + pcmBufferFrameSizeInBytes = canonicalAudioStreamBasicDescription.mBytesPerFrame; pcmBufferTotalFrameCount = pcmAudioBuffer->mDataByteSize / pcmBufferFrameSizeInBytes; @@ -547,7 +547,7 @@ -(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn pthread_mutex_init(&mainThreadSyncCallMutex, NULL); pthread_cond_init(&playerThreadReadyCondition, NULL); pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL); - + threadStartedLock = [[NSConditionLock alloc] initWithCondition:0]; threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0]; @@ -555,8 +555,8 @@ -(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn upcomingQueue = [[NSMutableArray alloc] init]; bufferingQueue = [[NSMutableArray alloc] init]; - - [self resetPcmBuffers]; + + [self resetPcmBuffers]; [self createAudioGraph]; [self createPlaybackThread]; } @@ -566,12 +566,12 @@ -(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn -(void) destroyAudioResources { - if (currentlyReadingEntry) + if (currentlyReadingEntry) { currentlyReadingEntry.dataSource.delegate = nil; [currentlyReadingEntry.dataSource unregisterForEvents]; - - currentlyReadingEntry = nil; + + currentlyReadingEntry = nil; } if (currentlyPlayingEntry) @@ -581,7 +581,7 @@ -(void) destroyAudioResources OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; + currentlyPlayingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); } @@ -589,38 +589,38 @@ -(void) destroyAudioResources [self closeRecordAudioFile]; [self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed]; - - [self clearQueue]; - + + [self clearQueue]; + if (audioFileStream) { AudioFileStreamClose(audioFileStream); - audioFileStream = nil; + audioFileStream = nil; } if (audioConverterRef) { AudioConverterDispose(audioConverterRef); - audioConverterRef = nil; + audioConverterRef = nil; } if (audioGraph) { - AUGraphUninitialize(audioGraph); - AUGraphClose(audioGraph); - DisposeAUGraph(audioGraph); - - audioGraph = nil; + AUGraphUninitialize(audioGraph); + AUGraphClose(audioGraph); + DisposeAUGraph(audioGraph); + + audioGraph = nil; } } -(void) dealloc { - NSLog(@"STKAudioPlayer dealloc"); - - deallocating = YES; - - [self destroyAudioResources]; + NSLog(@"STKAudioPlayer dealloc"); + + deallocating = YES; + + [self destroyAudioResources]; pthread_mutex_destroy(&playerMutex); pthread_mutex_destroy(&mainThreadSyncCallMutex); @@ -634,33 +634,33 @@ -(void) dealloc -(void) startSystemBackgroundTask { #if TARGET_OS_IPHONE - __block UIBackgroundTaskIdentifier backgroundTaskId = UIBackgroundTaskInvalid; - - backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ - { - [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; - backgroundTaskId = UIBackgroundTaskInvalid; - }]; - - stopBackBackgroundTaskBlock = [^ - { - if (backgroundTaskId != UIBackgroundTaskInvalid) - { - [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; - backgroundTaskId = UIBackgroundTaskInvalid; - } - } copy]; + __block UIBackgroundTaskIdentifier backgroundTaskId = UIBackgroundTaskInvalid; + + backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ + { + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; + backgroundTaskId = UIBackgroundTaskInvalid; + }]; + + stopBackBackgroundTaskBlock = [^ + { + if (backgroundTaskId != UIBackgroundTaskInvalid) + { + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; + backgroundTaskId = UIBackgroundTaskInvalid; + } + } copy]; #endif } -(void) stopSystemBackgroundTask { #if TARGET_OS_IPHONE - if (stopBackBackgroundTaskBlock != NULL) - { - stopBackBackgroundTaskBlock(); - stopBackBackgroundTaskBlock = NULL; - } + if (stopBackBackgroundTaskBlock != NULL) + { + stopBackBackgroundTaskBlock(); + stopBackBackgroundTaskBlock = NULL; + } #endif } @@ -681,6 +681,11 @@ +(STKDataSource*) dataSourceFromURL:(NSURL*)url } -(void) clearQueue +{ + [self clearQueueWithCompletion:NULL]; +} + +-(void) clearQueueWithCompletion:(void (^)())completionBlock { pthread_mutex_lock(&playerMutex); { @@ -692,30 +697,128 @@ -(void) clearQueue { [array addObject:entry.queueItemId]; } - - for (STKQueueEntry* entry in bufferingQueue) + + for (STKQueueEntry* entry in bufferingQueue) { [array addObject:entry.queueItemId]; } [upcomingQueue removeAllObjects]; - [bufferingQueue removeAllObjects]; + [bufferingQueue removeAllObjects]; - if (array.count > 0) + if ((array.count > 0 && [self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + || completionBlock) { [self playbackThreadQueueMainThreadSyncBlock:^ - { - if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) - { - [self.delegate audioPlayer:self didCancelQueuedItems:array]; - } - }]; + { + if (array.count > 0 && [self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + { + [self.delegate audioPlayer:self didCancelQueuedItems:array]; + } + + if (completionBlock) + { + completionBlock(); + } + + }]; } } else { [bufferingQueue removeAllObjects]; [upcomingQueue removeAllObjects]; + + if (completionBlock) { + [self playbackThreadQueueMainThreadSyncBlock:^ + { + completionBlock(); + }]; + } + } + } + pthread_mutex_unlock(&playerMutex); +} + +-(void) dequeueURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock +{ + pthread_mutex_lock(&playerMutex); + { + NSMutableArray* resultQueue = [NSMutableArray array]; + NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; + + for (int i = 0; i < upcomingQueue.count; i++) + { + STKQueueEntry* entry = upcomingCopy[i]; + + if ([entry.queueItemId isEqual:url]) { + [upcomingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; + } + } + + NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + + for (int i = 0; i < bufferingQueue.count; i++) + { + STKQueueEntry* entry = bufferingCopy[i]; + + if ([entry.queueItemId isEqual:url]) { + [bufferingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; + } + } + + if (completionBlock) + { + [self playbackThreadQueueMainThreadSyncBlock:^ + { + completionBlock([resultQueue copy]); + }]; + } + } + pthread_mutex_unlock(&playerMutex); +} + +-(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock +{ + pthread_mutex_lock(&playerMutex); + { + NSMutableArray* resultQueue = [NSMutableArray array]; + NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; + + for (int i = 0; i < upcomingQueue.count; i++) + { + STKQueueEntry* entry = upcomingCopy[i]; + + if (![entry.queueItemId isEqual:url]) { + [upcomingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; + } + } + + NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + + for (int i = 0; i < bufferingQueue.count; i++) + { + STKQueueEntry* entry = bufferingCopy[i]; + + if (![entry.queueItemId isEqual:url]) { + [bufferingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; + } + } + + if (completionBlock) + { + [self playbackThreadQueueMainThreadSyncBlock:^ + { + completionBlock([resultQueue copy]); + }]; } } pthread_mutex_unlock(&playerMutex); @@ -723,34 +826,34 @@ -(void) clearQueue -(void) play:(NSString*)urlString { - [self play:urlString withQueueItemID:urlString]; + [self play:urlString withQueueItemID:urlString]; } -(void) play:(NSString*)urlString withQueueItemID:(NSObject*)queueItemId { NSURL* url = [NSURL URLWithString:urlString]; - [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; + [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; } -(void) playURL:(NSURL*)url { - [self playURL:url withQueueItemID:url]; + [self playURL:url withQueueItemID:url]; } -(void) playURL:(NSURL*)url withQueueItemID:(NSObject*)queueItemId { - [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; + [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; } -(void) playDataSource:(STKDataSource*)dataSource { - [self playDataSource:dataSource withQueueItemID:dataSource]; + [self playDataSource:dataSource withQueueItemID:dataSource]; } -(void) playDataSource:(STKDataSource*)dataSource withQueueItemID:(NSObject *)queueItemId { - [self setDataSource:dataSource withQueueItemId:queueItemId]; + [self setDataSource:dataSource withQueueItemId:queueItemId]; } -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId @@ -759,13 +862,13 @@ -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)qu { LOGINFO(([NSString stringWithFormat:@"Playing: %@", [queueItemId description]])); - if (![self audioGraphIsRunning]) - { - [self startSystemBackgroundTask]; - } + if (![self audioGraphIsRunning]) + { + [self startSystemBackgroundTask]; + } [self clearQueue]; - + [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; self.internalState = STKAudioPlayerInternalStatePendingNext; @@ -777,32 +880,32 @@ -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)qu -(void) queue:(NSString*)urlString { - return [self queueURL:[NSURL URLWithString:urlString] withQueueItemId:urlString]; + return [self queueURL:[NSURL URLWithString:urlString] withQueueItemId:urlString]; } -(void) queue:(NSString*)urlString withQueueItemId:(NSObject*)queueItemId { - [self queueURL:[NSURL URLWithString:urlString] withQueueItemId:queueItemId]; + [self queueURL:[NSURL URLWithString:urlString] withQueueItemId:queueItemId]; } -(void) queueURL:(NSURL*)url { - [self queueURL:url withQueueItemId:url]; + [self queueURL:url withQueueItemId:url]; } -(void) queueURL:(NSURL*)url withQueueItemId:(NSObject*)queueItemId { - [self queueDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; + [self queueDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:queueItemId]; } -(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { pthread_mutex_lock(&playerMutex); { - if (![self audioGraphIsRunning]) - { - [self startSystemBackgroundTask]; - } + if (![self audioGraphIsRunning]) + { + [self startSystemBackgroundTask]; + } [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; } @@ -813,7 +916,7 @@ -(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*) -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream fileStreamPropertyID:(AudioFileStreamPropertyID)inPropertyID ioFlags:(UInt32*)ioFlags { - OSStatus error; + OSStatus error; if (!currentlyReadingEntry) { @@ -837,9 +940,9 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f case kAudioFileStreamProperty_FileFormat: { char fileFormat[4]; - UInt32 fileFormatSize = sizeof(fileFormat); + UInt32 fileFormatSize = sizeof(fileFormat); - AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FileFormat, &fileFormatSize, &fileFormat); + AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FileFormat, &fileFormatSize, &fileFormat); break; } @@ -847,13 +950,13 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f { AudioStreamBasicDescription newBasicDescription; STKQueueEntry* entryToUpdate = currentlyReadingEntry; - + if (!currentlyReadingEntry->parsedHeader) { UInt32 size = sizeof(newBasicDescription); AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription); - + pthread_mutex_lock(&playerMutex); if (entryToUpdate->audioStreamBasicDescription.mFormatID == 0) @@ -863,7 +966,7 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate; entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate; - + UInt32 packetBufferSize = 0; UInt32 sizeOfPacketBufferSize = sizeof(packetBufferSize); @@ -905,12 +1008,12 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f break; } - case kAudioFileStreamProperty_ReadyToProducePackets: + case kAudioFileStreamProperty_ReadyToProducePackets: { - if (audioConverterAudioStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) - { - discontinuous = YES; - } + if (audioConverterAudioStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) + { + discontinuous = YES; + } break; } @@ -942,7 +1045,7 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE || pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2) { currentlyReadingEntry->audioStreamBasicDescription = pasbd; - + break; } } @@ -951,7 +1054,7 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f break; } - + } } @@ -970,9 +1073,9 @@ -(void) unexpectedError:(STKAudioPlayerErrorCode)errorCodeIn self.internalState = STKAudioPlayerInternalStateError; [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self unexpectedError:errorCodeIn]; - }]; + { + [self.delegate audioPlayer:self unexpectedError:errorCodeIn]; + }]; } -(double) duration @@ -984,11 +1087,11 @@ -(double) duration OSSpinLockLock(¤tEntryReferencesLock); STKQueueEntry* entry = currentlyPlayingEntry; - OSSpinLockUnlock(¤tEntryReferencesLock); + OSSpinLockUnlock(¤tEntryReferencesLock); if (entry == nil) { - return 0; + return 0; } double retval = [entry duration]; @@ -1026,14 +1129,14 @@ -(double) progress OSSpinLockLock(&entry->spinLock); double retval = entry->seekTime + (entry->framesPlayed / canonicalAudioStreamBasicDescription.mSampleRate); OSSpinLockUnlock(&entry->spinLock); - + return retval; } -(BOOL) invokeOnPlaybackThread:(void(^)())block { - NSRunLoop* runLoop = playbackThreadRunLoop; - + NSRunLoop* runLoop = playbackThreadRunLoop; + if (runLoop) { CFRunLoopPerformBlock([runLoop getCFRunLoop], NSRunLoopCommonModes, block); @@ -1047,19 +1150,19 @@ -(BOOL) invokeOnPlaybackThread:(void(^)())block -(void) wakeupPlaybackThread { - [self invokeOnPlaybackThread:^ - { - [self processRunloop]; - }]; - - pthread_mutex_lock(&playerMutex); - - if (waiting) - { - pthread_cond_signal(&playerThreadReadyCondition); - } - - pthread_mutex_unlock(&playerMutex); + [self invokeOnPlaybackThread:^ + { + [self processRunloop]; + }]; + + pthread_mutex_lock(&playerMutex); + + if (waiting) + { + pthread_cond_signal(&playerThreadReadyCondition); + } + + pthread_mutex_unlock(&playerMutex); } -(void) seekToTime:(double)value @@ -1118,7 +1221,7 @@ -(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)sta -(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)startPlaying clearQueue:(BOOL)clearQueue { LOGINFO(([entry description])); - + if (startPlaying) { memset(&pcmAudioBuffer->mData[0], 0, pcmBufferTotalFrameCount * pcmBufferFrameSizeInBytes); @@ -1201,9 +1304,9 @@ -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:( if (!isPlayingSameItemProbablySeek && entry) { [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }]; + { + [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; + }]; } if (!isPlayingSameItemProbablySeek) @@ -1211,23 +1314,23 @@ -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:( [self setInternalState:STKAudioPlayerInternalStateWaitingForData]; [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; - }]; + { + [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; + }]; } } else { OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; + currentlyPlayingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); if (!isPlayingSameItemProbablySeek && entry) { [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }]; + { + [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; + }]; } } @@ -1236,42 +1339,42 @@ -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:( -(void) dispatchSyncOnMainThread:(void(^)())block { - __block BOOL finished = NO; - - if (disposeWasRequested) - { - return; - } - - dispatch_async(dispatch_get_main_queue(), ^ - { - if (!disposeWasRequested) - { - block(); - } - - pthread_mutex_lock(&mainThreadSyncCallMutex); - finished = YES; - pthread_cond_signal(&mainThreadSyncCallReadyCondition); - pthread_mutex_unlock(&mainThreadSyncCallMutex); - }); - + __block BOOL finished = NO; + + if (disposeWasRequested) + { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^ + { + if (!disposeWasRequested) + { + block(); + } + + pthread_mutex_lock(&mainThreadSyncCallMutex); + finished = YES; + pthread_cond_signal(&mainThreadSyncCallReadyCondition); + pthread_mutex_unlock(&mainThreadSyncCallMutex); + }); + pthread_mutex_lock(&mainThreadSyncCallMutex); - - while (true) - { - if (disposeWasRequested) - { - break; - } - - if (finished) - { - break; - } - - pthread_cond_wait(&mainThreadSyncCallReadyCondition, &mainThreadSyncCallMutex); - } + + while (true) + { + if (disposeWasRequested) + { + break; + } + + if (finished) + { + break; + } + + pthread_cond_wait(&mainThreadSyncCallReadyCondition, &mainThreadSyncCallMutex); + } pthread_mutex_unlock(&mainThreadSyncCallMutex); } @@ -1281,14 +1384,14 @@ -(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block block = [block copy]; [self invokeOnPlaybackThread:^ - { - if (disposeWasRequested) - { - return; - } - - [self dispatchSyncOnMainThread:block]; - }]; + { + if (disposeWasRequested) + { + return; + } + + [self dispatchSyncOnMainThread:block]; + }]; } -(void) requeueBufferingEntries @@ -1301,7 +1404,7 @@ -(void) requeueBufferingEntries [queueEntry reset]; } - + [upcomingQueue skipQueueWithQueue:bufferingQueue]; [bufferingQueue removeAllObjects]; @@ -1381,7 +1484,7 @@ -(BOOL) processRunloop { int32_t originalSeekVersion; BOOL originalSeekToTimeRequested; - + OSSpinLockLock(&seekLock); originalSeekVersion = seekVersion; originalSeekToTimeRequested = seekToTimeWasRequested; @@ -1411,18 +1514,18 @@ -(BOOL) processRunloop -(void) startInternal { - @autoreleasepool - { - playbackThreadRunLoop = [NSRunLoop currentRunLoop]; - NSThread.currentThread.threadPriority = 1; - - [threadStartedLock lockWhenCondition:0]; + @autoreleasepool + { + playbackThreadRunLoop = [NSRunLoop currentRunLoop]; + NSThread.currentThread.threadPriority = 1; + + [threadStartedLock lockWhenCondition:0]; [threadStartedLock unlockWithCondition:1]; - - [playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; - while (true) - { + [playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; + + while (true) + { @autoreleasepool { if (![self processRunloop]) @@ -1433,40 +1536,40 @@ -(void) startInternal NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceNow:10]; [playbackThreadRunLoop runMode:NSDefaultRunLoopMode beforeDate:date]; - } - - disposeWasRequested = NO; - seekToTimeWasRequested = NO; - - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyPlayingEntry.dataSource unregisterForEvents]; - - currentlyReadingEntry.dataSource.delegate = nil; - currentlyPlayingEntry.dataSource.delegate = nil; - + } + + disposeWasRequested = NO; + seekToTimeWasRequested = NO; + + [currentlyReadingEntry.dataSource unregisterForEvents]; + [currentlyPlayingEntry.dataSource unregisterForEvents]; + + currentlyReadingEntry.dataSource.delegate = nil; + currentlyPlayingEntry.dataSource.delegate = nil; + pthread_mutex_lock(&playerMutex); OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; + currentlyPlayingEntry = nil; + currentlyReadingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); pthread_mutex_unlock(&playerMutex); [self closeRecordAudioFile]; - - self.internalState = STKAudioPlayerInternalStateDisposed; - - playbackThreadRunLoop = nil; - - [self destroyAudioResources]; - - [threadFinishedCondLock lock]; - [threadFinishedCondLock unlockWithCondition:1]; - } + + self.internalState = STKAudioPlayerInternalStateDisposed; + + playbackThreadRunLoop = nil; + + [self destroyAudioResources]; + + [threadFinishedCondLock lock]; + [threadFinishedCondLock unlockWithCondition:1]; + } } -(void) processSeekToTime { - OSStatus error; + OSStatus error; STKQueueEntry* currentEntry = currentlyReadingEntry; NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same"); @@ -1522,8 +1625,8 @@ -(void) processSeekToTime [currentEntry reset]; [currentEntry.dataSource seekToOffset:seekByteOffset]; - self->waitingForDataAfterSeekFrameCount = 0; - + self->waitingForDataAfterSeekFrameCount = 0; + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; if (audioGraph) @@ -1534,7 +1637,7 @@ -(void) processSeekToTime -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn { - OSStatus error; + OSStatus error; if (currentlyReadingEntry.dataSource != dataSourceIn) { @@ -1627,7 +1730,7 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn dataSourceIn.delegate = nil; [dataSourceIn unregisterForEvents]; [dataSourceIn close]; - + return; } @@ -1641,11 +1744,11 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn [self closeRecordAudioFile]; [self dispatchSyncOnMainThread:^ - { - BOOL fullyProcessed = currentlyReadingEntry.dataSource.length == [currentlyReadingEntry.dataSource bytesRead]; - [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId fullyProcessed:fullyProcessed]; - }]; - + { + BOOL fullyProcessed = currentlyReadingEntry.dataSource.length == [currentlyReadingEntry.dataSource bytesRead]; + [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId fullyProcessed:fullyProcessed]; + }]; + pthread_mutex_lock(&playerMutex); if (currentlyReadingEntry == nil) @@ -1678,7 +1781,7 @@ -(void) pause { pthread_mutex_lock(&playerMutex); { - OSStatus error; + OSStatus error; if (self.internalState != STKAudioPlayerInternalStatePaused && (self.internalState & STKAudioPlayerInternalStateRunning)) { @@ -1709,8 +1812,8 @@ -(void) resume { pthread_mutex_lock(&playerMutex); { - OSStatus error; - + OSStatus error; + if (self.internalState == STKAudioPlayerInternalStatePaused) { self.internalState = self.stateBeforePaused; @@ -1719,10 +1822,10 @@ -(void) resume { [self resetPcmBuffers]; } - + if (audioGraph != nil) { - error = AUGraphStart(audioGraph); + error = AUGraphStart(audioGraph); if (error) { @@ -1746,10 +1849,10 @@ -(void) resetPcmBuffers self->pcmBufferFrameStartIndex = 0; self->pcmBufferUsedFrameCount = 0; - self->peakPowerDb[0] = STK_DBMIN; - self->peakPowerDb[1] = STK_DBMIN; - self->averagePowerDb[0] = STK_DBMIN; - self->averagePowerDb[1] = STK_DBMIN; + self->peakPowerDb[0] = STK_DBMIN; + self->peakPowerDb[1] = STK_DBMIN; + self->averagePowerDb[0] = STK_DBMIN; + self->averagePowerDb[1] = STK_DBMIN; OSSpinLockUnlock(&pcmBufferSpinLock); } @@ -1768,34 +1871,34 @@ -(void) stop [self closeRecordAudioFile]; [self stopAudioUnitWithReason:STKAudioPlayerStopReasonUserAction]; - + [self resetPcmBuffers]; - + [self invokeOnPlaybackThread:^ - { - pthread_mutex_lock(&playerMutex); - { - currentlyReadingEntry.dataSource.delegate = nil; - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyReadingEntry.dataSource close]; - - if (currentlyPlayingEntry) - { - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; - } - - [self clearQueue]; - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; - seekToTimeWasRequested = NO; - OSSpinLockUnlock(¤tEntryReferencesLock); - } - pthread_mutex_unlock(&playerMutex); - }]; + { + pthread_mutex_lock(&playerMutex); + { + currentlyReadingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; + [currentlyReadingEntry.dataSource close]; + + if (currentlyPlayingEntry) + { + [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; + } + + [self clearQueue]; + + OSSpinLockLock(¤tEntryReferencesLock); + currentlyPlayingEntry = nil; + currentlyReadingEntry = nil; + seekToTimeWasRequested = NO; + OSSpinLockUnlock(¤tEntryReferencesLock); + } + pthread_mutex_unlock(&playerMutex); + }]; - [self wakeupPlaybackThread]; + [self wakeupPlaybackThread]; } pthread_mutex_unlock(&playerMutex); } @@ -1811,17 +1914,17 @@ -(void) stopThread wait = YES; [self invokeOnPlaybackThread:^ - { - disposeWasRequested = YES; - - CFRunLoopStop(CFRunLoopGetCurrent()); - }]; + { + disposeWasRequested = YES; + + CFRunLoopStop(CFRunLoopGetCurrent()); + }]; pthread_mutex_lock(&playerMutex); disposeWasRequested = YES; pthread_cond_signal(&playerThreadReadyCondition); pthread_mutex_unlock(&playerMutex); - + pthread_mutex_lock(&mainThreadSyncCallMutex); disposeWasRequested = YES; pthread_cond_signal(&mainThreadSyncCallReadyCondition); @@ -1833,22 +1936,22 @@ -(void) stopThread [threadFinishedCondLock lockWhenCondition:1]; [threadFinishedCondLock unlockWithCondition:0]; } - - [self destroyAudioResources]; - - runLoop = nil; - playbackThread = nil; - playbackThreadRunLoop = nil; + + [self destroyAudioResources]; + + runLoop = nil; + playbackThread = nil; + playbackThreadRunLoop = nil; } -(BOOL) muted { - return self->muted; + return self->muted; } -(void) setMuted:(BOOL)value { - self->muted = value; + self->muted = value; } -(void) mute @@ -1928,12 +2031,12 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl { #if TARGET_OS_IPHONE UInt32 size; - + if (AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size) != 0) { return NO; } - + UInt32 decoderCount = size / sizeof(AudioClassDescription); AudioClassDescription encoderDescriptions[decoderCount]; @@ -1983,7 +2086,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd return; } - + [self destroyAudioConverter]; BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil; @@ -2035,7 +2138,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd NSLog(@"STKAudioPlayer failed to create a recording audio converter"); } } - + audioConverterAudioStreamBasicDescription = *asbd; // Should probably be (self->currentlyReadingEntry.dataSource.audioFileTypeHint == kAudioFileAAC_ADTSType). @@ -2045,7 +2148,7 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileMP3Type) { status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); - + if (status) { return; @@ -2128,13 +2231,13 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd } recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize); - + didFailToRecordStatus = AudioFileCreateWithURL( - (__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl), - kAudioFileCAFType, - &recordAudioStreamBasicDescription, - kAudioFileFlags_EraseFile, - &recordAudioFileId); + (__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl), + kAudioFileCAFType, + &recordAudioStreamBasicDescription, + kAudioFileFlags_EraseFile, + &recordAudioFileId); recordFilePacketPosition = 0; @@ -2151,45 +2254,45 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd -(void) createOutputUnit { - OSStatus status; - - CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &outputUnitDescription, &outputNode)); - CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, outputNode, &outputUnitDescription, &outputUnit)); - + OSStatus status; + + CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &outputUnitDescription, &outputNode)); + CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, outputNode, &outputUnitDescription, &outputUnit)); + #if TARGET_OS_IPHONE UInt32 flag = 1; - - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag))); - - flag = 0; - - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag))); -#endif + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag))); + + flag = 0; + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag))); +#endif CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription))); } -(void) createMixerUnit { - OSStatus status; - - if (!self.options.enableVolumeMixer) - { - return; - } - - CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &mixerDescription, &mixerNode)); - CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, mixerNode, &mixerDescription, &mixerUnit)); - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice))); - - UInt32 busCount = 1; - - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount))); - - Float64 graphSampleRate = 44100.0; - - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &graphSampleRate, sizeof(graphSampleRate))); - CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 0, 1.0, 0)); + OSStatus status; + + if (!self.options.enableVolumeMixer) + { + return; + } + + CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &mixerDescription, &mixerNode)); + CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, mixerNode, &mixerDescription, &mixerUnit)); + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice))); + + UInt32 busCount = 1; + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount))); + + Float64 graphSampleRate = 44100.0; + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &graphSampleRate, sizeof(graphSampleRate))); + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 0, 1.0, 0)); } -(void) createEqUnit @@ -2197,63 +2300,63 @@ -(void) createEqUnit #if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 return; #else - OSStatus status; - - if (self->options.equalizerBandFrequencies[0] == 0) - { - return; - } - - CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &nbandUnitDescription, &eqNode)); - CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, eqNode, NULL, &eqUnit)); - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice))); - - while (self->options.equalizerBandFrequencies[eqBandCount] != 0) - { - eqBandCount++; - } - - CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAUNBandEQProperty_NumberOfBands, kAudioUnitScope_Global, 0, &eqBandCount, sizeof(eqBandCount))); - - for (int i = 0; i < eqBandCount; i++) - { - CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Frequency + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)self->options.equalizerBandFrequencies[i], 0)); - } - - for (int i = 0; i < eqBandCount; i++) - { - CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_BypassBand + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)0, 0)); - } + OSStatus status; + + if (self->options.equalizerBandFrequencies[0] == 0) + { + return; + } + + CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &nbandUnitDescription, &eqNode)); + CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, eqNode, NULL, &eqUnit)); + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice))); + + while (self->options.equalizerBandFrequencies[eqBandCount] != 0) + { + eqBandCount++; + } + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAUNBandEQProperty_NumberOfBands, kAudioUnitScope_Global, 0, &eqBandCount, sizeof(eqBandCount))); + + for (int i = 0; i < eqBandCount; i++) + { + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Frequency + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)self->options.equalizerBandFrequencies[i], 0)); + } + + for (int i = 0; i < eqBandCount; i++) + { + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_BypassBand + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)0, 0)); + } #endif } -(void) setGain:(float)gain forEqualizerBand:(int)bandIndex { - if (!eqUnit) - { - return; - } + if (!eqUnit) + { + return; + } OSStatus status; - - CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Gain + bandIndex, kAudioUnitScope_Global, 0, gain, 0)); + + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Gain + bandIndex, kAudioUnitScope_Global, 0, gain, 0)); } -(AUNode) createConverterNode:(AudioStreamBasicDescription)srcFormat desFormat:(AudioStreamBasicDescription)desFormat { - OSStatus status; - AUNode convertNode; - AudioComponentInstance convertUnit; - - CHECK_STATUS_AND_RETURN_VALUE(AUGraphAddNode(audioGraph, &convertUnitDescription, &convertNode), 0); - CHECK_STATUS_AND_RETURN_VALUE(AUGraphNodeInfo(audioGraph, convertNode, &mixerDescription, &convertUnit), 0); - CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat)), 0); - CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &desFormat, sizeof(desFormat)), 0); - CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)), 0); + OSStatus status; + AUNode convertNode; + AudioComponentInstance convertUnit; + + CHECK_STATUS_AND_RETURN_VALUE(AUGraphAddNode(audioGraph, &convertUnitDescription, &convertNode), 0); + CHECK_STATUS_AND_RETURN_VALUE(AUGraphNodeInfo(audioGraph, convertNode, &mixerDescription, &convertUnit), 0); + CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat)), 0); + CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &desFormat, sizeof(desFormat)), 0); + CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)), 0); [converterNodes addObject:@(convertNode)]; - - return convertNode; + + return convertNode; } -(void) connectNodes:(AUNode)srcNode desNode:(AUNode)desNode srcUnit:(AudioComponentInstance)srcUnit desUnit:(AudioComponentInstance)desUnit @@ -2275,46 +2378,46 @@ -(void) connectNodes:(AUNode)srcNode desNode:(AUNode)desNode srcUnit:(AudioCompo addConverter = status != 0; } - if (addConverter) + if (addConverter) { AUNode convertNode = [self createConverterNode:srcFormat desFormat:desFormat]; CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, convertNode, 0)); - CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, convertNode, 0, desNode, 0)); + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, convertNode, 0, desNode, 0)); } else { - CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, desNode, 0)); + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, desNode, 0)); } } -(void) setOutputCallbackForFirstNode:(AUNode)firstNode firstUnit:(AudioComponentInstance)firstUnit { - OSStatus status; - AURenderCallbackStruct callbackStruct; - + OSStatus status; + AURenderCallbackStruct callbackStruct; + callbackStruct.inputProc = OutputRenderCallback; callbackStruct.inputProcRefCon = (__bridge void*)self; - + status = AudioUnitSetProperty(firstUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); - - if (status) - { - AudioStreamBasicDescription format; - UInt32 size = sizeof(format); - - CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(firstUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, &size)); - - AUNode converterNode = [self createConverterNode:canonicalAudioStreamBasicDescription desFormat:format]; - + + if (status) + { + AudioStreamBasicDescription format; + UInt32 size = sizeof(format); + + CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(firstUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, &size)); + + AUNode converterNode = [self createConverterNode:canonicalAudioStreamBasicDescription desFormat:format]; + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, converterNode, 0, &callbackStruct)); - - CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, converterNode, 0, firstNode, 0)); - } - else - { - CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, firstNode, 0, &callbackStruct)); - } + + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, converterNode, 0, firstNode, 0)); + } + else + { + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, firstNode, 0, &callbackStruct)); + } } -(void) createAudioGraph @@ -2322,18 +2425,18 @@ -(void) createAudioGraph OSStatus status; converterNodes = [[NSMutableArray alloc] init]; - CHECK_STATUS_AND_RETURN(NewAUGraph(&audioGraph)); - CHECK_STATUS_AND_RETURN(AUGraphOpen(audioGraph)); - - [self createEqUnit]; - [self createMixerUnit]; - [self createOutputUnit]; + CHECK_STATUS_AND_RETURN(NewAUGraph(&audioGraph)); + CHECK_STATUS_AND_RETURN(AUGraphOpen(audioGraph)); + + [self createEqUnit]; + [self createMixerUnit]; + [self createOutputUnit]; [self connectGraph]; - - CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph)); - - self.volume = self->volume; + + CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph)); + + self.volume = self->volume; } -(void) connectGraph @@ -2350,7 +2453,7 @@ -(void) connectGraph } [converterNodes removeAllObjects]; - + if (eqNode) { if (self->equalizerEnabled) @@ -2364,7 +2467,7 @@ -(void) connectGraph { self->equalizerOn = NO; } - } + } else { self->equalizerOn = NO; @@ -2375,16 +2478,16 @@ -(void) connectGraph [nodes addObject:@(mixerNode)]; [units addObject:[NSValue valueWithPointer:mixerUnit]]; } - + if (outputNode) { [nodes addObject:@(outputNode)]; [units addObject:[NSValue valueWithPointer:outputUnit]]; } - [self setOutputCallbackForFirstNode:(AUNode)[[nodes objectAtIndex:0] intValue] firstUnit:(AudioComponentInstance)[[units objectAtIndex:0] pointerValue]]; + [self setOutputCallbackForFirstNode:(AUNode)[[nodes objectAtIndex:0] intValue] firstUnit:(AudioComponentInstance)[[units objectAtIndex:0] pointerValue]]; - for (int i = 0; i < nodes.count - 1; i++) + for (int i = 0; i < nodes.count - 1; i++) { AUNode srcNode = [[nodes objectAtIndex:i] intValue]; AUNode desNode = [[nodes objectAtIndex:i + 1] intValue]; @@ -2397,30 +2500,30 @@ -(void) connectGraph -(BOOL) audioGraphIsRunning { - OSStatus status; - Boolean isRunning; + OSStatus status; + Boolean isRunning; status = AUGraphIsRunning(audioGraph, &isRunning); - - if (status) - { - return NO; - } - - return isRunning; + + if (status) + { + return NO; + } + + return isRunning; } -(BOOL) startAudioGraph { OSStatus status; - [self resetPcmBuffers]; + [self resetPcmBuffers]; if ([self audioGraphIsRunning]) { return NO; } - + status = AUGraphStart(audioGraph); if (status) @@ -2429,17 +2532,17 @@ -(BOOL) startAudioGraph return NO; } - - [self stopSystemBackgroundTask]; - + + [self stopSystemBackgroundTask]; + return YES; } -(void) stopAudioUnitWithReason:(STKAudioPlayerStopReason)stopReasonIn { - OSStatus status; - - if (!audioGraph) + OSStatus status; + + if (!audioGraph) { stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; @@ -2459,12 +2562,12 @@ -(void) stopAudioUnitWithReason:(STKAudioPlayerStopReason)stopReasonIn { stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; - + return; } status = AUGraphStop(audioGraph); - + if (status) { [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; @@ -2493,12 +2596,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { ioNumberDataPackets = 0; - return 100; + return 100; } ioData->mNumberBuffers = 1; ioData->mBuffers[0] = convertInfo->audioBuffer; - + if (outDataPacketDescription) { *outDataPacketDescription = convertInfo->packetDescriptions; @@ -2521,11 +2624,11 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte { return; } - - if (disposeWasRequested) - { - return; - } + + if (disposeWasRequested) + { + return; + } if (audioConverterRef == nil) { @@ -2534,34 +2637,34 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte if ((seekToTimeWasRequested && [currentlyPlayingEntry calculatedBitRate] > 0.0)) { - [self wakeupPlaybackThread]; - + [self wakeupPlaybackThread]; + return; } - - discontinuous = NO; + + discontinuous = NO; OSStatus status; AudioConvertInfo convertInfo; - + convertInfo.done = NO; convertInfo.numberOfPackets = numberPackets; convertInfo.packetDescriptions = packetDescriptionsIn; convertInfo.audioBuffer.mData = (void *)inputData; convertInfo.audioBuffer.mDataByteSize = numberBytes; convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; - + if (packetDescriptionsIn && currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) { int count = MIN(numberPackets, STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION - currentlyReadingEntry->processedPacketsCount); for (int i = 0; i < count; i++) { - SInt64 packetSize; - - packetSize = packetDescriptionsIn[i].mDataByteSize; - + SInt64 packetSize; + + packetSize = packetDescriptionsIn[i].mDataByteSize; + OSAtomicAdd32((int32_t)packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); OSAtomicIncrement32(¤tlyReadingEntry->processedPacketsCount); } @@ -2588,7 +2691,7 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount; framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; OSSpinLockUnlock(&pcmBufferSpinLock); - + if (framesLeftInsideBuffer > 0) { break; @@ -2603,18 +2706,18 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte return; } - - if (seekToTimeWasRequested && [currentlyPlayingEntry calculatedBitRate] > 0.0) - { - pthread_mutex_unlock(&playerMutex); - - [self wakeupPlaybackThread]; - - return; - } + + if (seekToTimeWasRequested && [currentlyPlayingEntry calculatedBitRate] > 0.0) + { + pthread_mutex_unlock(&playerMutex); + + [self wakeupPlaybackThread]; + + return; + } waiting = YES; - + pthread_cond_wait(&playerThreadReadyCondition, &playerMutex); waiting = NO; @@ -2646,13 +2749,13 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte { [self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]]; } - + if (status == 100) { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); - + OSSpinLockLock(¤tlyReadingEntry->spinLock); currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(¤tlyReadingEntry->spinLock); @@ -2764,7 +2867,7 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte OSSpinLockLock(¤tlyReadingEntry->spinLock); currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(¤tlyReadingEntry->spinLock); - + continue; } else if (status != 0) @@ -2807,13 +2910,13 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio if (ioOutputDataPackets > 0) { OSStatus writeError = AudioFileWritePackets(recordAudioFileId, - NO, - convertedData.mBuffers[0].mDataByteSize, - recordPacketDescriptions, - recordFilePacketPosition, - &ioOutputDataPackets, - convertedData.mBuffers[0].mData); - + NO, + convertedData.mBuffers[0].mDataByteSize, + recordPacketDescriptions, + recordFilePacketPosition, + &ioOutputDataPackets, + convertedData.mBuffers[0].mData); + if (writeError) { NSLog(@"STKAudioPlayer:handleRecordingOfAudioPackets failed on AudioFileWritePackets with error \"" OSSTATUS_PRINTF_PLACEHOLDER "\"", OSSTATUS_PRINTF_VALUE(writeError)); @@ -2872,16 +2975,16 @@ - (void)fireRecordingErrorWithStatus:(OSStatus)status isInLock:(BOOL)locked static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; - + OSSpinLockLock(&audioPlayer->currentEntryReferencesLock); - STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; + STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; STKQueueEntry* currentlyReadingEntry = audioPlayer->currentlyReadingEntry; OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock); OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); BOOL waitForBuffer = NO; - BOOL muted = audioPlayer->muted; + BOOL muted = audioPlayer->muted; AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; UInt32 used = audioPlayer->pcmBufferUsedFrameCount; @@ -2889,54 +2992,54 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* STKAudioPlayerInternalState state = audioPlayer->internalState; UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; - NSArray* frameFilters = audioPlayer->frameFilters; - - if (entry) - { - if (state == STKAudioPlayerInternalStateWaitingForData) - { - SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; - - if (entry->lastFrameQueued >= 0) - { - framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued); - } - - if (entry && currentlyReadingEntry == entry - && entry->framesQueued < framesRequiredToStartPlaying) - { - waitForBuffer = YES; - } - } - else if (state == STKAudioPlayerInternalStateRebuffering) - { - SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToPlayAfterRebuffering; - - if (entry->lastFrameQueued >= 0) - { - framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued); - } - - if (used < framesRequiredToStartPlaying) - { - waitForBuffer = YES; - } - } - else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek) - { - SInt64 framesRequiredToStartPlaying = 1024; - - if (entry->lastFrameQueued >= 0) - { - framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued); - } - - if (used < framesRequiredToStartPlaying) - { - waitForBuffer = YES; - } - } - } + NSArray* frameFilters = audioPlayer->frameFilters; + + if (entry) + { + if (state == STKAudioPlayerInternalStateWaitingForData) + { + SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + + if (entry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued); + } + + if (entry && currentlyReadingEntry == entry + && entry->framesQueued < framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + } + else if (state == STKAudioPlayerInternalStateRebuffering) + { + SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToPlayAfterRebuffering; + + if (entry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued); + } + + if (used < framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + } + else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek) + { + SInt64 framesRequiredToStartPlaying = 1024; + + if (entry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued); + } + + if (used < framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + } + } OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); @@ -2959,15 +3062,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - - if (muted) - { - memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); - } - else - { - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - } + + if (muted) + { + memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); + } + else + { + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + } totalFramesCopied = framesToCopy; @@ -2982,15 +3085,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - - if (muted) - { - memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); - } - else - { - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - } + + if (muted) + { + memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); + } + else + { + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + } UInt32 moreFramesToCopy = 0; UInt32 delta = inNumberFrames - framesToCopy; @@ -3001,15 +3104,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; - - if (muted) - { - memset(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), 0, frameSizeInBytes * moreFramesToCopy); - } - else - { - memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); - } + + if (muted) + { + memset(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), 0, frameSizeInBytes * moreFramesToCopy); + } + else + { + memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + } } totalFramesCopied = framesToCopy + moreFramesToCopy; @@ -3021,9 +3124,9 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* } [audioPlayer setInternalState:STKAudioPlayerInternalStatePlaying ifInState:^BOOL(STKAudioPlayerInternalState state) - { - return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused; - }]; + { + return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused; + }]; } if (totalFramesCopied < inNumberFrames) @@ -3037,43 +3140,43 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* // Buffering [audioPlayer setInternalState:STKAudioPlayerInternalStateRebuffering ifInState:^BOOL(STKAudioPlayerInternalState state) - { + { return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused; - }]; - } - else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek) - { - if (totalFramesCopied == 0) - { - OSAtomicAdd32(inNumberFrames - totalFramesCopied, &audioPlayer->waitingForDataAfterSeekFrameCount); - - if (audioPlayer->waitingForDataAfterSeekFrameCount > audioPlayer->framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying) - { - [audioPlayer setInternalState:STKAudioPlayerInternalStatePlaying ifInState:^BOOL(STKAudioPlayerInternalState state) - { - return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused; - }]; - } - } - else - { - audioPlayer->waitingForDataAfterSeekFrameCount = 0; - } - } - } - - if (frameFilters) - { - NSUInteger count = frameFilters.count; - AudioStreamBasicDescription asbd = canonicalAudioStreamBasicDescription; - - for (int i = 0; i < count; i++) - { - STKFrameFilterEntry* entry = [frameFilters objectAtIndex:i]; - - entry->filter(asbd.mChannelsPerFrame, asbd.mBytesPerFrame, inNumberFrames, ioData->mBuffers[0].mData); - } - } + }]; + } + else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek) + { + if (totalFramesCopied == 0) + { + OSAtomicAdd32(inNumberFrames - totalFramesCopied, &audioPlayer->waitingForDataAfterSeekFrameCount); + + if (audioPlayer->waitingForDataAfterSeekFrameCount > audioPlayer->framesRequiredBeforeWaitingForDataAfterSeekBecomesPlaying) + { + [audioPlayer setInternalState:STKAudioPlayerInternalStatePlaying ifInState:^BOOL(STKAudioPlayerInternalState state) + { + return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused; + }]; + } + } + else + { + audioPlayer->waitingForDataAfterSeekFrameCount = 0; + } + } + } + + if (frameFilters) + { + NSUInteger count = frameFilters.count; + AudioStreamBasicDescription asbd = canonicalAudioStreamBasicDescription; + + for (int i = 0; i < count; i++) + { + STKFrameFilterEntry* entry = [frameFilters objectAtIndex:i]; + + entry->filter(asbd.mChannelsPerFrame, asbd.mBytesPerFrame, inNumberFrames, ioData->mBuffers[0].mData); + } + } if (audioPlayer->equalizerEnabled != audioPlayer->equalizerOn) { @@ -3085,17 +3188,17 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* isUpdated = isUpdated; } - + if (entry == nil) { return 0; } OSSpinLockLock(&entry->spinLock); - + SInt64 extraFramesPlayedNotAssigned = 0; SInt64 framesPlayedForCurrent = totalFramesCopied; - + if (entry->lastFrameQueued >= 0) { framesPlayedForCurrent = MIN(entry->lastFrameQueued - entry->framesPlayed, framesPlayedForCurrent); @@ -3105,7 +3208,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* extraFramesPlayedNotAssigned = totalFramesCopied - framesPlayedForCurrent; BOOL lastFramePlayed = entry->framesPlayed == entry->lastFrameQueued; - + OSSpinLockUnlock(&entry->spinLock); if (signal || lastFramePlayed) @@ -3115,7 +3218,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* OSSpinLockLock(&audioPlayer->currentEntryReferencesLock); STKQueueEntry* currentlyPlayingEntry = audioPlayer->currentlyPlayingEntry; OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock); - + if (lastFramePlayed && entry == currentlyPlayingEntry) { [audioPlayer audioQueueFinishedPlaying:entry]; @@ -3152,13 +3255,13 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* extraFramesPlayedNotAssigned -= framesPlayedForCurrent; } - else - { - break; - } + else + { + break; + } } } - + pthread_cond_signal(&audioPlayer->playerThreadReadyCondition); pthread_mutex_unlock(&audioPlayer->playerMutex); } @@ -3168,11 +3271,11 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* -(NSArray*) pendingQueue { - pthread_mutex_lock(&playerMutex); - - NSArray* retval; - NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:upcomingQueue.count + bufferingQueue.count]; - + pthread_mutex_lock(&playerMutex); + + NSArray* retval; + NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:upcomingQueue.count + bufferingQueue.count]; + for (STKQueueEntry* entry in upcomingQueue) { [mutableArray addObject:[entry queueItemId]]; @@ -3183,300 +3286,300 @@ -(NSArray*) pendingQueue [mutableArray addObject:[entry queueItemId]]; } - retval = [NSArray arrayWithArray:mutableArray]; - - pthread_mutex_unlock(&playerMutex); - - return retval; + retval = [NSArray arrayWithArray:mutableArray]; + + pthread_mutex_unlock(&playerMutex); + + return retval; } -(NSUInteger) pendingQueueCount { - pthread_mutex_lock(&playerMutex); - - NSUInteger retval = upcomingQueue.count + bufferingQueue.count; - - pthread_mutex_unlock(&playerMutex); - - return retval; + pthread_mutex_lock(&playerMutex); + + NSUInteger retval = upcomingQueue.count + bufferingQueue.count; + + pthread_mutex_unlock(&playerMutex); + + return retval; } -(NSObject*) mostRecentlyQueuedStillPendingItem { - pthread_mutex_lock(&playerMutex); - - if (upcomingQueue.count > 0) - { - NSObject* retval = [[upcomingQueue objectAtIndex:0] queueItemId]; - - pthread_mutex_unlock(&playerMutex); - - return retval; - } - - if (bufferingQueue.count > 0) - { - NSObject* retval = [[bufferingQueue objectAtIndex:0] queueItemId]; - - pthread_mutex_unlock(&playerMutex); - - return retval; - } - - pthread_mutex_unlock(&playerMutex); - - return nil; + pthread_mutex_lock(&playerMutex); + + if (upcomingQueue.count > 0) + { + NSObject* retval = [[upcomingQueue objectAtIndex:0] queueItemId]; + + pthread_mutex_unlock(&playerMutex); + + return retval; + } + + if (bufferingQueue.count > 0) + { + NSObject* retval = [[bufferingQueue objectAtIndex:0] queueItemId]; + + pthread_mutex_unlock(&playerMutex); + + return retval; + } + + pthread_mutex_unlock(&playerMutex); + + return nil; } -(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber { - if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) - { - return 0; - } - - return peakPowerDb[channelNumber]; + if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) + { + return 0; + } + + return peakPowerDb[channelNumber]; } -(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber { - if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) - { - return 0; - } - - return averagePowerDb[channelNumber]; + if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) + { + return 0; + } + + return averagePowerDb[channelNumber]; } -(BOOL) meteringEnabled { - return self->meteringEnabled; + return self->meteringEnabled; } #define CALCULATE_METER(channel) \ - Float32 currentFilteredValueOfSampleAmplitude##channel = STK_LOWPASSFILTERTIMESLICE * absoluteValueOfSampleAmplitude##channel + (1.0 - STK_LOWPASSFILTERTIMESLICE) * previousFilteredValueOfSampleAmplitude##channel; \ - previousFilteredValueOfSampleAmplitude##channel = currentFilteredValueOfSampleAmplitude##channel; \ - Float32 sampleDB##channel = 20.0 * log10(currentFilteredValueOfSampleAmplitude##channel) + STK_DBOFFSET; \ - if ((sampleDB##channel == sampleDB##channel) && (sampleDB##channel != -DBL_MAX)) \ - { \ - if(sampleDB##channel > peakValue##channel) \ - { \ - peakValue##channel = sampleDB##channel; \ - } \ - if (sampleDB##channel > -DBL_MAX) \ - { \ - count##channel++; \ - totalValue##channel += sampleDB##channel; \ - } \ - decibels##channel = peakValue##channel; \ - }; +Float32 currentFilteredValueOfSampleAmplitude##channel = STK_LOWPASSFILTERTIMESLICE * absoluteValueOfSampleAmplitude##channel + (1.0 - STK_LOWPASSFILTERTIMESLICE) * previousFilteredValueOfSampleAmplitude##channel; \ +previousFilteredValueOfSampleAmplitude##channel = currentFilteredValueOfSampleAmplitude##channel; \ +Float32 sampleDB##channel = 20.0 * log10(currentFilteredValueOfSampleAmplitude##channel) + STK_DBOFFSET; \ +if ((sampleDB##channel == sampleDB##channel) && (sampleDB##channel != -DBL_MAX)) \ +{ \ +if(sampleDB##channel > peakValue##channel) \ +{ \ +peakValue##channel = sampleDB##channel; \ +} \ +if (sampleDB##channel > -DBL_MAX) \ +{ \ +count##channel++; \ +totalValue##channel += sampleDB##channel; \ +} \ +decibels##channel = peakValue##channel; \ +}; -(void) setMeteringEnabled:(BOOL)value { - if (self->meteringEnabled == value) - { - return; - } - - if (!value) - { - [self removeFrameFilterWithName:@"STKMeteringFilter"]; - self->meteringEnabled = NO; - } - else - { - [self appendFrameFilterWithName:@"STKMeteringFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames) - { - SInt16* samples16 = (SInt16*)frames; - SInt32* samples32 = (SInt32*)frames; - UInt32 countLeft = 0; - UInt32 countRight = 0; - Float32 decibelsLeft = STK_DBMIN; - Float32 peakValueLeft = STK_DBMIN; - Float64 totalValueLeft = 0; - Float32 previousFilteredValueOfSampleAmplitudeLeft = 0; - Float32 decibelsRight = STK_DBMIN; - Float32 peakValueRight = STK_DBMIN; - Float64 totalValueRight = 0; - Float32 previousFilteredValueOfSampleAmplitudeRight = 0; - - if (bytesPerFrame / channelsPerFrame == 2) - { - for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) - { - Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples16[i]); - Float32 absoluteValueOfSampleAmplitudeRight = abs(samples16[i + 1]); - - CALCULATE_METER(Left); - CALCULATE_METER(Right); - } - } - else if (bytesPerFrame / channelsPerFrame == 4) - { - for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) - { - Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples32[i]) / 32768.0; - Float32 absoluteValueOfSampleAmplitudeRight = abs(samples32[i + 1]) / 32768.0; - - CALCULATE_METER(Left); - CALCULATE_METER(Right); - } - } - else - { - return; - } - - peakPowerDb[0] = MIN(MAX(decibelsLeft, -60), 0); - peakPowerDb[1] = MIN(MAX(decibelsRight, -60), 0); - - if (countLeft > 0) - { - averagePowerDb[0] = MIN(MAX(totalValueLeft / frameCount, -60), 0); - } - - if (countRight != 0) - { - averagePowerDb[1] = MIN(MAX(totalValueRight / frameCount, -60), 0); - } - }]; - } + if (self->meteringEnabled == value) + { + return; + } + + if (!value) + { + [self removeFrameFilterWithName:@"STKMeteringFilter"]; + self->meteringEnabled = NO; + } + else + { + [self appendFrameFilterWithName:@"STKMeteringFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames) + { + SInt16* samples16 = (SInt16*)frames; + SInt32* samples32 = (SInt32*)frames; + UInt32 countLeft = 0; + UInt32 countRight = 0; + Float32 decibelsLeft = STK_DBMIN; + Float32 peakValueLeft = STK_DBMIN; + Float64 totalValueLeft = 0; + Float32 previousFilteredValueOfSampleAmplitudeLeft = 0; + Float32 decibelsRight = STK_DBMIN; + Float32 peakValueRight = STK_DBMIN; + Float64 totalValueRight = 0; + Float32 previousFilteredValueOfSampleAmplitudeRight = 0; + + if (bytesPerFrame / channelsPerFrame == 2) + { + for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) + { + Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples16[i]); + Float32 absoluteValueOfSampleAmplitudeRight = abs(samples16[i + 1]); + + CALCULATE_METER(Left); + CALCULATE_METER(Right); + } + } + else if (bytesPerFrame / channelsPerFrame == 4) + { + for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) + { + Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples32[i]) / 32768.0; + Float32 absoluteValueOfSampleAmplitudeRight = abs(samples32[i + 1]) / 32768.0; + + CALCULATE_METER(Left); + CALCULATE_METER(Right); + } + } + else + { + return; + } + + peakPowerDb[0] = MIN(MAX(decibelsLeft, -60), 0); + peakPowerDb[1] = MIN(MAX(decibelsRight, -60), 0); + + if (countLeft > 0) + { + averagePowerDb[0] = MIN(MAX(totalValueLeft / frameCount, -60), 0); + } + + if (countRight != 0) + { + averagePowerDb[1] = MIN(MAX(totalValueRight / frameCount, -60), 0); + } + }]; + } } #pragma mark Frame Filters -(NSArray*) frameFilters { - return frameFilters; + return frameFilters; } -(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block { - [self addFrameFilterWithName:name afterFilterWithName:nil block:block]; + [self addFrameFilterWithName:name afterFilterWithName:nil block:block]; } -(void) removeFrameFilterWithName:(NSString*)name { - pthread_mutex_lock(&self->playerMutex); - - NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; - - for (STKFrameFilterEntry* filterEntry in frameFilters) - { - if (![filterEntry->name isEqualToString:name]) - { - [newFrameFilters addObject:filterEntry]; - } - } - - NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; - - OSSpinLockLock(&pcmBufferSpinLock); - if (newFrameFilters.count > 0) - { - frameFilters = replacement; - } - else - { - frameFilters = nil; - } - OSSpinLockUnlock(&pcmBufferSpinLock); - - pthread_mutex_unlock(&self->playerMutex); + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + if (![filterEntry->name isEqualToString:name]) + { + [newFrameFilters addObject:filterEntry]; + } + } + + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + + OSSpinLockLock(&pcmBufferSpinLock); + if (newFrameFilters.count > 0) + { + frameFilters = replacement; + } + else + { + frameFilters = nil; + } + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); } -(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block { - pthread_mutex_lock(&self->playerMutex); - - NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; - - if (afterFilterWithName == nil) - { - [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; - [newFrameFilters addObjectsFromArray:frameFilters]; - } - else - { - for (STKFrameFilterEntry* filterEntry in frameFilters) - { - if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) - { - [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; - } - - [newFrameFilters addObject:filterEntry]; - } - } - - NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; - - OSSpinLockLock(&pcmBufferSpinLock); - frameFilters = replacement; - OSSpinLockUnlock(&pcmBufferSpinLock); - - pthread_mutex_unlock(&self->playerMutex); + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + if (afterFilterWithName == nil) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; + [newFrameFilters addObjectsFromArray:frameFilters]; + } + else + { + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; + } + + [newFrameFilters addObject:filterEntry]; + } + } + + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + + OSSpinLockLock(&pcmBufferSpinLock); + frameFilters = replacement; + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); } -(void) addFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName { - pthread_mutex_lock(&self->playerMutex); - - NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; - - if (afterFilterWithName == nil) - { - [newFrameFilters addObjectsFromArray:frameFilters]; - [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; - } - else - { - for (STKFrameFilterEntry* filterEntry in frameFilters) - { - [newFrameFilters addObject:filterEntry]; - - if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) - { - [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; - } - } - } - - NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; - - OSSpinLockLock(&pcmBufferSpinLock); - frameFilters = replacement; - OSSpinLockUnlock(&pcmBufferSpinLock); - - pthread_mutex_unlock(&self->playerMutex); + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + if (afterFilterWithName == nil) + { + [newFrameFilters addObjectsFromArray:frameFilters]; + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; + } + else + { + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + [newFrameFilters addObject:filterEntry]; + + if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; + } + } + } + + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + + OSSpinLockLock(&pcmBufferSpinLock); + frameFilters = replacement; + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); } #pragma mark Volume -(void) setVolume:(Float32)value; { - self->volume = value; - + self->volume = value; + #if (TARGET_OS_IPHONE) - if (self->mixerNode) - { - AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0); - } + if (self->mixerNode) + { + AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0); + } #else - if (self->mixerNode) - { - AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0); - } - else - { - AudioUnitSetParameter(outputUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, kOutputBus, self->volume, 0); - } + if (self->mixerNode) + { + AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0); + } + else + { + AudioUnitSetParameter(outputUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, kOutputBus, self->volume, 0); + } #endif } -(Float32) volume { - return self->volume; + return self->volume; } -(BOOL) equalizerEnabled @@ -3489,5 +3592,4 @@ -(void) setEqualizerEnabled:(BOOL)value self->equalizerEnabled = value; } - @end From 31a56e7d49438cda95dea3b9cae1fa914de1b6a7 Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Mon, 24 Oct 2016 19:07:20 +0300 Subject: [PATCH 13/15] Dispatch clearQueue completion on mainThread not on playbackThread. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 44 ++++++++++------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index baf7f48b..7642d9e6 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -685,7 +685,7 @@ -(void) clearQueue [self clearQueueWithCompletion:NULL]; } --(void) clearQueueWithCompletion:(void (^)())completionBlock +-(void) clearQueueWithCompletion:( void (^ _Nullable )())completionBlock { pthread_mutex_lock(&playerMutex); { @@ -706,8 +706,7 @@ -(void) clearQueueWithCompletion:(void (^)())completionBlock [upcomingQueue removeAllObjects]; [bufferingQueue removeAllObjects]; - if ((array.count > 0 && [self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) - || completionBlock) + if ((array.count > 0 && [self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)])) { [self playbackThreadQueueMainThreadSyncBlock:^ { @@ -716,11 +715,6 @@ -(void) clearQueueWithCompletion:(void (^)())completionBlock [self.delegate audioPlayer:self didCancelQueuedItems:array]; } - if (completionBlock) - { - completionBlock(); - } - }]; } } @@ -728,19 +722,19 @@ -(void) clearQueueWithCompletion:(void (^)())completionBlock { [bufferingQueue removeAllObjects]; [upcomingQueue removeAllObjects]; - - if (completionBlock) { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - completionBlock(); - }]; - } + } + + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock(); + }); } } pthread_mutex_unlock(&playerMutex); } --(void) dequeueURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock +-(void) dequeueURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock { pthread_mutex_lock(&playerMutex); { @@ -773,16 +767,16 @@ -(void) dequeueURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))co if (completionBlock) { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - completionBlock([resultQueue copy]); - }]; + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock([resultQueue copy]); + }); } } pthread_mutex_unlock(&playerMutex); } --(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock +-(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock { pthread_mutex_lock(&playerMutex); { @@ -815,10 +809,10 @@ -(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^)(NSArray *r if (completionBlock) { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - completionBlock([resultQueue copy]); - }]; + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock([resultQueue copy]); + }); } } pthread_mutex_unlock(&playerMutex); From 871554b366efc09965003d43bfeb27fda71b7bfe Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Mon, 24 Oct 2016 19:11:24 +0300 Subject: [PATCH 14/15] Nullability declared in h file. --- StreamingKit/StreamingKit/STKAudioPlayer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 1b3a6944..9dddd499 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -229,13 +229,13 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn /// The didCancelItems event will be raised for the items removed from the queue. -(void) clearQueue; --(void) clearQueueWithCompletion:(void (^)())completionBlock; +-(void) clearQueueWithCompletion:(void (^_Nullable)())completionBlock; /// Dequeues the URL for playback and uses the NSURL as the queueItemID --(void) dequeueURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock; +-(void) dequeueURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock; /// Dequeues all items the URL for playback and uses the NSURL as the queueItemID --(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^)(NSArray *resultQueue))completionBlock; +-(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock; /// Pauses playback -(void) pause; From 56788e2c5a5314b15e6e232e73116172854dfbde Mon Sep 17 00:00:00 2001 From: Nikita Borisov Date: Tue, 25 Oct 2016 16:52:17 +0300 Subject: [PATCH 15/15] Call completion after unlock mutex. --- StreamingKit/StreamingKit/STKAudioPlayer.m | 134 ++++++++++----------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 7642d9e6..26770576 100755 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -723,99 +723,99 @@ -(void) clearQueueWithCompletion:( void (^ _Nullable )())completionBlock [bufferingQueue removeAllObjects]; [upcomingQueue removeAllObjects]; } - - if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), ^ - { - completionBlock(); - }); - } } pthread_mutex_unlock(&playerMutex); + + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock(); + }); + } } -(void) dequeueURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock { pthread_mutex_lock(&playerMutex); + + NSMutableArray* resultQueue = [NSMutableArray array]; + NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; + + for (int i = 0; i < upcomingQueue.count; i++) { - NSMutableArray* resultQueue = [NSMutableArray array]; - NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; - - for (int i = 0; i < upcomingQueue.count; i++) - { - STKQueueEntry* entry = upcomingCopy[i]; - - if ([entry.queueItemId isEqual:url]) { - [upcomingQueue removeObjectAtIndex:i]; - } else { - [resultQueue addObject:entry.queueItemId]; - } - } - - NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + STKQueueEntry* entry = upcomingCopy[i]; - for (int i = 0; i < bufferingQueue.count; i++) - { - STKQueueEntry* entry = bufferingCopy[i]; - - if ([entry.queueItemId isEqual:url]) { - [bufferingQueue removeObjectAtIndex:i]; - } else { - [resultQueue addObject:entry.queueItemId]; - } + if ([entry.queueItemId isEqual:url]) { + [upcomingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; } + } + + NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + + for (int i = 0; i < bufferingQueue.count; i++) + { + STKQueueEntry* entry = bufferingCopy[i]; - if (completionBlock) - { - dispatch_async(dispatch_get_main_queue(), ^ - { - completionBlock([resultQueue copy]); - }); + if ([entry.queueItemId isEqual:url]) { + [bufferingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; } } + pthread_mutex_unlock(&playerMutex); + + if (completionBlock) + { + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock([resultQueue copy]); + }); + } } -(void) dequeueAllItemsExceptURL:(NSURL*)url withCompletion:(void (^_Nullable)(NSArray *resultQueue))completionBlock { pthread_mutex_lock(&playerMutex); + + NSMutableArray* resultQueue = [NSMutableArray array]; + NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; + + for (int i = 0; i < upcomingQueue.count; i++) { - NSMutableArray* resultQueue = [NSMutableArray array]; - NSMutableArray* upcomingCopy = [upcomingQueue mutableCopy]; - - for (int i = 0; i < upcomingQueue.count; i++) - { - STKQueueEntry* entry = upcomingCopy[i]; - - if (![entry.queueItemId isEqual:url]) { - [upcomingQueue removeObjectAtIndex:i]; - } else { - [resultQueue addObject:entry.queueItemId]; - } - } - - NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + STKQueueEntry* entry = upcomingCopy[i]; - for (int i = 0; i < bufferingQueue.count; i++) - { - STKQueueEntry* entry = bufferingCopy[i]; - - if (![entry.queueItemId isEqual:url]) { - [bufferingQueue removeObjectAtIndex:i]; - } else { - [resultQueue addObject:entry.queueItemId]; - } + if (![entry.queueItemId isEqual:url]) { + [upcomingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; } + } + + NSMutableArray* bufferingCopy = [bufferingQueue mutableCopy]; + + for (int i = 0; i < bufferingQueue.count; i++) + { + STKQueueEntry* entry = bufferingCopy[i]; - if (completionBlock) - { - dispatch_async(dispatch_get_main_queue(), ^ - { - completionBlock([resultQueue copy]); - }); + if (![entry.queueItemId isEqual:url]) { + [bufferingQueue removeObjectAtIndex:i]; + } else { + [resultQueue addObject:entry.queueItemId]; } } + pthread_mutex_unlock(&playerMutex); + + if (completionBlock) + { + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock([resultQueue copy]); + }); + } } -(void) play:(NSString*)urlString