Always uninitialize nodes when doing connections.

Guard some graph functions with @synchronized()

Changing sample rate doesn't rebuild the graph; now requires a restart. (Can't reliably be done with encoders/streams active)
This commit is contained in:
Zakk 2020-03-01 00:40:01 -05:00
parent d14db2b8f0
commit d9f5bff5fe
4 changed files with 115 additions and 95 deletions

View file

@ -43,7 +43,7 @@
@property (strong) CAMultiAudioDownmixer *encodeMixer;
//@property (strong) CSAacEncoder *encoder;
@property (assign) UInt32 sampleRate;
@property (assign) double sampleRate;
@property (assign) int audioBitrate;
@property (assign) double audio_adjust;
@property (strong) NSArray *audioOutputs;

View file

@ -84,10 +84,17 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
if ([aDecoder containsValueForKey:@"sampleRate"])
{
self.sampleRate = [aDecoder decodeInt32ForKey:@"sampleRate"];
UInt32 oldSampleRate = [aDecoder decodeInt32ForKey:@"sampleRate"];
self.sampleRate = (double)oldSampleRate;
}
if ([aDecoder containsValueForKey:@"sampleRateDouble"])
{
self.sampleRate = [aDecoder decodeDoubleForKey:@"sampleRateDouble"];
}
if ([aDecoder containsValueForKey:@"audioBitrate"])
{
self.audioBitrate = [aDecoder decodeIntForKey:@"audioBitrate"];
@ -242,7 +249,7 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.sampleRate forKey:@"sampleRate"];
[aCoder encodeDouble:self.sampleRate forKey:@"sampleRateDouble"];
[aCoder encodeObject:self.outputNode.deviceUID forKey:@"selectedAudioId"];
[aCoder encodeFloat:self.encodeMixer.volume forKey:@"streamVolume"];
[aCoder encodeBool:!self.encodeMixer.enabled forKey:@"streamMuted"];
@ -644,7 +651,8 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
-(bool)buildGraph
{
self.graph = [[CAMultiAudioGraph alloc] initWithFormat:nil];
AVAudioFormat *useFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:self.sampleRate channels:2];
self.graph = [[CAMultiAudioGraph alloc] initWithFormat:useFormat];
self.graph.engine = self;
@ -1038,16 +1046,16 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
}
}
-(UInt32)sampleRate
/*
-(double)sampleRate
{
return _sampleRate;
}
-(void)setSampleRate:(UInt32)sampleRate
-(void)setSampleRate:(double)sampleRate
{
UInt32 old_samplerate = _sampleRate;
double old_samplerate = _sampleRate;
_sampleRate = sampleRate;
if (sampleRate > 0 && (sampleRate != old_samplerate))
{
@ -1058,6 +1066,7 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
}
}
*/
-(void)removeFileInput:(CAMultiAudioFile *)toRemove

View file

