-(void)makeGraph { XThrowIfError (NewAUGraph (&fGraph), "NewAUGraph"); CAComponentDescription cd; UInt32 numChannels = 0; // output node cd.componentType = kAudioUnitType_Output; cd.componentSubType = kAudioUnitSubType_DefaultOutput; cd.componentManufacturer = kAudioUnitManufacturer_Apple; AUNode outputNode; XThrowIfError (AUGraphNewNode (fGraph, &cd, 0, NULL, &outputNode), "AUGraphNewNode"); // varispeed AU node AUNode timepitchNode; cd.componentType = kAudioUnitType_FormatConverter; cd.componentSubType = kAudioUnitSubType_TimePitch; XThrowIfError (AUGraphNewNode (fGraph, &cd, 0, NULL, &timepitchNode), "AUGraphNewNode"); // file AU node AUNode fileNode; cd.componentType = kAudioUnitType_Generator; cd.componentSubType = kAudioUnitSubType_AudioFilePlayer; XThrowIfError (AUGraphNewNode (fGraph, &cd, 0, NULL, &fileNode), "AUGraphNewNode"); // connect & setup XThrowIfError (AUGraphOpen (fGraph), "AUGraphOpen"); // install overload listener to detect when something is wrong AudioUnit theFileAU; XThrowIfError (AUGraphGetNodeInfo(fGraph, fileNode, NULL, NULL, NULL, &theFileAU), "AUGraphGetNodeInfo"); // here's where we connect the local fileNode reference to the global AUFile fFileAU = CAAudioUnit (fileNode, theFileAU); // install overload listener to detect when something is wrong AudioUnit theTimepitchAU; XThrowIfError (AUGraphGetNodeInfo(fGraph, timepitchNode, NULL, NULL, NULL, &theTimepitchAU), "AUGraphGetNodeInfo"); // here's where we connect the local timepitchNode reference to the global AUVarispeed fTimepitchAU = CAAudioUnit (timepitchNode, theTimepitchAU); /*** using SetFormat seems to cause an exception, any idea why? ***/ // XThrowIfError( fFileAU.SetFormat( kAudioUnitScope_Input, 0, fFileFormat ), "SetFormat" ); // printf ("makeGraph file in: "); // XThrowIfError( fFileAU.SetFormat( kAudioUnitScope_Output, 0, fFileFormat ), "SetFormat" ); // printf ("makeGraph file out: "); // XThrowIfError( fTimepitchAU.SetFormat( kAudioUnitScope_Input, 0, fFileFormat ), "SetFormat" ); // printf ("makeGraph time in: "); // XThrowIfError( fTimepitchAU.SetFormat( kAudioUnitScope_Output, 0, fFileFormat ), "SetFormat" ); /*** commenting-out these lines results in successful playback of multichannel audio, why? ***/ // prepare the file AU for playback // set its output channels // XThrowIfError (fFileAU.SetNumberChannels (kAudioUnitScope_Output, 0, fFileFormat.NumberChannels()), "SetNumberChannels"); // set the output sample rate of the file AU to be the same as the file: // XThrowIfError (fFileAU.SetSampleRate (kAudioUnitScope_Output, 0, fFileFormat.mSampleRate), "SetSampleRate"); // load in the file XThrowIfError (fFileAU.SetProperty(kAudioUnitProperty_ScheduledFileIDs, kAudioUnitScope_Global, 0, &fAudioFile, sizeof(fAudioFile)), "SetScheduleFile"); /*** commenting-out these lines results in successful playback of multichannel audio, why? ***/ // set the input number of channels of the varispeed AU to be the same as the output of the file AU: // XThrowIfError (fTimepitchAU.SetNumberChannels (kAudioUnitScope_Input, 0, fFileFormat.NumberChannels()), "SetNumberChannels"); // set the output number of channels of the varispeed AU to be the same as the output of the file AU: // XThrowIfError (fTimepitchAU.SetNumberChannels (kAudioUnitScope_Output, 0, fFileFormat.NumberChannels()), "SetNumberChannels"); // set the input sample rate of the varispeed AU to be the same as the output of the file AU(ok I'm cheating, I'm getting the file format sample rate): // XThrowIfError (fTimepitchAU.SetSampleRate (kAudioUnitScope_Input, 0, fFileFormat.mSampleRate), "SetSampleRate"); // connect file node to varispeed node XThrowIfError (AUGraphConnectNodeInput (fGraph, fileNode, 0, timepitchNode, 0), "AUGraphConnectNodeInput"); // connect varispeed node to output node XThrowIfError (AUGraphConnectNodeInput (fGraph, timepitchNode, 0, outputNode, 0), "AUGraphConnectNodeInput"); // AT this point we make sure we have the file player AU initialized // this also propogates the output format of the AU to the output unit XThrowIfError (AUGraphInitialize (fGraph), "AUGraphInitialize"); // workaround a race condition in the file player AU usleep (10 * 1000); // if we have a surround file, then we should try to tell the output AU what the order of the channels will be if (fFileFormat.NumberChannels() > 2) { UInt32 layoutSize = 0; OSStatus err; // NSLog(@"before AudioFileGetPropertyInfo kAudioFilePropertyChannelLayout"); NSLog(@"layoutSize %d", layoutSize); try { XThrowIfError (err = AudioFileGetPropertyInfo (fAudioFile, kAudioFilePropertyChannelLayout, &layoutSize, NULL), "kAudioFilePropertyChannelLayout"); } catch(CAXException) { NSLog(@"caught CAXException at AudioFileGetPropertyInfo"); } if (!err && layoutSize) { char* layout = new char[layoutSize]; err = AudioFileGetProperty(fAudioFile, kAudioFilePropertyChannelLayout, &layoutSize, layout); XThrowIfError (err, "Get Layout From AudioFile"); // ok, now get the output AU and set its layout XThrowIfError (AUGraphGetNodeInfo(fGraph, outputNode, NULL, NULL, NULL, &theFileAU), "AUGraphGetNodeInfo"); delete [] layout; } } XThrowIfError(AudioUnitAddRenderNotify (theFileAU, MyRenderNotification, self), "AudioUnitAddRenderNotify"); }