diff --git a/ExampleApp/ExampleApp.xcodeproj/project.pbxproj b/ExampleApp/ExampleApp.xcodeproj/project.pbxproj index a04c284..bad1390 100644 --- a/ExampleApp/ExampleApp.xcodeproj/project.pbxproj +++ b/ExampleApp/ExampleApp.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ A1115967188D6AEE00641365 /* AudioPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1115966188D6AEE00641365 /* AudioPlayerView.m */; }; A111596C188D6C8100641365 /* sample.m4a in Resources */ = {isa = PBXBuildFile; fileRef = A111596B188D6C8100641365 /* sample.m4a */; }; A111596F188D6DB100641365 /* SampleQueueId.m in Sources */ = {isa = PBXBuildFile; fileRef = A111596E188D6DB100641365 /* SampleQueueId.m */; }; + A142571D189079BE005F0129 /* airplane.aac in Resources */ = {isa = PBXBuildFile; fileRef = A142571C18907861005F0129 /* airplane.aac */; }; A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; }; /* End PBXBuildFile section */ @@ -59,6 +60,7 @@ A111596B188D6C8100641365 /* sample.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = sample.m4a; path = Resources/sample.m4a; sourceTree = ""; }; A111596D188D6DB100641365 /* SampleQueueId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleQueueId.h; sourceTree = ""; }; A111596E188D6DB100641365 /* SampleQueueId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleQueueId.m; sourceTree = ""; }; + A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = ""; }; A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -168,6 +170,7 @@ A111596A188D6C4B00641365 /* Resources */ = { isa = PBXGroup; children = ( + A142571C18907861005F0129 /* airplane.aac */, A111596B188D6C8100641365 /* sample.m4a */, ); name = Resources; @@ -251,6 +254,7 @@ A111593F188D686000641365 /* InfoPlist.strings in Resources */, A111596C188D6C8100641365 /* sample.m4a in Resources */, A1115947188D686000641365 /* Images.xcassets in Resources */, + A142571D189079BE005F0129 /* airplane.aac in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 37823d2..2cc4c01 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -54,18 +54,17 @@ [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } --(void) audioPlayerViewQueueFromHTTPSelected:(AudioPlayerView*)audioPlayerView +-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView { - NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; + NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"]; + NSURL* url = [NSURL fileURLWithPath:path]; - STKAutoRecoveringHttpDataSource* dataSource = [[STKAutoRecoveringHttpDataSource alloc] initWithHttpDataSource:(STKHttpDataSource*)[audioPlayer dataSourceFromURL:url]]; - - [audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; + [audioPlayer queueDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } -(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView { - NSString * path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"]; + NSString* path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"]; NSURL* url = [NSURL fileURLWithPath:path]; [audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.h b/ExampleApp/ExampleApp/AudioPlayerView.h index f2a65ad..c2f015a 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.h +++ b/ExampleApp/ExampleApp/AudioPlayerView.h @@ -39,7 +39,7 @@ @protocol AudioPlayerViewDelegate -(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView; --(void) audioPlayerViewQueueFromHTTPSelected:(AudioPlayerView*)audioPlayerView; +-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView; -(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView; @end @@ -48,9 +48,10 @@ @private NSTimer* timer; UISlider* slider; + UISwitch* repeatSwitch; UIButton* playButton; UIButton* playFromHTTPButton; - UIButton* queueFromHttpButton; + UIButton* queueShortFileButton; UIButton* playFromLocalFileButton; } diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index ec9ab7e..9628b7e 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -65,24 +65,29 @@ [playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal]; - queueFromHttpButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - queueFromHttpButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 100, size.width, size.height); - [queueFromHttpButton addTarget:self action:@selector(queueFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside]; - [queueFromHttpButton setTitle:@"Queue from HTTP" forState:UIControlStateNormal]; + queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 100, size.width, size.height); + [queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; + [queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal]; playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - playButton.frame = CGRectMake((320 - size.width) / 2, 350, size.width, size.height); + playButton.frame = CGRectMake((320 - size.width) / 2, 370, size.width, size.height); [playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 290, 280, 20)]; + slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)]; slider.continuous = YES; [slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged]; + + size = CGSizeMake(80, 50); + + repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 170, size.width, size.height)]; [self addSubview:slider]; [self addSubview:playButton]; [self addSubview:playFromHTTPButton]; [self addSubview:playFromLocalFileButton]; - [self addSubview:queueFromHttpButton]; + [self addSubview:queueShortFileButton]; + [self addSubview:repeatSwitch]; [self setupTimer]; [self updateControls]; @@ -105,7 +110,7 @@ -(void) setupTimer { - timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(tick) userInfo:nil repeats:YES]; + timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(tick) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; } @@ -135,12 +140,11 @@ [self.delegate audioPlayerViewPlayFromLocalFileSelected:self]; } --(void) queueFromHTTPButtonTouched +-(void) queueShortFileButtonTouched { - [self.delegate audioPlayerViewQueueFromHTTPSelected:self]; + [self.delegate audioPlayerViewQueueShortFileSelected:self]; } - -(void) playButtonPressed { if (!audioPlayer) @@ -162,7 +166,7 @@ { if (audioPlayer == nil) { - [playButton setTitle:@"Play" forState:UIControlStateNormal]; + [playButton setTitle:@"" forState:UIControlStateNormal]; } else if (audioPlayer.state == AudioPlayerStatePaused) { @@ -174,7 +178,7 @@ } else { - [playButton setTitle:@"Play" forState:UIControlStateNormal]; + [playButton setTitle:@"" forState:UIControlStateNormal]; } } @@ -221,11 +225,14 @@ // This queues on the currently playing track to be buffered and played immediately after (gapless) - SampleQueueId* queueId = (SampleQueueId*)queueItemId; + if (repeatSwitch.on) + { + SampleQueueId* queueId = (SampleQueueId*)queueItemId; - NSLog(@"Requeuing: %@", [queueId.url description]); + NSLog(@"Requeuing: %@", [queueId.url description]); - [self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]]; + [self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]]; + } } -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration @@ -237,4 +244,9 @@ NSLog(@"Finished: %@", [queueId.url description]); } +-(void) audioPlayer:(STKAudioPlayer *)audioPlayer logInfo:(NSString *)line +{ + NSLog(@"%@", line); +} + @end diff --git a/ExampleApp/ExampleApp/SampleQueueId.m b/ExampleApp/ExampleApp/SampleQueueId.m index 0a07b89..f1124a2 100644 --- a/ExampleApp/ExampleApp/SampleQueueId.m +++ b/ExampleApp/ExampleApp/SampleQueueId.m @@ -36,4 +36,9 @@ return [((SampleQueueId*)object).url isEqual: self.url] && ((SampleQueueId*)object).count == self.count; } +-(NSString*) description +{ + return [self.url description]; +} + @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 6626120..ac62c84 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -54,11 +54,11 @@ typedef enum AudioPlayerInternalStateWaitingForQueueToStart = (1 << 4) | AudioPlayerInternalStateRunning, AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning, AudioPlayerInternalStateRebuffering = (1 << 6) | AudioPlayerInternalStateRunning, - AudioPlayerInternalStateStopping = (1 << 7), - AudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 9), - AudioPlayerInternalStateStopped = (1 << 10), - AudioPlayerInternalStateDisposed = (1 << 11), - AudioPlayerInternalStateError = (1 << 12) + AudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 7) | AudioPlayerInternalStateRunning, + AudioPlayerInternalStateStopping = (1 << 8), + AudioPlayerInternalStateStopped = (1 << 9), + AudioPlayerInternalStateDisposed = (1 << 10), + AudioPlayerInternalStateError = (1 << 31) } AudioPlayerInternalState; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index eae4ef4..b8a154e 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -115,7 +115,6 @@ AudioQueueBufferRefLookupEntry; UInt64 audioDataOffset; UInt64 audioDataByteCount; UInt32 packetBufferSize; - volatile int bytesBuffered; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; @@ -123,8 +122,10 @@ AudioQueueBufferRefLookupEntry; @property (readwrite, retain) NSObject* queueItemId; @property (readwrite, retain) STKDataSource* dataSource; @property (readwrite) Float64 seekTime; -@property (readwrite) Float64 firstFrameIndex; +@property (readwrite) int bytesBuffered; +@property (readwrite) int lastByteIndex; @property (readwrite) Float64 lastFrameIndex; +@property (readwrite) Float64 firstFrameIndex; @property (readonly) UInt64 audioDataLengthInBytes; -(double) duration; @@ -143,6 +144,7 @@ AudioQueueBufferRefLookupEntry; self.dataSource = dataSourceIn; self.queueItemId = queueItemIdIn; self.lastFrameIndex = -1; + self.lastByteIndex = -1; } return self; @@ -256,7 +258,7 @@ AudioQueueBufferRefLookupEntry; UInt8* readBuffer; int readBufferSize; - NSOperationQueue* fastApiQueue; + NSOperationQueue* asyncApiRequestQueue; STKQueueEntry* currentlyPlayingEntry; STKQueueEntry* currentlyReadingEntry; @@ -319,6 +321,7 @@ AudioQueueBufferRefLookupEntry; } @property (readwrite) AudioPlayerInternalState internalState; +@property (readwrite) AudioPlayerInternalState stateBeforePaused; -(void) logInfo:(NSString*)line; -(void) processQueue:(BOOL)skipCurrent; @@ -327,7 +330,6 @@ AudioQueueBufferRefLookupEntry; -(void) resetAudioQueueWithReason:(NSString*)reason; -(BOOL) startAudioQueue; -(void) stopAudioQueueWithReason:(NSString*)reason; --(void) stopAudioQueueWithReason:(NSString*)reason immediately:(BOOL)immediately; -(BOOL) processRunloop; -(void) wakeupPlaybackThread; -(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry; @@ -468,8 +470,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { if (self = [super init]) { - fastApiQueue = [[NSOperationQueue alloc] init]; - [fastApiQueue setMaxConcurrentOperationCount:1]; + asyncApiRequestQueue = [[NSOperationQueue alloc] init]; + [asyncApiRequestQueue setMaxConcurrentOperationCount:1]; readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); @@ -651,9 +653,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - [fastApiQueue cancelAllOperations]; + [asyncApiRequestQueue cancelAllOperations]; - [fastApiQueue addOperationWithBlock:^ + [asyncApiRequestQueue addOperationWithBlock:^ { pthread_mutex_lock(&playerMutex); { @@ -672,7 +674,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - [fastApiQueue addOperationWithBlock:^ + [asyncApiRequestQueue addOperationWithBlock:^ { pthread_mutex_lock(&playerMutex); { @@ -709,7 +711,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ UInt32 fileFormatSize = sizeof(fileFormat); AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FileFormat, &fileFormatSize, &fileFormat); - NSLog(@""); } break; case kAudioFileStreamProperty_DataFormat: @@ -971,16 +972,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(BOOL) processFinishedPlaying:(STKQueueEntry*)entry { - NSLog(@"Finished playing"); + [self logInfo:[NSString stringWithFormat:@"Finished playing: %@", entry.queueItemId]]; entry.lastFrameIndex = -1; if (playbackThread) { CFRunLoopPerformBlock([playbackThreadRunLoop getCFRunLoop], NSDefaultRunLoopMode, ^ - { - [self audioQueueFinishedPlaying:entry]; - }); + { + [self audioQueueFinishedPlaying:entry]; + }); CFRunLoopWakeUp([playbackThreadRunLoop getCFRunLoop]); @@ -1008,6 +1009,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (currentlyPlayingEntry) { entry = currentlyPlayingEntry; + + if (!audioQueueFlushing) + { + entry.bytesBuffered += bufferIn->mAudioDataByteSize; + } } } OSSpinLockUnlock(¤tlyPlayingLock); @@ -1072,6 +1078,33 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ signal = YES; } } + else if (entry.lastByteIndex == audioPacketsPlayedCount && entry.lastByteIndex != -1) + { + BOOL everythingInBufferingQueueBuffered = YES; + + for (int i = 0; i < bufferingQueue.count; i++) + { + if ([[bufferingQueue objectAtIndex:i] lastFrameIndex] == -1 && [bufferingQueue objectAtIndex:i] != currentlyReadingEntry) + { + everythingInBufferingQueueBuffered = NO; + } + } + + if ([upcomingQueue peek] == nil && everythingInBufferingQueueBuffered) + { + if (self.internalState != AudioPlayerInternalStateFlushingAndStoppingButStillPlaying) + { + if (audioQueue) + { + self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; + + [self logInfo:@"Stopping AudioQueue asynchronously to get notification of playback end (last item on queue)"]; + + AudioQueueStop(audioQueue, NO); + } + } + } + } } } @@ -1493,9 +1526,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (runLoop) { CFRunLoopPerformBlock([runLoop getCFRunLoop], NSDefaultRunLoopMode, ^ - { - [self processRunloop]; - }); + { + [self processRunloop]; + }); CFRunLoopWakeUp([runLoop getCFRunLoop]); } @@ -1653,6 +1686,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ OSSpinLockLock(¤tlyPlayingLock); currentlyPlayingEntry = next; + currentlyPlayingEntry.bytesBuffered = 0; currentlyPlayingEntry.firstFrameIndex = [self currentTimeInFrames]; NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId; OSSpinLockUnlock(¤tlyPlayingLock); @@ -1725,6 +1759,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) { currentlyPlayingEntry.lastFrameIndex = -1; + currentlyPlayingEntry.lastByteIndex = -1; [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; @@ -1991,6 +2026,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self resetAudioQueueWithReason:@"from seekToTime"]; } + currentEntry.bytesBuffered = 0; currentEntry.firstFrameIndex = [self currentTimeInFrames]; } @@ -2030,11 +2066,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } -(void) stopAudioQueueWithReason:(NSString*)reason -{ - [self stopAudioQueueWithReason:reason immediately:YES]; -} - --(void) stopAudioQueueWithReason:(NSString*)reason immediately:(BOOL)immediately { OSStatus error; @@ -2052,12 +2083,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ audioQueueFlushing = YES; - error = AudioQueueStop(audioQueue, immediately); - - if (immediately) - { - error = error | AudioQueueDispose(audioQueue, YES); - } + error = AudioQueueStop(audioQueue, YES); + error = error | AudioQueueDispose(audioQueue, YES); audioQueue = nil; } @@ -2238,63 +2265,23 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self enqueueBuffer]; } - NSLog(@"dataSourceEof"); - [self logInfo:[NSString stringWithFormat:@"dataSourceEof for dataSource: %@", dataSourceIn]]; NSObject* queueItemId = currentlyReadingEntry.queueItemId; - dispatch_sync(dispatch_get_main_queue(), ^ + dispatch_async(dispatch_get_main_queue(), ^ { [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }); - [fastApiQueue waitUntilAllOperationsAreFinished]; - pthread_mutex_lock(&playerMutex); { - BOOL stopping = NO; - BOOL everythingInBufferingQueueBuffered = YES; - - for (int i = 0; i < bufferingQueue.count; i++) - { - if ([[bufferingQueue objectAtIndex:i] lastFrameIndex] == -1 && [bufferingQueue objectAtIndex:i] != currentlyReadingEntry) - { - everythingInBufferingQueueBuffered = NO; - } - } - - if ([upcomingQueue peek] == nil && everythingInBufferingQueueBuffered) - { - self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - if (audioQueue) - { - NSLog(@"stopping audio queue asynchronously in dataSourceEof"); - - AudioQueueStop(audioQueue, NO); - - stopping = YES; - } - } - if (audioQueue) { currentlyReadingEntry.lastFrameIndex = self->framesQueued; - currentlyReadingEntry = nil; + currentlyReadingEntry.lastByteIndex = audioPacketsReadCount; - if (self.internalState | AudioPlayerInternalStateRunning) - { - if (audioQueue) - { - if (![self audioQueueIsRunning] && !stopping) - { - [self logInfo:@"startAudioQueue from dataSourceEof"]; - - [self startAudioQueue]; - } - } - } + currentlyReadingEntry = nil; } else { @@ -2311,8 +2298,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { OSStatus error; - if (self.internalState != AudioPlayerInternalStatePaused) + if (self.internalState != AudioPlayerInternalStatePaused && (self.internalState & AudioPlayerInternalStateRunning)) { + self.stateBeforePaused = self.internalState; self.internalState = AudioPlayerInternalStatePaused; if (audioQueue) @@ -2343,7 +2331,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (self.internalState == AudioPlayerInternalStatePaused) { - self.internalState = AudioPlayerInternalStatePlaying; + self.internalState = self.stateBeforePaused; if (seekToTimeWasRequested) {