我做了一个简单的应用程序,当超过一定的噪音水平时会发出警报。所以我有一个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];
}
}
实际上代码工作正常,因此代码中应该没有错误,波形文件的格式也是正确的。这就是发生的事情:
iPhone模拟器: 一切正常。创建对象SoundPlayer时,在构造函数中对[self playSound]的调用会使模拟器播放随机声音,从而证明该方法已正确实现。然后我开始录音,当超过某个声级时,从AudioRecorder(在一个单独的线程中)调用该方法,并且还播放声音。所以这是理想的行为。
实际iPhone设备: 创建对象SoundPlayer时会播放随机声音,因此可以正常工作。但是当我开始录音并且超出噪音水平时,SoundBook中的方法playSound会被AudioRecorder调用,但不会播放声音。我怀疑这是因为这发生在一个分离的线程中。但我不知道如何修复它。
我已经尝试通过默认通知中心使用通知来修复它,但它也无法正常工作。如何管理声音播放?
答案 0 :(得分:3)
要考虑的几件事情:
1)确保启用播放和录制:
UInt32 category = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
sizeof(category), &category);
2)检查AudioServices*
中各种-(void)playSound
来电的返回值,看看它实际失败的位置。
3)确保正确初始化类AudioRecorder中的sp
属性(即您调用AudioRecorder的正确init方法)
4)(某些)iOS设备可能不支持同时录制和不同的采样率。回放。如果.wav文件的采样率与录制的AudioQueue不同,那么值得尝试调整其中任何一个。
5)而不是调用[sp playSound]
使用此语句在主线程上调用它:
[sp performSelectorOnMainThread:@selector(playSound) withObject:nil waitUntilDone:NO];
另请注意,最有可能的是,录音时会听到您的闹钟声(虽然iPhone 4可以将其过滤掉)。