Cog Audio: Dealt with a major retain cycle leak

This seals up a major memory leak of the playback state whenever a chain
is released on stop or on manual track change. CogAudioMulti was
retaining the input node due to its listeners, and InputNode was not
releasing the listeners when asked to stop running. This is fixed now.

Fixes #221

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-01-29 21:10:59 -08:00
parent 77a079bd53
commit 9e5a70c9ae
7 changed files with 53 additions and 13 deletions

View File

@ -68,7 +68,10 @@
ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @""); ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @"");
[self waitUntilCallbacksExit]; [self waitUntilCallbacksExit];
output = nil; if (output) {
[output setShouldContinue:NO];
output = nil;
}
output = [[OutputNode alloc] initWithController:self previous:nil]; output = [[OutputNode alloc] initWithController:self previous:nil];
[output setup]; [output setup];
[output setVolume: volume]; [output setVolume: volume];
@ -135,6 +138,20 @@
//Set shouldoContinue to NO on all things //Set shouldoContinue to NO on all things
[self setShouldContinue:NO]; [self setShouldContinue:NO];
[self setPlaybackStatus:CogStatusStopped waitUntilDone:YES]; [self setPlaybackStatus:CogStatusStopped waitUntilDone:YES];
@synchronized(chainQueue) {
for (id anObject in chainQueue)
{
[anObject setShouldContinue:NO];
}
[chainQueue removeAllObjects];
endOfInputReached = NO;
if (bufferChain)
{
bufferChain = nil;
}
}
output = nil;
} }
- (void)pause - (void)pause

View File

@ -28,6 +28,8 @@
BOOL shouldSeek; BOOL shouldSeek;
long seekFrame; long seekFrame;
BOOL observersAdded;
Semaphore *exitAtTheEndOfTheStream; Semaphore *exitAtTheEndOfTheStream;
} }

View File

@ -90,6 +90,8 @@
forKeyPath:@"metadata" forKeyPath:@"metadata"
options:(NSKeyValueObservingOptionNew) options:(NSKeyValueObservingOptionNew)
context:NULL]; context:NULL];
observersAdded = YES;
} }
- (void)observeValueForKeyPath:(NSString *)keyPath - (void)observeValueForKeyPath:(NSString *)keyPath
@ -219,8 +221,19 @@
- (void)removeObservers - (void)removeObservers
{ {
[decoder removeObserver:self forKeyPath:@"properties"]; if (observersAdded)
[decoder removeObserver:self forKeyPath:@"metadata"]; {
[decoder removeObserver:self forKeyPath:@"properties"];
[decoder removeObserver:self forKeyPath:@"metadata"];
observersAdded = NO;
}
}
- (void)setShouldContinue:(BOOL)s
{
[super setShouldContinue:s];
if (!s)
[self removeObservers];
} }
- (void)dealloc - (void)dealloc

View File

@ -137,6 +137,7 @@
- (void)close - (void)close
{ {
[output stop]; [output stop];
output = nil;
} }
- (void)setVolume:(double) v - (void)setVolume:(double) v

View File

@ -108,10 +108,11 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
- (void)close - (void)close
{ {
if ( theDecoder != nil ) { if ( theDecoder != nil ) {
[theDecoder close];
for (NSDictionary *obsItem in cachedObservers) { for (NSDictionary *obsItem in cachedObservers) {
[theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]]; [theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]];
} }
[cachedObservers removeAllObjects];
[theDecoder close];
theDecoder = nil; theDecoder = nil;
} }
} }

View File

@ -46,6 +46,7 @@
atomic_long bytesHdcdSustained; atomic_long bytesHdcdSustained;
BOOL listenerapplied; BOOL listenerapplied;
BOOL observersapplied;
float volume; float volume;

View File

@ -174,10 +174,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
_logFile = fopen("/tmp/CogAudioLog.raw", "wb"); _logFile = fopen("/tmp/CogAudioLog.raw", "wb");
#endif #endif
}
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:NULL];
}
return self; return self;
} }
@ -704,6 +701,10 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
[_au allocateRenderResourcesAndReturnError:&err]; [_au allocateRenderResourcesAndReturnError:&err];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:NULL];
observersapplied = YES;
return (err == nil); return (err == nil);
} }
@ -732,6 +733,12 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
- (void)stop - (void)stop
{ {
stopInvoked = YES;
if (observersapplied) {
observersapplied = NO;
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable"];
}
if (stopNext && started && !paused) { if (stopNext && started && !paused) {
while (![[outputController buffer] isEmpty]) { while (![[outputController buffer] isEmpty]) {
[writeSemaphore signal]; [writeSemaphore signal];
@ -743,7 +750,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
stopNext = NO; stopNext = NO;
[self signalEndOfStream]; [self signalEndOfStream];
} }
stopInvoked = YES;
stopping = YES; stopping = YES;
paused = NO; paused = NO;
[writeSemaphore signal]; [writeSemaphore signal];
@ -782,14 +788,13 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
_logFile = NULL; _logFile = NULL;
} }
#endif #endif
outputController = nil;
} }
- (void)dealloc - (void)dealloc
{ {
[self stop]; if (!stopInvoked)
[self stop];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable"];
} }
- (void)pause - (void)pause