From d563617998fac780e4804fa091251ca55c857d4f Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Fri, 24 Jun 2022 19:14:48 -0700 Subject: [PATCH] [Audio Output] Stop immediately, and fix deadlocks Stop output when requested, except on natural completion of the last track in the play queue. Also fix deadlocks with stopping and restarting. Signed-off-by: Christopher Snowhill --- Audio/AudioPlayer.m | 10 +++++--- Audio/Chain/OutputNode.m | 4 +++ Audio/Output/OutputAVFoundation.h | 2 ++ Audio/Output/OutputAVFoundation.m | 41 +++++++++++++++++++------------ 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index c6ea5d61d..c720bf783 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -62,9 +62,10 @@ [self waitUntilCallbacksExit]; if(output) { [output setShouldContinue:NO]; - output = nil; } - output = [[OutputNode alloc] initWithController:self previous:nil]; + if(!output) { + output = [[OutputNode alloc] initWithController:self previous:nil]; + } [output setup]; [output setVolume:volume]; @synchronized(chainQueue) { @@ -133,6 +134,9 @@ bufferChain = nil; } } + if(output) { + [output setShouldContinue:NO]; + } output = nil; } @@ -278,7 +282,7 @@ } - (void)endEqualizer:(AudioUnit)eq { - [self sendDelegateMethod:@selector(audioPlayer:removeEqualizer:) withVoid:eq waitUntilDone:YES]; + [self sendDelegateMethod:@selector(audioPlayer:removeEqualizer:) withVoid:eq waitUntilDone:NO]; } - (void)addChainToQueue:(BufferChain *)newChain { diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index dc128bb8f..c03d82d32 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -156,6 +156,10 @@ } - (void)setShouldContinue:(BOOL)s { + if(output && !s) { + [output stop]; + } + [super setShouldContinue:s]; // if (s == NO) diff --git a/Audio/Output/OutputAVFoundation.h b/Audio/Output/OutputAVFoundation.h index fc042da4c..8f1552213 100644 --- a/Audio/Output/OutputAVFoundation.h +++ b/Audio/Output/OutputAVFoundation.h @@ -43,6 +43,7 @@ using std::atomic_long; double lastVisRate; BOOL stopInvoked; + BOOL stopCompleted; BOOL running; BOOL stopping; BOOL stopped; @@ -50,6 +51,7 @@ using std::atomic_long; BOOL paused; BOOL restarted; BOOL stopFlush; + BOOL commandStop; BOOL eqEnabled; BOOL eqInitialized; diff --git a/Audio/Output/OutputAVFoundation.m b/Audio/Output/OutputAVFoundation.m index d3c7c67e8..2790aaf10 100644 --- a/Audio/Output/OutputAVFoundation.m +++ b/Audio/Output/OutputAVFoundation.m @@ -212,20 +212,16 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA return 0; } + if(stopping) return 0; + float volumeScale = 1.0; double sustained; - @synchronized(self) { - sustained = secondsHdcdSustained; - } + sustained = secondsHdcdSustained; if(sustained > 0) { if(sustained < amountRead) { - @synchronized(self) { - secondsHdcdSustained = 0; - } + secondsHdcdSustained = 0; } else { - @synchronized(self) { - secondsHdcdSustained -= chunkDuration; - } + secondsHdcdSustained -= chunkDuration; volumeScale = 0.5; } } @@ -315,9 +311,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons } - (BOOL)processEndOfStream { - if([outputController endOfStream] == YES && [self signalEndOfStream:secondsLatency]) { + if(stopping || ([outputController endOfStream] == YES && [self signalEndOfStream:secondsLatency])) { stopping = YES; - stopFlush = YES; return YES; } return NO; @@ -378,7 +373,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons stopped = YES; if(!stopInvoked) { - [self stop]; + [self doStop]; } } @@ -803,6 +798,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons @synchronized(self) { stopInvoked = NO; + stopCompleted = NO; + commandStop = NO; running = NO; stopping = NO; @@ -1000,8 +997,21 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons } - (void)stop { + commandStop = YES; + [self doStop]; +} + +- (void)doStop { + if(stopInvoked) { + while(!stopCompleted) { + usleep(5000); + } + return; + } @synchronized(self) { - if(stopInvoked) return; + if(commandStop) { + stopFlush = NO; + } stopInvoked = YES; if(observersapplied) { [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice" context:kOutputAVFoundationContext]; @@ -1094,6 +1104,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons r8bstate_delete(r8bvis); r8bvis = NULL; } + stopCompleted = YES; } } @@ -1114,9 +1125,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons } - (void)sustainHDCD { - @synchronized(self) { - secondsHdcdSustained = 10.0; - } + secondsHdcdSustained = 10.0; } @end