title: ios: Play sound outside the main thread
body: <p>I made a simple app that plays an alarm when a certain noise level is exceeded. So therefore I have an AudioQueue that records sound and meters the level of the recorded sound (only important code parts shown below):</p>
  
  <pre><code>#import "AudioRecorder.h"
  
  #include &lt;AudioToolbox/AudioToolbox.h&gt;
  
  #include &lt;iostream&gt;
  
  using namespace std;
  
  @implementation AudioRecorder
  
  @synthesize sp; //custom object SoundPlayer
  @synthesize bias; //a bias, if the soundlevel exeeds this bias something happens
  
  AudioRecorder* ar;    
  
  //callback function to handle the audio data contained in the audio buffer.
  //In my case it is called every 0.5 seconds
  static void HandleInputBuffer (...) {
      ...
      char* levelMeterData = new char[size];
      AudioQueueGetProperty ( inAQ, kAudioQueueProperty_CurrentLevelMeter, levelMeterData, &amp;size );
      AudioQueueLevelMeterState* meterState =  reinterpret_cast&lt;AudioQueueLevelMeterState*&gt;(levelMeterData);
      cout &lt;&lt; "mAveragePower = " &lt;&lt; meterState-&gt;mAveragePower &lt;&lt; endl;
      cout &lt;&lt; "mPeakPower = " &lt;&lt; meterState-&gt;mPeakPower &lt;&lt; endl;
      if( meterState-&gt;mPeakPower &gt; ar.bias )
          [ar playAlarmSound];
  
  }
  ...
  //The constructor of the AudioRecorder class
  -(id) init {
      self = [super init];
      if( self ) {
          ar = self;
          sp = [[SoundPlayer alloc] init];
      }
      return self;
  }
  
  -(void)playAlarmSound {
      [sp playSound];
  }
  </code></pre>
  
  <p>So what basically happens here is the following: </p>
  
  <p>I tap on the iphone screen on a button that causes the audioqueue to record sound from the mic in the iphone. When a queue is full the callback function "HandleInputBuffer" gets called to handle the data in the audio buffer. Handling the data means in my particular case that I want to measure the sound intensity. If the intensity exceeds a bias the method "playAlarmSound" get invoked. </p>
  
  <p>So this invocation happens outside the main thread i.e. in an extra thread. </p>
  
  <p>The object "sp" (SoundPlayer) has the following implementation:</p>
  
  <pre><code>//callback function. Gets called when the sound has finished playing
  void soundDidFinishPlaying( SystemSoundID ssID, void *clientData ) {
      NSLog(@"Finished playing system sound");
      sp.soundIsPlaying = NO;
  }
  
  -(id)init {
      self = [super init];
      if(self) {
          self.soundIsPlaying = NO;
          srand(time(NULL));
          soundFilenames = [[NSArray alloc] initWithObjects:@"Alarm Clock Bell.wav", @"Bark.wav", @"Cartoon Boing.wav", @"Chimpanzee Calls.wav", @"School Bell Ringing.wav", @"Sheep Bah.wav", @"Squeeze Toy.wav", @"Tape Rewinding.wav", nil];
          [self copySoundsIfNeeded];
          sp = self;
          [self playSound]; //gets played without any problems
      }
      return self;
  }
  
  -(void)playSound {
      if( !self.soundIsPlaying ) {
          NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
          int index = rand() % [soundFilenames count];
          NSString* filePath = [ [self getBasePath] stringByAppendingPathComponent: [soundFilenames objectAtIndex:index] ];
  
          NSFileManager *fileManager = [NSFileManager defaultManager];
          if( [fileManager fileExistsAtPath:filePath] )
              NSLog(@"File %@ exists", [soundFilenames objectAtIndex:index]);
          else
              NSLog(@"File %@ NOT exists", [soundFilenames objectAtIndex:index]);
  
          CFURLRef url = CFURLCreateWithFileSystemPath ( 0, (CFStringRef) filePath, kCFURLPOSIXPathStyle, NO );
          SystemSoundID outSystemSoundID;
          AudioServicesCreateSystemSoundID( url, &amp;outSystemSoundID );
          AudioServicesAddSystemSoundCompletion ( outSystemSoundID, 0, 0, soundDidFinishPlaying, 0 );
          self.soundIsPlaying = YES;
          AudioServicesPlaySystemSound( outSystemSoundID );
          [pool drain];
      }
  }
  </code></pre>
  
  <p>Actually the code works fine, so there should be no errors in the code, also the format of the wave files is correct. And this is what happens:</p>
  
  <ol>
  <li><p>iPhone Simulator:
  Everything works fine. When the object SoundPlayer is created the call to [self playSound] in the constructor causes the simulator to play a random sound, so that proves that the method is correctly implemented. Then I start soundrecording and when a certain soundlevel is exceeded the method gets invoked from AudioRecorder (in a separated thread) and the sounds are also being played. So this is the desired behavior.</p></li>
  <li><p>Actual iPhone device:
  When the object SoundPlayer is created a random sound is played, so this works fine. BUT when I start audio recording and a noise level is exceeded the method playSound in Soundplayer gets invoked by AudioRecorder but no sound is played. I suspect this is because this happens in a separated thread. But I have no idea how this could be repaired.</p></li>
  </ol>
  
  <p>I already tried to repair it by using notifications form the default notification center but it didn't work either. How can I manage that the sound gets played?</p>