From 5424e18f27d30c79a76fefecc2567ab1ce2a11f6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 13 Feb 2025 22:25:27 -0800 Subject: [PATCH] Audio: Fix more hangs and resume playback on start Check for paused processing state in various places, so that startup playback works properly, and resume playback at seek offset works properly and doesn't hang the player. Signed-off-by: Christopher Snowhill --- Audio/Chain/ChunkList.m | 9 ++++- Audio/Chain/ConverterNode.h | 2 ++ Audio/Chain/ConverterNode.m | 4 +++ Audio/Chain/DSP/DSPDownmixNode.h | 2 ++ Audio/Chain/DSP/DSPDownmixNode.m | 5 ++- Audio/Chain/DSP/DSPEqualizerNode.h | 2 ++ Audio/Chain/DSP/DSPEqualizerNode.m | 4 +++ Audio/Chain/DSP/DSPFSurroundNode.h | 2 ++ Audio/Chain/DSP/DSPFSurroundNode.m | 4 +++ Audio/Chain/DSP/DSPHRTFNode.h | 2 ++ Audio/Chain/DSP/DSPHRTFNode.m | 4 +++ Audio/Chain/DSP/DSPRubberbandNode.h | 2 ++ Audio/Chain/DSP/DSPRubberbandNode.m | 4 +++ Audio/Chain/Node.h | 2 ++ Audio/Chain/Node.m | 53 ++++++++++++++++++----------- Audio/Chain/VisualizationNode.h | 2 ++ Audio/Chain/VisualizationNode.m | 4 +++ 17 files changed, 85 insertions(+), 22 deletions(-) diff --git a/Audio/Chain/ChunkList.m b/Audio/Chain/ChunkList.m index 0dd6de125..eeb1a6439 100644 --- a/Audio/Chain/ChunkList.m +++ b/Audio/Chain/ChunkList.m @@ -569,7 +569,14 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes double streamTimestamp = 0.0; double streamTimeRatio = 1.0; - if (![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { + BOOL blocked = NO; + while(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { + if((blocked = block())) { + break; + } + } + + if(blocked) { inMerger = NO; return [[AudioChunk alloc] init]; } diff --git a/Audio/Chain/ConverterNode.h b/Audio/Chain/ConverterNode.h index d5b02adfb..8977f1a43 100644 --- a/Audio/Chain/ConverterNode.h +++ b/Audio/Chain/ConverterNode.h @@ -73,6 +73,8 @@ - (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outputFormat isLossless:(BOOL)lossless; - (void)cleanUp; +- (BOOL)paused; + - (void)process; - (AudioChunk *)convert; diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index 00af5de23..3e4ac572c 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -90,6 +90,10 @@ void scale_by_volume(float *buffer, size_t count, float volume) { } } +- (BOOL)paused { + return paused; +} + - (void)process { // Removed endOfStream check from here, since we want to be able to flush the converter // when the end of stream is reached. Convert function instead processes what it can, diff --git a/Audio/Chain/DSP/DSPDownmixNode.h b/Audio/Chain/DSP/DSPDownmixNode.h index dc7915736..2db82b32c 100644 --- a/Audio/Chain/DSP/DSPDownmixNode.h +++ b/Audio/Chain/DSP/DSPDownmixNode.h @@ -22,6 +22,8 @@ - (void)resetBuffer; +- (BOOL)paused; + - (void)process; - (AudioChunk * _Nullable)convert; diff --git a/Audio/Chain/DSP/DSPDownmixNode.m b/Audio/Chain/DSP/DSPDownmixNode.m index e48adbcbe..eccae3838 100644 --- a/Audio/Chain/DSP/DSPDownmixNode.m +++ b/Audio/Chain/DSP/DSPDownmixNode.m @@ -77,7 +77,6 @@ usleep(500); } [super resetBuffer]; - [self fullShutdown]; paused = NO; } @@ -87,6 +86,10 @@ formatSet = YES; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) { diff --git a/Audio/Chain/DSP/DSPEqualizerNode.h b/Audio/Chain/DSP/DSPEqualizerNode.h index b51550e5a..c422765d6 100644 --- a/Audio/Chain/DSP/DSPEqualizerNode.h +++ b/Audio/Chain/DSP/DSPEqualizerNode.h @@ -21,6 +21,8 @@ - (void)resetBuffer; +- (BOOL)paused; + - (void)process; - (AudioChunk * _Nullable)convert; diff --git a/Audio/Chain/DSP/DSPEqualizerNode.m b/Audio/Chain/DSP/DSPEqualizerNode.m index 9b2115d00..597ffd104 100644 --- a/Audio/Chain/DSP/DSPEqualizerNode.m +++ b/Audio/Chain/DSP/DSPEqualizerNode.m @@ -315,6 +315,10 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA paused = NO; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) { diff --git a/Audio/Chain/DSP/DSPFSurroundNode.h b/Audio/Chain/DSP/DSPFSurroundNode.h index dc9a19c10..7bbd0e9e2 100644 --- a/Audio/Chain/DSP/DSPFSurroundNode.h +++ b/Audio/Chain/DSP/DSPFSurroundNode.h @@ -20,6 +20,8 @@ - (void)resetBuffer; +- (BOOL)paused; + - (void)process; - (AudioChunk * _Nullable)convert; diff --git a/Audio/Chain/DSP/DSPFSurroundNode.m b/Audio/Chain/DSP/DSPFSurroundNode.m index 0c676f2fa..7311b043f 100644 --- a/Audio/Chain/DSP/DSPFSurroundNode.m +++ b/Audio/Chain/DSP/DSPFSurroundNode.m @@ -132,6 +132,10 @@ static void * kDSPFSurroundNodeContext = &kDSPFSurroundNodeContext; paused = NO; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) { diff --git a/Audio/Chain/DSP/DSPHRTFNode.h b/Audio/Chain/DSP/DSPHRTFNode.h index fd65aac24..1906442ed 100644 --- a/Audio/Chain/DSP/DSPHRTFNode.h +++ b/Audio/Chain/DSP/DSPHRTFNode.h @@ -22,6 +22,8 @@ - (void)resetBuffer; +- (BOOL)paused; + - (void)process; - (AudioChunk * _Nullable)convert; diff --git a/Audio/Chain/DSP/DSPHRTFNode.m b/Audio/Chain/DSP/DSPHRTFNode.m index 3ba6d0092..bb169a7ef 100644 --- a/Audio/Chain/DSP/DSPHRTFNode.m +++ b/Audio/Chain/DSP/DSPHRTFNode.m @@ -257,6 +257,10 @@ static void unregisterMotionListener(void) { paused = NO; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) { diff --git a/Audio/Chain/DSP/DSPRubberbandNode.h b/Audio/Chain/DSP/DSPRubberbandNode.h index f32a76293..c9816d79f 100644 --- a/Audio/Chain/DSP/DSPRubberbandNode.h +++ b/Audio/Chain/DSP/DSPRubberbandNode.h @@ -20,6 +20,8 @@ - (void)resetBuffer; +- (BOOL)paused; + - (void)process; - (AudioChunk * _Nullable)convert; diff --git a/Audio/Chain/DSP/DSPRubberbandNode.m b/Audio/Chain/DSP/DSPRubberbandNode.m index dee87b5f0..641b49650 100644 --- a/Audio/Chain/DSP/DSPRubberbandNode.m +++ b/Audio/Chain/DSP/DSPRubberbandNode.m @@ -338,6 +338,10 @@ static void * kDSPRubberbandNodeContext = &kDSPRubberbandNodeContext; paused = NO; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) { diff --git a/Audio/Chain/Node.h b/Audio/Chain/Node.h index 48dbb7ac8..77a58a2a2 100644 --- a/Audio/Chain/Node.h +++ b/Audio/Chain/Node.h @@ -46,6 +46,8 @@ - (void)cleanUp; +- (BOOL)paused; + - (void)writeData:(const void *_Nonnull)ptr amount:(size_t)a; - (void)writeChunk:(AudioChunk *_Nonnull)chunk; - (AudioChunk *_Nonnull)readChunk:(size_t)maxFrames; diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index fc402b3c6..dfdeffaf2 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -79,7 +79,7 @@ - (void)writeData:(const void *)ptr amount:(size_t)amount { inWrite = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inWrite = NO; return; } @@ -105,7 +105,7 @@ } } - while(shouldContinue == YES && durationLeft < 0.0) { + while(shouldContinue == YES && ![self paused] && durationLeft < 0.0) { if(durationLeft < 0.0 || shouldReset) { [accessLock unlock]; [writeSemaphore timedWait:2000]; @@ -132,7 +132,7 @@ - (void)writeChunk:(AudioChunk *)chunk { inWrite = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inWrite = NO; return; } @@ -150,7 +150,7 @@ } } - while(shouldContinue == YES && durationLeft < 0.0) { + while(shouldContinue == YES && ![self paused] && durationLeft < 0.0) { if(previousNode && [previousNode shouldContinue] == NO) { shouldContinue = NO; break; @@ -192,20 +192,22 @@ - (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config { inPeek = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inPeek = NO; return NO; } [accessLock lock]; - while(shouldContinue && [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { + while(shouldContinue && ![self paused] && + [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { [accessLock unlock]; + [writeSemaphore signal]; [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; } - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { [accessLock unlock]; inPeek = NO; return NO; @@ -229,20 +231,22 @@ - (BOOL)peekTimestamp:(double *_Nonnull)timestamp timeRatio:(double *_Nonnull)timeRatio { inPeek = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inPeek = NO; return NO; } [accessLock lock]; - while(shouldContinue && [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { + while(shouldContinue && ![self paused] && + [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { [accessLock unlock]; + [writeSemaphore signal]; [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; } - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { [accessLock unlock]; inPeek = NO; return NO; @@ -266,15 +270,17 @@ - (AudioChunk *)readChunk:(size_t)maxFrames { inRead = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inRead = NO; return [[AudioChunk alloc] init]; } [accessLock lock]; - while(shouldContinue && [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { + while(shouldContinue && ![self paused] && + [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { [accessLock unlock]; + [writeSemaphore signal]; [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; if([previousNode shouldReset] == YES) { @@ -282,7 +288,7 @@ } } - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { [accessLock unlock]; inRead = NO; return [[AudioChunk alloc] init]; @@ -325,15 +331,17 @@ - (AudioChunk *)readChunkAsFloat32:(size_t)maxFrames { inRead = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inRead = NO; return [[AudioChunk alloc] init]; } [accessLock lock]; - while(shouldContinue && [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { + while(shouldContinue && ![self paused] && + [[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) { [accessLock unlock]; + [writeSemaphore signal]; [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; if([previousNode shouldReset] == YES) { @@ -341,7 +349,7 @@ } } - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { [accessLock unlock]; inRead = NO; return [[AudioChunk alloc] init]; @@ -384,7 +392,7 @@ - (AudioChunk *)readAndMergeChunks:(size_t)maxFrames { inMerge = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inMerge = NO; return [[AudioChunk alloc] init]; } @@ -416,7 +424,7 @@ [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; - return !shouldContinue || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES); + return !shouldContinue || [self paused] || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES); }]; } @@ -433,7 +441,7 @@ - (AudioChunk *)readAndMergeChunksAsFloat32:(size_t)maxFrames { inMerge = YES; - if(!shouldContinue) { + if(!shouldContinue || [self paused]) { inMerge = NO; return [[AudioChunk alloc] init]; } @@ -465,7 +473,7 @@ [[previousNode readSemaphore] timedWait:2000]; [accessLock lock]; - return !shouldContinue || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES); + return !shouldContinue || [self paused] || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES); }]; } @@ -515,6 +523,11 @@ } } +// Implementations should override +- (BOOL)paused { + return NO; +} + - (Semaphore *)writeSemaphore { return writeSemaphore; } diff --git a/Audio/Chain/VisualizationNode.h b/Audio/Chain/VisualizationNode.h index 6ff4b0a86..4ec90e4c7 100644 --- a/Audio/Chain/VisualizationNode.h +++ b/Audio/Chain/VisualizationNode.h @@ -20,6 +20,8 @@ - (BOOL)setup; - (void)cleanUp; +- (BOOL)paused; + - (void)resetBuffer; - (void)pop; diff --git a/Audio/Chain/VisualizationNode.m b/Audio/Chain/VisualizationNode.m index a57971ed9..2ade0c51d 100644 --- a/Audio/Chain/VisualizationNode.m +++ b/Audio/Chain/VisualizationNode.m @@ -222,6 +222,10 @@ static VisualizationCollection *theCollection = nil; downmixer = nil; } +- (BOOL)paused { + return paused; +} + - (void)process { while([self shouldContinue] == YES) { if(paused) {