Search code examples
audiocore-audioaudiounitavaudioengine

AVAudioEngine crash on connect node


I've setup my AVAudioEngine in its own method like this:

AVAudioSession* session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:[session sampleRate] error:nil];
[session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[session setActive:YES error:nil];

audioEngine_ = [[AVAudioEngine alloc] init];

//tap the outputNode to create the singleton
[audioEngine_ outputNode];
NSError* error;
[audioEngine_ startAndReturnError:&error];

There is no error starting it up. I have another method to attach an AVAudioUnitMIDIInstrument from an AudioComponentDescription I've received from an Inter-App Audio instrument. My connect method looks like this:

-(void)connectInstrument:(AudioComponentDescription)desc {  
    instNode_ = nil;
    instNode_ = [[AVAudioUnitMIDIInstrument alloc] initWithAudioComponentDescription:desc];

    [audioEngine_ attachNode:instNode_];

    //Crashes here
    [audioEngine_ connect:instNode_ to:[audioEngine_ outputNode] format:nil];

    [audioEngine_ startAndReturnError:nil];

    NSLog(@"done connecting");
}

The crash gives me no useful information. I get this:

invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.

* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndexedSubscript:]: index 0 beyond bounds for empty array'

If I do a test and try to create a new mixer node and attach/connect it, there is no crash.

I have more relevant information
If I do this:

AVAudioFormat* instFormat = [instUnit_ outputFormatForBus:0];

I get the same error:
-[__NSArrayM objectAtIndexedSubscript:]: index 0 beyond bounds for empty array

It's almost as though there is no AVAudioFormat set for any output busses, nor any input busses (I checked inputFormatForBus as well). This is odd because if I do:

AudioStreamBasicDescription f1;
UInt32 outsize = sizeof(f1);
AudioUnitGetProperty(instNode_.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &f1, &outsize);

Then f1 is a valid AudioStreamBasicDescription showing a standard 44.1khz sample rate, 32 bit float, 2 channels. So there is an output stream format, but it doesn't seem to be attached to any output bus of the AVAudioUnitMIDIInstrument instance.

EDIT - Further information Even if I just try to access instUnite_.name I get the NSRangeException. I'm wondering now if this is a problem with how I'm getting the component description. I'm attempting to use Inter-App Audio (I have done all of the proper entitlements, capabilities, and app ID setup). This is how I am discovering Inter-App Audio components:

NSMutableArray* units = [[NSMutableArray alloc] init];
AudioComponentDescription searchDesc = { kAudioUnitType_RemoteInstrument, 0, 0, 0, 0 };

NSArray* componentArray = [[AVAudioUnitComponentManager sharedAudioUnitComponentManager] componentsMatchingDescription:searchDesc];

for(AVAudioUnitComponent* component in componentArray) {
    AudioComponentDescription description = component.audioComponentDescription;

    InterAppAudioUnit* unit = [[InterAppAudioUnit alloc] init];
    unit.componentDescription = description;
    unit.icon = AudioComponentGetIcon(component.audioComponent, 44.0f);
    unit.name = component.name;
    unit.avComponent = component;

    [units addObject:unit];
}

_units = units;
[self.tableView reloadData];

This is all in a presented UITableViewController. After clicking one I simply execute:

[self connectInstrument:unit.componentDescription];

If I instead hand build a component description for a local unit, the AVAudioUnit is instantiated just fine and nothing crashes.


Solution

  • I found the answer. The AUAudioUnit object inside of the AVAudioUnit object has no output busses upon creation. I'm not sure why. You can fix it by doing this:

    AUAudioUnit* auInst = instUnit_.AUAudioUnit;
    NSMutableArray<AUAudioUnitBus*>* busArray = [[NSMutableArray alloc] init];
    AVAudioFormat* outFormat = [[self->audioEngine_ outputNode] outputFormatForBus:0];
    AUAudioUnitBus* bus = [[AUAudioUnitBus alloc] initWithFormat:outFormat error:&err];
    [busArrays addObject:bus];
    [[auInst outputBusses] replaceBusses:busArray];