From 04426d755c56e96b87be584e736643f9b88fe575 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 15 Feb 2025 19:53:34 -0800 Subject: [PATCH] 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 --- Audio/Chain/BufferChain.h | 2 +- Audio/Chain/BufferChain.m | 39 ++++++++++++++++++++++++++++++++++----- Audio/Chain/OutputNode.m | 19 ++++++++++++------- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index ee3f021a3..6c4df021c 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -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; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index edb14dec5..688abe2ff 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -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 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; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index b0227ef93..d8e85b779 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -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]; + } } }