diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index a1ff3b53..9dddd499 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; @@ -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 @@ -134,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 @@ -227,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 (^_Nullable)())completionBlock; + +/// Dequeues the URL for playback and uses the NSURL as the queueItemID +-(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 (^_Nullable)(NSArray *resultQueue))completionBlock; + /// Pauses playback -(void) pause; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 46affb4f..26770576 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 (^ _Nullable )())completionBlock { pthread_mutex_lock(&playerMutex); { @@ -692,24 +697,25 @@ -(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:)])) { [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]; + } + + }]; } } else @@ -719,38 +725,129 @@ -(void) clearQueue } } 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++) + { + 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]; + } + } + + 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++) + { + 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]; + } + } + + pthread_mutex_unlock(&playerMutex); + + if (completionBlock) + { + dispatch_async(dispatch_get_main_queue(), ^ + { + completionBlock([resultQueue copy]); + }); + } } -(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 +856,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 +874,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 +910,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 +934,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 +944,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 +960,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 +1002,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 +1039,7 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE || pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2) { currentlyReadingEntry->audioStreamBasicDescription = pasbd; - + break; } } @@ -951,7 +1048,7 @@ -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream f break; } - + } } @@ -970,9 +1067,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 +1081,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 +1123,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 +1144,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 +1215,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 +1298,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 +1308,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 +1333,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 +1378,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 +1398,7 @@ -(void) requeueBufferingEntries [queueEntry reset]; } - + [upcomingQueue skipQueueWithQueue:bufferingQueue]; [bufferingQueue removeAllObjects]; @@ -1381,7 +1478,7 @@ -(BOOL) processRunloop { int32_t originalSeekVersion; BOOL originalSeekToTimeRequested; - + OSSpinLockLock(&seekLock); originalSeekVersion = seekVersion; originalSeekToTimeRequested = seekToTimeWasRequested; @@ -1411,18 +1508,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 +1530,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 +1619,8 @@ -(void) processSeekToTime [currentEntry reset]; [currentEntry.dataSource seekToOffset:seekByteOffset]; - self->waitingForDataAfterSeekFrameCount = 0; - + self->waitingForDataAfterSeekFrameCount = 0; + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; if (audioGraph) @@ -1534,7 +1631,7 @@ -(void) processSeekToTime -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn { - OSStatus error; + OSStatus error; if (currentlyReadingEntry.dataSource != dataSourceIn) { @@ -1627,7 +1724,7 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn dataSourceIn.delegate = nil; [dataSourceIn unregisterForEvents]; [dataSourceIn close]; - + return; } @@ -1641,10 +1738,11 @@ -(void) dataSourceEof:(STKDataSource*)dataSourceIn [self closeRecordAudioFile]; [self dispatchSyncOnMainThread:^ - { - [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; - }]; - + { + BOOL fullyProcessed = currentlyReadingEntry.dataSource.length == [currentlyReadingEntry.dataSource bytesRead]; + [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId fullyProcessed:fullyProcessed]; + }]; + pthread_mutex_lock(&playerMutex); if (currentlyReadingEntry == nil) @@ -1677,7 +1775,7 @@ -(void) pause { pthread_mutex_lock(&playerMutex); { - OSStatus error; + OSStatus error; if (self.internalState != STKAudioPlayerInternalStatePaused && (self.internalState & STKAudioPlayerInternalStateRunning)) { @@ -1708,8 +1806,8 @@ -(void) resume { pthread_mutex_lock(&playerMutex); { - OSStatus error; - + OSStatus error; + if (self.internalState == STKAudioPlayerInternalStatePaused) { self.internalState = self.stateBeforePaused; @@ -1718,10 +1816,10 @@ -(void) resume { [self resetPcmBuffers]; } - + if (audioGraph != nil) { - error = AUGraphStart(audioGraph); + error = AUGraphStart(audioGraph); if (error) { @@ -1745,10 +1843,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); } @@ -1767,34 +1865,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); } @@ -1810,17 +1908,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); @@ -1832,22 +1930,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 @@ -1864,14 +1962,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) @@ -1919,12 +2025,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]; @@ -1974,10 +2080,11 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd return; } - + [self destroyAudioConverter]; BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil; + OSStatus didFailToRecordStatus = 0; if (isRecording) { @@ -2018,20 +2125,24 @@ -(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"); } } - + 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 != kAudioFileMP3Type) { status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); - + if (status) { return; @@ -2114,40 +2225,42 @@ -(void) createAudioConverter:(AudioStreamBasicDescription*)asbd } recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize); - - error = AudioFileCreateWithURL( - (__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl), - kAudioFileCAFType, - &recordAudioStreamBasicDescription, - kAudioFileFlags_EraseFile, - &recordAudioFileId); + + didFailToRecordStatus = AudioFileCreateWithURL( + (__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl), + kAudioFileCAFType, + &recordAudioStreamBasicDescription, + kAudioFileFlags_EraseFile, + &recordAudioFileId); recordFilePacketPosition = 0; - if (error) + if (didFailToRecordStatus) { NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl); - - [self closeRecordAudioFile]; } } + + if (didFailToRecordStatus) { + [self fireRecordingErrorWithStatus:didFailToRecordStatus isInLock:YES]; + } } -(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))); + + 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))); @@ -2155,25 +2268,25 @@ -(void) createOutputUnit -(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 @@ -2181,63 +2294,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 @@ -2259,46 +2372,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 @@ -2306,18 +2419,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 @@ -2334,7 +2447,7 @@ -(void) connectGraph } [converterNodes removeAllObjects]; - + if (eqNode) { if (self->equalizerEnabled) @@ -2348,7 +2461,7 @@ -(void) connectGraph { self->equalizerOn = NO; } - } + } else { self->equalizerOn = NO; @@ -2359,16 +2472,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]; @@ -2381,30 +2494,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) @@ -2413,17 +2526,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; @@ -2443,12 +2556,12 @@ -(void) stopAudioUnitWithReason:(STKAudioPlayerStopReason)stopReasonIn { stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; - + return; } status = AUGraphStop(audioGraph); - + if (status) { [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; @@ -2477,12 +2590,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; @@ -2505,11 +2618,11 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte { return; } - - if (disposeWasRequested) - { - return; - } + + if (disposeWasRequested) + { + return; + } if (audioConverterRef == nil) { @@ -2518,34 +2631,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); } @@ -2572,7 +2685,7 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount; framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; OSSpinLockUnlock(&pcmBufferSpinLock); - + if (framesLeftInsideBuffer > 0) { break; @@ -2587,18 +2700,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; @@ -2630,13 +2743,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); @@ -2748,7 +2861,7 @@ -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberByte OSSpinLockLock(¤tlyReadingEntry->spinLock); currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(¤tlyReadingEntry->spinLock); - + continue; } else if (status != 0) @@ -2791,16 +2904,29 @@ - (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)); + + /** + * 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. + */ + + NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); + [self fireRecordingErrorWithStatus:status isInLock:NO]; + break; } else { @@ -2811,6 +2937,8 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio else { NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion"); + [self fireRecordingErrorWithStatus:status isInLock:NO]; + break; } if (status == 100) @@ -2821,19 +2949,36 @@ - (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(Audio } } +- (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]; + } +} + 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; @@ -2841,54 +2986,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); @@ -2911,15 +3056,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; @@ -2934,15 +3079,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; @@ -2953,15 +3098,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; @@ -2973,9 +3118,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) @@ -2989,43 +3134,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) { @@ -3037,17 +3182,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); @@ -3057,7 +3202,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* extraFramesPlayedNotAssigned = totalFramesCopied - framesPlayedForCurrent; BOOL lastFramePlayed = entry->framesPlayed == entry->lastFrameQueued; - + OSSpinLockUnlock(&entry->spinLock); if (signal || lastFramePlayed) @@ -3067,7 +3212,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* OSSpinLockLock(&audioPlayer->currentEntryReferencesLock); STKQueueEntry* currentlyPlayingEntry = audioPlayer->currentlyPlayingEntry; OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock); - + if (lastFramePlayed && entry == currentlyPlayingEntry) { [audioPlayer audioQueueFinishedPlaying:entry]; @@ -3104,13 +3249,13 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* extraFramesPlayedNotAssigned -= framesPlayedForCurrent; } - else - { - break; - } + else + { + break; + } } } - + pthread_cond_signal(&audioPlayer->playerThreadReadyCondition); pthread_mutex_unlock(&audioPlayer->playerMutex); } @@ -3120,11 +3265,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]]; @@ -3135,300 +3280,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 @@ -3441,5 +3586,4 @@ -(void) setEqualizerEnabled:(BOOL)value self->equalizerEnabled = value; } - @end 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 0a1e7168..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; 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];