From 5f2335b79682ec66d30b7b9e889d3506c9c47914 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 25 Jun 2022 06:42:56 -0700 Subject: [PATCH] [Audio Output] Play last track and stop correctly Play last track up until it actually ends, and stop on command. Signed-off-by: Christopher Snowhill --- Audio/AudioPlayer.m | 11 ++++++++--- Audio/Chain/OutputNode.h | 2 ++ Audio/Chain/OutputNode.m | 4 ++++ Audio/Output/OutputAVFoundation.h | 3 ++- Audio/Output/OutputAVFoundation.m | 17 +++++++++-------- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index 2ce5558cd..aa0cbc54f 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -452,11 +452,16 @@ } while(0); if(signalStopped) { - [self stop]; + double latency = 0; + if(output) latency = [output latency]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, latency * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [self stop]; - bufferChain = nil; + self->bufferChain = nil; - [self notifyPlaybackStopped:nil]; + [self notifyPlaybackStopped:nil]; + }); return YES; } diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index 971e09d03..5cb89b0b9 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -51,6 +51,8 @@ - (void)close; - (void)seek:(double)time; +- (double)latency; + - (AudioChunk *)readChunk:(size_t)amount; - (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index dc128bb8f..6be97d72e 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -186,4 +186,8 @@ [controller restartPlaybackAtCurrentPosition]; } +- (double)latency { + return [output latency]; +} + @end diff --git a/Audio/Output/OutputAVFoundation.h b/Audio/Output/OutputAVFoundation.h index 8d415c6c1..e997a7251 100644 --- a/Audio/Output/OutputAVFoundation.h +++ b/Audio/Output/OutputAVFoundation.h @@ -50,7 +50,6 @@ using std::atomic_long; BOOL started; BOOL paused; BOOL restarted; - BOOL stopFlush; BOOL commandStop; BOOL eqEnabled; @@ -122,6 +121,8 @@ using std::atomic_long; - (void)resume; - (void)stop; +- (double)latency; + - (void)setVolume:(double)v; - (void)setEqualizerEnabled:(BOOL)enabled; diff --git a/Audio/Output/OutputAVFoundation.m b/Audio/Output/OutputAVFoundation.m index 84f122916..f88332f2e 100644 --- a/Audio/Output/OutputAVFoundation.m +++ b/Audio/Output/OutputAVFoundation.m @@ -325,7 +325,6 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons running = YES; started = NO; secondsLatency = 1.0; - stopFlush = NO; while(!stopping) { if([outputController shouldReset]) { @@ -361,7 +360,6 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons CFRelease(bufferRef); } else { - stopFlush = YES; break; } } @@ -1021,6 +1019,11 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons eqEnabled = enabled; } +- (double)latency { + if(secondsLatency > 0) return secondsLatency; + else return 0; +} + - (void)start { [self threadEntry:nil]; } @@ -1032,13 +1035,9 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons - (void)doStop { if(stopInvoked) { - stopFlush = NO; return; } @synchronized(self) { - if(commandStop) { - stopFlush = NO; - } stopInvoked = YES; if(observersapplied) { [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice" context:kOutputAVFoundationContext]; @@ -1074,14 +1073,16 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons } if(renderSynchronizer || audioRenderer) { if(renderSynchronizer) { - if(stopFlush) { + if(!commandStop) { int compareVal = 0; + double secondsLatency = self->secondsLatency >= 0 ? self->secondsLatency : 0; + int compareMax = (((1000000 / 5000) * secondsLatency) + (10000 / 5000)); // latency plus 10ms, divide by sleep intervals do { [currentPtsLock lock]; compareVal = CMTimeCompare(outputPts, currentPts); [currentPtsLock unlock]; usleep(5000); - } while(stopFlush && compareVal > 0); + } while(!commandStop && compareVal > 0 && compareMax-- > 0); } [self removeSynchronizerBlock]; [renderSynchronizer setRate:0];