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):
#import "AudioRecorder.h"
#include <AudioToolbox/AudioToolbox.h>
#include <iostream>
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, &size );
AudioQueueLevelMeterState* meterState = reinterpret_cast<AudioQueueLevelMeterState*>(levelMeterData);
cout << "mAveragePower = " << meterState->mAveragePower << endl;
cout << "mPeakPower = " << meterState->mPeakPower << endl;
if( meterState->mPeakPower > 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];
}
So what basically happens here is the following:
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.
So this invocation happens outside the main thread i.e. in an extra thread.
The object "sp" (SoundPlayer) has the following implementation:
//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, &outSystemSoundID );
AudioServicesAddSystemSoundCompletion ( outSystemSoundID, 0, 0, soundDidFinishPlaying, 0 );
self.soundIsPlaying = YES;
AudioServicesPlaySystemSound( outSystemSoundID );
[pool drain];
}
}
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:
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.
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.
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?