@ -175,71 +175,71 @@
-(bool)connectNode:(CAMultiAudioNode *)node toNode:(CAMultiAudioNode *)toNode format:(AVAudioFormat *)format inBus:(UInt32)inBus outBus:(UInt32)outBus
{
if (!node || !toNode)
@synchronized(self)
{
NSLog(@"ConnectNode: Source or destination node is nil %@ -> %@", node, toNode);
return NO;
}
NSLog(@"CONNECT %@:%d TO %@:%d FORMAT %@", node, outBus, toNode, inBus, format);
AUNode inNode;
AUNode connectTo;
OSStatus err;
UInt32 bus = inBus;
[self disconnectNode:node outputBus:outBus];
[self disconnectNode:toNode inputBus:inBus];
[node willConnectToNode:toNode inBus:bus outBus:outBus];
[toNode willConnectNode:node inBus:bus outBus:outBus];
[toNode setInputStreamFormat:format bus:inBus];
[node setOutputStreamFormat:format bus:outBus];
AudioUnitConnection newConn;
newConn.destInputNumber = inBus;
newConn.sourceOutputNumber = outBus;
newConn.sourceAudioUnit = node.audioUnit;
err = AudioUnitSetProperty(toNode.audioUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inBus, &newConn, sizeof(newConn));
if (err)
{
NSLog(@"AudioUnitSetProperty(MakeConnection) failed for %@ -> %@, err: %d", node, toNode, err);
if (!node || !toNode)
{
NSLog(@"ConnectNode: Source or destination node is nil %@ -> %@", node, toNode);
return NO;
}
NSLog(@"CONNECT %@:%d TO %@:%d FORMAT %@", node, outBus, toNode, inBus, format);
OSStatus err;
UInt32 bus = inBus;
[self disconnectNode:node outputBus:outBus];
[self disconnectNode:toNode inputBus:inBus];
[node willConnectToNode:toNode inBus:bus outBus:outBus];
[toNode willConnectNode:node inBus:bus outBus:outBus];
[toNode setInputStreamFormat:format bus:inBus];
[node setOutputStreamFormat:format bus:outBus];
AudioUnitConnection newConn;
newConn.destInputNumber = inBus;
newConn.sourceOutputNumber = outBus;
newConn.sourceAudioUnit = node.audioUnit;
err = AudioUnitSetProperty(toNode.audioUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inBus, &newConn, sizeof(newConn));
if (err)
{
NSLog(@"AudioUnitSetProperty(MakeConnection) failed for %@ -> %@, err: %d", node, toNode, err);
NSLog(@"%@ OUTPUT %@", node, [node outputFormatForBus:outBus]);
NSLog(@"%@ INPUT %@", toNode, [toNode inputFormatForBus:inBus]);
return NO;
}
[node nodeConnected:toNode inBus:bus outBus:outBus];
[toNode connectedToNode:node inBus:bus outBus:outBus];
NSMutableArray *outputsForBus = node.outputConnections[@(outBus)];
if (!outputsForBus)
{
outputsForBus = [NSMutableArray array];
node.outputConnections[@(outBus)] = outputsForBus;
}
[outputsForBus addObject:[[CAMultiAudioConnection alloc] initWithNode:toNode bus:inBus]];
toNode.inputConnections[@(inBus)] = [[CAMultiAudioConnection alloc] initWithNode:node bus:outBus];
NSLog(@"%@ OUTPUT %@", node, [node outputFormatForBus:outBus]);
NSLog(@"%@ INPUT %@", toNode, [toNode inputFormatForBus:inBus]);
return NO;
}
[node nodeConnected:toNode inBus:bus outBus:outBus];
[toNode connectedToNode:node inBus:bus outBus:outBus];
NSMutableArray *outputsForBus = node.outputConnections[@(outBus)];
if (!outputsForBus)
{
outputsForBus = [NSMutableArray array];
node.outputConnections[@(outBus)] = outputsForBus;
}
[outputsForBus addObject:[[CAMultiAudioConnection alloc] initWithNode:toNode bus:inBus]];
toNode.inputConnections[@(inBus)] = [[CAMultiAudioConnection alloc] initWithNode:node bus:outBus];
NSLog(@"%@ OUTPUT %@", node, [node outputFormatForBus:outBus]);
NSLog(@"%@ INPUT %@", toNode, [toNode inputFormatForBus:inBus]);
return YES;
}
-(NSArray *)connectedInputBusses:(CAMultiAudioNode *)node
{
return node.inputConnections.allKeys;
}
@ -300,41 +300,44 @@
return NO;
}
CAMultiAudioConnection *inputConnection = node.inputConnections[@(inputBus)];
if (inputConnection)
@synchronized (self)
{
CAMultiAudioConnection *inputConnection = node.inputConnections[@(inputBus)];
AudioUnitConnection breakConn;
breakConn.destInputNumber = inputBus;
breakConn.sourceOutputNumber = inputConnection.bus;
breakConn.sourceAudioUnit = NULL;
OSErr err = AudioUnitSetProperty(node.audioUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inputBus, &breakConn, sizeof(breakConn));
if (err)
if (inputConnection)
{
NSLog(@"AudioUnitSetProperty(MakeConnection) failed for node %@:%d, err %d", node, inputBus, err);
}
if (updateOutputs)
{
CAMultiAudioNode *srcNode = inputConnection.node;
if (srcNode)
AudioUnitConnection breakConn;
breakConn.destInputNumber = inputBus;
breakConn.sourceOutputNumber = inputConnection.bus;
breakConn.sourceAudioUnit = NULL;
OSErr err = AudioUnitSetProperty(node.audioUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inputBus, &breakConn, sizeof(breakConn));
if (err)
{
NSMutableArray *newConns = [NSMutableArray array];
NSArray *conns = [self outputConnections:srcNode forBus:inputConnection.bus];
for(CAMultiAudioConnection *nConn in conns)
{
if (nConn.node != node)
{
[newConns addObject:nConn];
}
}
srcNode.outputConnections[@(inputConnection.bus)] = newConns;
NSLog(@"AudioUnitSetProperty(MakeConnection) failed for node %@:%d, err %d", node, inputBus, err);
}
if (updateOutputs)
{
CAMultiAudioNode *srcNode = inputConnection.node;
if (srcNode)
{
NSMutableArray *newConns = [NSMutableArray array];
NSArray *conns = [self outputConnections:srcNode forBus:inputConnection.bus];
for(CAMultiAudioConnection *nConn in conns)
{
if (nConn.node != node)
{
[newConns addObject:nConn];
}
}
srcNode.outputConnections[@(inputConnection.bus)] = newConns;
}
}
[node.inputConnections removeObjectForKey:@(inputBus)];
}
[node.inputConnections removeObjectForKey:@(inputBus)];
}
return YES;
}

View file

@ -304,6 +304,8 @@ UInt32 inNumberFrames,
-(bool)setInputStreamFormat:(AVAudioFormat *)format bus:(UInt32)bus
{
AudioUnitUninitialize(self.audioUnit);
OSStatus err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, bus, format.streamDescription, sizeof(AudioStreamBasicDescription));
if (err)
@ -311,7 +313,10 @@ UInt32 inNumberFrames,
NSLog(@"Failed to set StreamFormat for input on node %@ with %d", self, err);
return NO;
}
[self willInitializeNode];
AudioUnitInitialize(self.audioUnit);
[self didInitializeNode];
return YES;
}
@ -319,7 +324,8 @@ UInt32 inNumberFrames,
-(bool)setOutputStreamFormat:(AVAudioFormat *)format bus:(UInt32)bus
{
AudioUnitUninitialize(self.audioUnit);
OSStatus err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, bus, format.streamDescription, sizeof(AudioStreamBasicDescription));
if (err)
@ -327,7 +333,9 @@ UInt32 inNumberFrames,
NSLog(@"Failed to set StreamFormat for output on node %@ with %d", self, err);
return NO;
}
[self willInitializeNode];
AudioUnitInitialize(self.audioUnit);
[self didInitializeNode];
return YES;
}