title: Apple CoreAudio - Why does AUGraphStop() take 25ms to complete?
body: <p>Our audio app is using AUGraphs containing a mixer unit, a converter unit and an output unit. It's a realtime app, so performance is a high concern.</p>
  
  <p>An issue has been flagged up where AUGraphStop() takes 25 milliseconds to complete on the main thread, and our profiler shows that it spends this time sleeping. Could anyone explain why this happens? Is it waiting for the next zero crossover point, or waiting for one more buffer to render?</p>
  
  <p>I've tried several workarounds, including sending one render frame of silence (and setting the kAudioUnitRenderAction_OutputIsSilence flag) before attempting to stop, and calling AUGraphStop() inside the kAudioUnitRenderAction_PostRender notify callback (this has mixed results, and after reading around it doesn't seem a recommended approach).</p>
  
  <p>I've tried lowering the number of frames per slice, but it doesn't seem to make a difference. I've also tried removing all audio units except the output node, in an attempt to narrow the problem down to a specific unit, but the cost of AUGraphStop() still stands at 25ms.</p>
  
  <p>Here's how we are initialising the graph:</p>
  
  <pre><code>//Configure converter/mixer/output unit descriptors
  AudioComponentDescription OutputUnitDesc = { kAudioUnitType_Output ,kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple };
  AudioComponentDescription MixerUnitDesc = { kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple };
  AudioComponentDescription ConverterUnitDesc = { kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple };
  
  //Create graph
  CA_ERR(NewAUGraph(&amp;mAudioGraph));
  
  //Add converter/mixer/output nodes
  CA_ERR(AUGraphAddNode(mAudioGraph, &amp;OutputUnitDesc, &amp;mOutputNode));
  CA_ERR(AUGraphAddNode(mAudioGraph, &amp;MixerUnitDesc, &amp;mMixerNode));
  CA_ERR(AUGraphAddNode(mAudioGraph, &amp;ConverterUnitDesc, &amp;mConverterNode));
  
  //Connect nodes
  CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mConverterNode, 0, mMixerNode, 0));
  CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mMixerNode, 0, mOutputNode, 0));
  
  //Open the graph (instantiates the units)
  CA_ERR(AUGraphOpen(mAudioGraph));
  
  //Get the created units
  CA_ERR(AUGraphNodeInfo(mAudioGraph, mOutputNode, NULL, &amp;mOutputUnit));
  CA_ERR(AUGraphNodeInfo(mAudioGraph, mMixerNode, NULL, &amp;mMixerUnit));
  CA_ERR(AUGraphNodeInfo(mAudioGraph, mConverterNode, NULL, &amp;mConverterUnit));
  
  //Setup stream format description
  mStreamDesc.mFormatID = kAudioFormatLinearPCM;
  mStreamDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
  mStreamDesc.mChannelsPerFrame = Header-&gt;nChannels;
  mStreamDesc.mSampleRate = (Float64)Header-&gt;nSamplesPerSec;
  mStreamDesc.mBitsPerChannel = Header-&gt;wBitsPerSample;
  mStreamDesc.mBytesPerFrame = (Header-&gt;wBitsPerSample &gt;&gt; 3) * Header-&gt;nChannels;
  mStreamDesc.mFramesPerPacket = 1;
  mStreamDesc.mBytesPerPacket = mStreamDesc.mBytesPerFrame * mStreamDesc.mFramesPerPacket;
  mStreamDesc.mReserved = 0;
  
  //Set data endianness according to file type - TODO: Get endianness from header
  AudioSystem::FileType FileType = mSample-&gt;GetFiletype();
  
  if(FileType == AudioSystem::WAV)
          mStreamDesc.mFormatFlags |= kAudioFormatFlagsNativeEndian;
  else if(FileType == AudioSystem::OGG)
          mStreamDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
  
  //Configure number of input/output busses for mixer unit
  int NumChannelsIn = Header-&gt;nChannels;
  int NumChannelsOut = (int)AudioSystem::GetOutputChannelConfig();
  CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &amp;NumChannelsIn, sizeof(u32)));
  CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &amp;NumChannelsOut, sizeof(u32)));
  
  //Set render callback
  AURenderCallbackStruct callback = { AudioRenderCallback, this };
  CA_ERR(AUGraphSetNodeInputCallback(mAudioGraph, mConverterNode, 0, &amp;callback));
  
  //Set stream format to something native to CoreAudio
  AudioStreamBasicDescription OutputDesc = {0};
  UInt32 Size = sizeof(AudioStreamBasicDescription);
  CA_ERR(AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &amp;OutputDesc, &amp;Size));
  
  //Set num output channels
  OutputDesc.mChannelsPerFrame = (int)AudioSystem::GetOutputChannelConfig();
  
  //Set stream format
  CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &amp;mStreamDesc, sizeof(AudioStreamBasicDescription)));
  CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &amp;OutputDesc, sizeof(AudioStreamBasicDescription)));
  CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &amp;OutputDesc, sizeof(AudioStreamBasicDescription)));
  CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &amp;OutputDesc, sizeof(AudioStreamBasicDescription)));
  CA_ERR(AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &amp;OutputDesc, sizeof(AudioStreamBasicDescription)));
  
  //Initialise graph
  CA_ERR(AUGraphInitialize(mAudioGraph));
  
  //Set notification callback
  CA_ERR(AUGraphAddRenderNotify(mAudioGraph, AudioNotifyCallback, this));
  
  //Set global mixer volume
  CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0));
  
  //Set input channel volumes
  for(int i = 0; i &lt; Header-&gt;nChannels; i++)
  {
          CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, i, 1.0, 0));
  }
  
  //Set output channel volumes
  for(int i = 0; i &lt; (int)AudioSystem::GetOutputChannelConfig(); i++)
  {
          CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, i, 1.0, 0));
  }
  </code></pre>
  
  <p>The graph is started and stopped using AUGraphStart() and AUGraphStop(), nothing special. Here's the callstack captured using Shark Profiler:</p>
  
  <pre><code>| | | | | | | | | + 1.3%, AudioUnitGraph::Stop(), AudioToolbox
  | | | | | | | | | | + 1.3%, AudioOutputUnitStop, AudioUnit
  | | | | | | | | | | | + 1.3%, CallComponentDispatch, CarbonCore
  | | | | | | | | | | | | + 1.3%, DefaultOutputAUEntry, CoreAudio
  | | | | | | | | | | | | | + 1.3%, AUHALEntry, CoreAudio
  | | | | | | | | | | | | | | + 1.3%, usleep$UNIX2003, libSystem.B.dylib
  | | | | | | | | | | | | | | | + 1.3%, nanosleep$UNIX2003, libSystem.B.dylib
  | | | | | | | | | | | | | | | |   1.3%, __semwait_signal, libSystem.B.dylib
  </code></pre>
  
  <p>It spends the entirety of AUGraphStop() just sleeping!</p>
  
  <p>Any clues? </p>