title: Triggering UI code from Audio Unit Render Proc on iOS
body: <p>I have a Multichannel Mixer audio unit playing back audio files in an iOS app, and I need to figure out how to update the app's UI and perform a reset when the render callback hits the end of the longest audio file (which is set up to run on bus 0). As my code below shows I am trying to use KVO to achieve this (using the boolean variable <code>tapesUnderway</code> - the AutoreleasePool is necessary as this Objective-C code is running outside of its normal domain, see <a href="http://www.cocoabuilder.com/archive/cocoa/57412-nscfnumber-no-pool-in-place-just-leaking.html" rel="nofollow">http://www.cocoabuilder.com/archive/cocoa/57412-nscfnumber-no-pool-in-place-just-leaking.html</a>).</p>
  
  <pre><code>static OSStatus tapesRenderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
  {
      SoundBufferPtr sndbuf = (SoundBufferPtr)inRefCon;
  
      UInt32 bufferFrames = sndbuf[inBusNumber].numFrames;
      AudioUnitSampleType *in = sndbuf[inBusNumber].data; 
  
      // These mBuffers are the output buffers and are empty; these two lines are just  setting the references to them (via outA and outB)
      AudioUnitSampleType *outA = (AudioUnitSampleType *)ioData-&gt;mBuffers[0].mData;
      AudioUnitSampleType *outB = (AudioUnitSampleType *)ioData-&gt;mBuffers[1].mData;
  
      UInt32 sample = sndbuf[inBusNumber].sampleNum;
  
  
      // --------------------------------------------------------------
      // Set the start time here
      if(inBusNumber == 0 &amp;&amp; !tapesFirstRenderPast)
      {
          printf("Tapes first render past\n");
  
          tapesStartSample = inTimeStamp-&gt;mSampleTime;
          tapesFirstRenderPast = YES;                     // MAKE SURE TO RESET THIS ON SONG RESTART
          firstPauseSample = tapesStartSample;
      }
  
      // --------------------------------------------------------------
      // Now process the samples
       for(UInt32 i = 0; i &lt; inNumberFrames; ++i)
       {
           if(inBusNumber == 0)
           {
              // ------------------------------------------------------
              // Bus 0 is the backing track, and is always playing back
  
              outA[i] = in[sample++];
              outB[i] = in[sample++];     // For stereo set desc.SetAUCanonical to (2, true) and increment samples in both output calls
  
              lastSample = inTimeStamp-&gt;mSampleTime + (Float64)i;     // Set the last played sample in order to compensate for pauses
  
  
              // ------------------------------------------------------
              // Use this logic to mark end of tune
              if(sample &gt;= (bufferFrames * 2) &amp;&amp; !tapesEndPast)
              {
                  // USE KVO TO NOTIFY METHOD OF VALUE CHANGE
  
                  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                  FuturesEPMedia *futuresMedia = [FuturesEPMedia sharedFuturesEPMedia];
                  NSNumber *boolNo = [[NSNumber alloc] initWithBool: NO];
                  [futuresMedia setValue: boolNo forKey: @"tapesUnderway"];
                  [boolNo release];
                  [pool release];
  
                  tapesEndPast = YES;
              }
          }
          else
          {
              // ------------------------------------------------------
              // The other buses are the open sections, and are synched through the tapesSectionsTimes array
  
              Float64 sectionTime = tapesSectionTimes[inBusNumber] * kGraphSampleRate;        // Section time in samples
              Float64 currentSample = inTimeStamp-&gt;mSampleTime + (Float64)i;
  
              if(!isPaused &amp;&amp; !playFirstRenderPast)
              {
                  pauseGap += currentSample - firstPauseSample;
                  playFirstRenderPast = YES;
                  pauseFirstRenderPast = NO;
              }
  
  
              if(currentSample &gt; (tapesStartSample + sectionTime + pauseGap) &amp;&amp; sample &lt; (bufferFrames * 2))
              {
                  outA[i] = in[sample++];
                  outB[i] = in[sample++];
              }
              else
              {
                  outA[i] = 0;
                  outB[i] = 0;
              }
          }
      }
  
     sndbuf[inBusNumber].sampleNum = sample;
  
     return noErr;
  }
  </code></pre>
  
  <p>At the moment when this variable is changed it triggers a method in self, but this leads to an unacceptable delay (20-30 seconds) when executed from this render callback (I am thinking because it is Objective-C code running in the high priority audio thread?). How do I effectively trigger such a change without the delay? (The trigger will change a pause button to a play button and call a reset method to prepare for the next play.)</p>
  
  <p>Thanks</p>