2011-08-06 132 views
3

我做了一个简单的应用程序,当超过特定的噪音水平时播放警报。所以,所以我有一个记录声音和仪表录制的声音水平(如下图所示唯一重要的部分代码)的AudioQueue:ios:在主线程外播放声音

#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]; 
} 

那么基本上在这里发生如下:

我轻按iPhone屏幕上的一个按钮,导致音频从iPhone中的麦克风录制声音。当一个队列已满时,调用回调函数“HandleInputBuffer”来处理音频缓冲区中的数据。处理数据意味着我要测量声音强度。如果强度超过偏差,则会调用“playAlarmSound”方法。

所以这个调用发生在主线程之外,即在一个额外的线程中。

对象“SP”(声音播放)具有以下实现:

//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]; 
    } 
} 

实际上,代码工作正常,所以应该在代码中没有错误,也波形文件的格式是正确的。这是什么情况:

  1. iPhone模拟器: 一切工作正常。创建对象SoundPlayer时,在构造函数中调用[self playSound]会导致模拟器播放随机声音,以便证明该方法已正确实施。然后我开始声音记录,当超过某个声级时,方法从AudioRecorder(在一个单独的线程中)被调用,并且声音也被播放。所以这是所需的行为。

  2. 实际的iPhone设备: 当创建对象SoundPlayer时播放随机声音,所以这工作正常。但是当我开始录音并且超过噪音水平时,Soundplayer中的playSound方法被AudioRecorder调用,但没有播放声音。我怀疑这是因为这发生在一个单独的线程中。但我不知道如何修复。

我已经尝试通过使用默认通知中心的通知来修复它,但它也没有工作。我如何管理声音播放?

回答

3

几件事情要考虑:

1)确保你能玩&记录:

UInt32 category = kAudioSessionCategory_PlayAndRecord; 
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
         sizeof(category), &category); 

2)检查-(void)playSound各种AudioServices*调用的返回值真正看到它在那里失败。

3)确保在课堂AudioRecorder的sp属性正确初始化(即调用AudioRecorder的正确init方法)

4)这是可能的(一些)的iOS设备不支持不同的采样同时播放的速率为&。如果您的.wav文件的采样率与您录制的AudioQueue不同,那么可以尝试使用其中一种。

5),而不是调用[sp playSound]使用此语句来调用它在主线程:

[sp performSelectorOnMainThread:@selector(playSound) withObject:nil waitUntilDone:NO]; 

还要注意的是最有可能的,你的报警声将在录音听见(虽然iPhone 4可以过滤它出)。