2
votes

I am trying to create an audio graph with a mixer and an io unit. The io unit will receive audio from the microphone and send it to the mixer, which will mix it with an external sound, and play it back out the speaker. I have created my audio graph like below, tried to follow guidelines as much as possible. However, I keep getting error -10865 (kAudioUnitErr_PropertyNotWriteable) when I try to connect the mixer node to the output node. Could somebody clarify for me what is going wrong? I will include more code such as my callback and private variables if needed.

NSLog(@"Creating audio graph");
sampleRate = 44100.0;

// Will check results
OSStatus result;

// Create the graph
result = NewAUGraph(&graph);
if(result != noErr)
    NSLog(@"Failed creating graph");

result = AUGraphInitialize(graph);
if(result != noErr)
    NSLog(@"Failed to initialize audio graph Error code: %d '%.4s", (int) result, (const char *)&result);

// Create audio nodes
AudioComponentDescription ioDescription;
ioDescription.componentType = kAudioUnitType_Output;
ioDescription.componentSubType = kAudioUnitSubType_RemoteIO;
ioDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
ioDescription.componentFlagsMask = 0;
ioDescription.componentFlags = 0;

AudioComponentDescription mixerDescription;
mixerDescription.componentType = kAudioUnitType_Mixer;
mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDescription.componentFlagsMask = 0;
mixerDescription.componentFlags = 0;

// Add nodes to the graph
AUNode ioNode;
AUNode mixerNode;

result = AUGraphAddNode(graph, &ioDescription, &ioNode);
if(result != noErr)
    NSLog(@"Failed to add microphone node");
result = AUGraphAddNode(graph, &mixerDescription, &mixerNode);
if(result != noErr)
    NSLog(@"Failed to add mixer node");

// Open the graph
result = AUGraphOpen(graph);
if(result != noErr)
    NSLog(@"Failed to open graph");

// Get the IO node
result = AUGraphNodeInfo(graph, ioNode, NULL, &ioUnit);
if(result != noErr)
    NSLog(@"Failed to fetch info from io node");

// Enable IO on the io node
UInt32 flag = 1;
result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));
if(result != noErr)
    NSLog(@"Failed enabling IO on io unit");

// Get the mixer unit
result = AUGraphNodeInfo(graph, mixerNode, NULL, &mixerUnit);
if(result != noErr)
    NSLog(@"Failed to fetch info from mixer node");

// Set up the mixer unit bus count
UInt32 busCount = 2;
result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount));
if(result != noErr)
    NSLog(@"Failed to set property mixer input bus count");

// Attach render callback to sound effect bus
UInt32 soundEffectBus = 1;

AURenderCallbackStruct inputCallbackStruct;
inputCallbackStruct.inputProc = &inputRenderCallback;
inputCallbackStruct.inputProcRefCon = soundStruct;

result = AUGraphSetNodeInputCallback(graph, mixerNode, soundEffectBus, &inputCallbackStruct);
if(result != noErr)
    NSLog(@"Failed to set mixer node input callback for sound effect bus");

// Set stream format for sound effect bus
UInt32 bytesPerSample = sizeof (AudioUnitSampleType);

stereoDescription.mFormatID = kAudioFormatLinearPCM;
stereoDescription.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
stereoDescription.mBytesPerPacket = bytesPerSample;
stereoDescription.mFramesPerPacket = 1;
stereoDescription.mBytesPerFrame = bytesPerSample;
stereoDescription.mChannelsPerFrame = 2;
stereoDescription.mBitsPerChannel = 8 * bytesPerSample;
stereoDescription.mSampleRate = sampleRate;

result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, soundEffectBus, &stereoDescription, sizeof(stereoDescription));
if(result != noErr)
    NSLog(@"Failed to set stream description");

// Set mixer output sample rate
result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, sizeof(sampleRate));
if(result != noErr)
    NSLog(@"Failed to set mixer output sample rate");

// Connect input to mixer
result = AUGraphConnectNodeInput(graph, ioNode, 1, mixerNode, 0);
if(result != noErr)
    NSLog(@"Failed to connect microphone node to mixer node");

Error occurs here

// Connect mixer to output
result = AUGraphConnectNodeInput(graph, mixerNode, 0, ioNode, 0);
if(result != noErr)
    NSLog(@"Failed to connect mixer to output node %d", result);

// Initialize the audio graph
CAShow(graph);

// Start the graph
result = AUGraphStart(graph);
if(result != noErr)
    NSLog(@"Failed to start audio graph");

NSLog(@"Graph started");

EDIT

I was able to understand why I got this error, I think I was assigning my mixer output to the input channel of the io unit (which is read only, of course because it comes from the mic). However, after switching that, as I changed the code above, I have this error

ERROR:     [0x196f982a0] 308: input bus 0 sample rate is 0

Could anyone help me? Is there something I am forgetting to set?

1

1 Answers

1
votes

According to the error message, I suggest you explicitly set the stream format on every buses of the mixer (both input and output), just to be sure. Personally, I don't set kAudioUnitProperty_SampleRate on the mixer, I don't think it has a meaning there (IMHO, this is meaningful on a hardware IO unit to choose the sample rate of the DAC, it might also have a meaning of format converters units)