Bug Fix: Audio chain should do more error checking

Check all audio chain elements for allocation failures, and also dispose
of all of the previous handles in reverse order, including nulling the
final node handle so the output does not attempt to poll for audio while
the chain is being rebuilt.

Also set up output node to handle the new null finalNode state, and
return an empty chunk to the caller.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-02-15 19:53:34 -08:00
parent 6fee16eb82
commit 40b2214c88
3 changed files with 47 additions and 13 deletions

View file

@ -38,7 +38,7 @@
}
- (id)initWithController:(id)c;
- (void)buildChain;
- (BOOL)buildChain;
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;

View file

@ -40,29 +40,52 @@
return self;
}
- (void)buildChain {
inputNode = nil;
- (BOOL)buildChain {
// Cut off output source
finalNode = nil;
// Tear them down in reverse
visualizationNode = nil;
downmixNode = nil;
hrtfNode = nil;
equalizerNode = nil;
fsurroundNode = nil;
rubberbandNode = nil;
converterNode = nil;
inputNode = nil;
inputNode = [[InputNode alloc] initWithController:self previous:nil];
if(!inputNode) return NO;
converterNode = [[ConverterNode alloc] initWithController:self previous:inputNode];
if(!converterNode) return NO;
rubberbandNode = [[DSPRubberbandNode alloc] initWithController:self previous:converterNode latency:0.1];
if(!rubberbandNode) return NO;
fsurroundNode = [[DSPFSurroundNode alloc] initWithController:self previous:rubberbandNode latency:0.03];
if(!fsurroundNode) return NO;
equalizerNode = [[DSPEqualizerNode alloc] initWithController:self previous:fsurroundNode latency:0.03];
if(!equalizerNode) return NO;
hrtfNode = [[DSPHRTFNode alloc] initWithController:self previous:equalizerNode latency:0.03];
if(!hrtfNode) return NO;
downmixNode = [[DSPDownmixNode alloc] initWithController:self previous:hrtfNode latency:0.03];
if(!downmixNode) return NO;
// Approximately five frames
visualizationNode = [[VisualizationNode alloc] initWithController:self previous:downmixNode latency:5.0 / 60.0];
if(!visualizationNode) return NO;
finalNode = visualizationNode;
return YES;
}
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
[self setStreamURL:url];
[self setUserInfo:userInfo];
[self buildChain];
if (![self buildChain]) {
DLog(@"Couldn't build processing chain...");
return NO;
}
id<CogSource> source = [AudioSource audioSourceForURL:url];
DLog(@"Opening: %@", url);
@ -102,7 +125,10 @@
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
DLog(@"New buffer chain!");
[self setUserInfo:userInfo];
[self buildChain];
if(![self buildChain]) {
DLog(@"Couldn't build processing chain...");
return NO;
}
if(![inputNode openWithDecoder:[i decoder]])
return NO;
@ -135,7 +161,10 @@
{
DLog(@"New buffer chain!");
[self setUserInfo:userInfo];
[self buildChain];
if(![self buildChain]) {
DLog(@"Couldn't build processing chain...");
return NO;
}
if(![inputNode openWithDecoder:decoder])
return NO;

View file

@ -100,15 +100,20 @@
- (AudioChunk *)readChunk:(size_t)amount {
@autoreleasepool {
[self setPreviousNode:[[controller bufferChain] finalNode]];
Node *finalNode = [[controller bufferChain] finalNode];
[self setPreviousNode:finalNode];
AudioChunk *ret = [super readChunk:amount];
if(finalNode) {
AudioChunk *ret = [super readChunk:amount];
/* if (n == 0) {
DLog(@"Output Buffer dry!");
}
*/
return ret;
/* if (n == 0) {
DLog(@"Output Buffer dry!");
}
*/
return ret;
} else {
return [[AudioChunk alloc] init];
}
}
}