title: How to record sound produced by mixer unit output (iOS Core Audio & Audio Graph)
body: <p>I'm trying to record sound produced by a mixer unit output.</p>
  
  <p>For the moment, my code is based on the <a href="http://developer.apple.com/library/ios/#samplecode/MixerHost/Introduction/Intro.html">apple MixerHost iOS app</a> demo : A <strong>mixer node</strong> is connected to a <strong>remote IO node</strong> on the audio graphe.</p>
  
  <p>And i try to set an <em>input callback</em> on the <strong>remote IO node</strong> input on the mixer output.</p>
  
  <p>I do something wrong but I can not find the error.</p>
  
  <p>Here is the code below. This is done just after the Multichannel Mixer unit Setup :</p>
  
  <pre><code>UInt32 flag = 1;
  
  // Enable IO for playback
  result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 
                                0, // Output bus
                                &amp;flag, 
                                sizeof(flag));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;}
  
  /* can't do that because *** AudioUnitSetProperty EnableIO error: -1073752493 00000000
  result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 
                                0, // Output bus
                                &amp;flag, 
                                sizeof(flag));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;}
  */
  </code></pre>
  
  <p>Then create a stream format :</p>
  
  <pre><code>// I/O stream format
  iOStreamFormat.mSampleRate          = 44100.0;
  iOStreamFormat.mFormatID            = kAudioFormatLinearPCM;
  iOStreamFormat.mFormatFlags         = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  iOStreamFormat.mFramesPerPacket     = 1;
  iOStreamFormat.mChannelsPerFrame    = 1;
  iOStreamFormat.mBitsPerChannel      = 16;
  iOStreamFormat.mBytesPerPacket      = 2;
  iOStreamFormat.mBytesPerFrame       = 2;
  
  [self printASBD: iOStreamFormat];
  </code></pre>
  
  <p>Then affect the format and specify sample rate :</p>
  
  <pre><code>result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
                                1, // Input bus 
                                &amp;iOStreamFormat, 
                                sizeof(iOStreamFormat));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;}
  
  result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
                                0, // Output bus 
                                &amp;iOStreamFormat, 
                                sizeof(iOStreamFormat));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;}
  
  // SampleRate I/O 
  result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input,
                                 0, // Output
                                 &amp;graphSampleRate,
                                 sizeof (graphSampleRate));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty (set I/O unit input stream format)" withStatus: result]; return;}
  </code></pre>
  
  <p>Then, i try to set the render callback.</p>
  
  <p><strong>Solution 1 >>> my recording callback is never called</strong></p>
  
  <pre><code>effectState.rioUnit = iOUnit;
  
  AURenderCallbackStruct renderCallbackStruct;
  renderCallbackStruct.inputProc        = &amp;recordingCallback;
  renderCallbackStruct.inputProcRefCon  = &amp;effectState;
  result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
                                 0, // Output bus
                                 &amp;renderCallbackStruct,
                                 sizeof (renderCallbackStruct));
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty SetRenderCallback" withStatus: result]; return;}
  </code></pre>
  
  <p><strong>Solution 2 >>> my app crashes at launch on this</strong></p>
  
  <pre><code>AURenderCallbackStruct renderCallbackStruct;
  renderCallbackStruct.inputProc        = &amp;recordingCallback;
  renderCallbackStruct.inputProcRefCon  = &amp;effectState;
  
  result = AUGraphSetNodeInputCallback (processingGraph, iONode,
                                         0, // Output bus
                                         &amp;renderCallbackStruct);
  if (noErr != result) {[self printErrorMessage: @"AUGraphSetNodeInputCallback (I/O unit input callback bus 0)" withStatus: result]; return;}
  </code></pre>
  
  <p>If anyone have an idea ...</p>
  
  <p><strong>EDIT Solution 3 (thanks to arlo anwser) >> There is now a format problem</strong></p>
  
  <pre><code>AudioStreamBasicDescription dstFormat = {0};
  dstFormat.mSampleRate=44100.0;
  dstFormat.mFormatID=kAudioFormatLinearPCM;
  dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
  dstFormat.mBytesPerPacket=4;
  dstFormat.mBytesPerFrame=4;
  dstFormat.mFramesPerPacket=1;
  dstFormat.mChannelsPerFrame=2;
  dstFormat.mBitsPerChannel=16;
  dstFormat.mReserved=0;
  
  result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
                       1, 
                       &amp;stereoStreamFormat, 
                       sizeof(stereoStreamFormat));
  
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;}
  
  
  result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
                       0, 
                       &amp;stereoStreamFormat, 
                       sizeof(stereoStreamFormat));
  
  if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;}
  
  
  AudioUnitAddRenderNotify(
                           iOUnit,
                           &amp;recordingCallback,
                           &amp;effectState
                           );
  </code></pre>
  
  <p>and the file setup :</p>
  
  <pre><code>if (noErr != result) {[self printErrorMessage: @"AUGraphInitialize" withStatus: result]; return;}
  
  // On initialise le fichier audio
  NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];
  NSString *destinationFilePath = [[[NSString alloc] initWithFormat: @"%@/output.caf", documentsDirectory] autorelease];
  NSLog(@"&gt;&gt;&gt; %@", destinationFilePath);
  CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)destinationFilePath, kCFURLPOSIXPathStyle, false);
  
  OSStatus setupErr = ExtAudioFileCreateWithURL(destinationURL, kAudioFileWAVEType, &amp;dstFormat, NULL, kAudioFileFlags_EraseFile, &amp;effectState.audioFileRef);  
  CFRelease(destinationURL);
  NSAssert(setupErr == noErr, @"Couldn't create file for writing");
  
  setupErr = ExtAudioFileSetProperty(effectState.audioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &amp;stereoStreamFormat);
  NSAssert(setupErr == noErr, @"Couldn't create file for format");
  
  setupErr =  ExtAudioFileWriteAsync(effectState.audioFileRef, 0, NULL);
  NSAssert(setupErr == noErr, @"Couldn't initialize write buffers for audio file");
  </code></pre>
  
  <p>And the recording callback :</p>
  
  <pre><code>static OSStatus recordingCallback       (void *                         inRefCon,
                                AudioUnitRenderActionFlags *      ioActionFlags,
                                const AudioTimeStamp *            inTimeStamp,
                                UInt32                            inBusNumber,
                                UInt32                            inNumberFrames,
                                AudioBufferList *                 ioData) {
  if (*ioActionFlags == kAudioUnitRenderAction_PostRender &amp;&amp; inBusNumber == 0) 
  {
      EffectState *effectState = (EffectState *)inRefCon;
  
      ExtAudioFileWriteAsync(effectState-&gt;audioFileRef, inNumberFrames, ioData);
  }
  return noErr;     
  }
  </code></pre>
  
  <p>There is something missing in the output file <a href="http://www.iphone.re/audio/output.caf">output.caf</a> :).
  I'm totally lost in formats to apply.</p>