iOS:在主线程之外播放声音

3
我制作了一个简单的应用程序,当某个噪音水平超过设定值时会播放警报。因此,我有一个AudioQueue来记录声音并测量记录声音的级别(只显示重要的代码部分如下所示):
#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”(SoundPlayer)具有以下实现:
//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对象被创建时,会播放随机声音,所以这也是正常的。但是当我开始录音并且超过噪音级别时,AudioRecorder会调用Soundplayer中的playSound方法但没有声音播放。我认为这是因为它发生在一个分离的线程中,但我不知道如何修复它。

我已经尝试使用默认通知中心的通知来进行修复,但也没有成功。怎样才能让声音播放?

1个回答

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可以过滤掉它)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接