iOS 设备 检测声音输出设备及耳机麦克风的处理
2013-12-04 17:31
435 查看
1.
检测声音输入设备
-(BOOL)hasMicphone{
return[[AVAudioSessionsharedInstance]inputIsAvailable];
}
2.
检测声音输出设备
对于输出设备的检测,我们只考虑了2个情况,一种是设备自身的外放(iTouch/iPad/iPhone都有),一种是当前是否插入了带外放的耳机。iOS已经提供了相关方法用于获取当前的所有声音设备,我们只需要检查在这些设备中是否存在我们所关注的那几个就可以了。获取当前所有声音设备:
CFStringRef
route;
UInt32propertySize=sizeof(CFStringRef);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute,&propertySize,&route);
在iOS上所有可能的声音设备包括:
每一项的具体代表的设备请查考iOS文档,此处我们关注的是是否有耳机,所以只需要检查在route中是否有Headphone或Headset存在,具体方法如下:
-
(BOOL)hasHeadset{
#ifTARGET_IPHONE_SIMULATOR
#warning***Simulatormode:audiosessioncodeworksonlyonadevice
returnNO;
#else
CFStringRefroute;
UInt32propertySize=sizeof(CFStringRef);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute,&propertySize,&route);
if((route==NULL)||(CFStringGetLength(route)==0)){
//SilentMode
NSLog(@”AudioRoute:SILENT,donothing!”);
}else{
NSString*routeStr=(NSString*)route;
NSLog(@”AudioRoute:%@”,routeStr);
NSRangeheadphoneRange=[routeStrrangeOfString:@"Headphone"];
NSRangeheadsetRange=[routeStrrangeOfString:@"Headset"];
if(headphoneRange.location!=NSNotFound){
returnYES;
}elseif(headsetRange.location!=NSNotFound){
returnYES;
}
}
returnNO;
#endif
}
请注意,由于获取AudioRoute的相关方法不能再simulator上运行(会直接crush),所以必须先行处理。
3.
设置声音输出设备
在我们的项目中,存在当正在播放时用户会插入或拔出耳机的情况。如果是播放时用户插入了耳机,苹果会自动将声音输出指向到耳机并自动将音量调整为合适大小;如果是在用耳机的播放过程中用户拔出了耳机,声音会自动从设备自身的外放里面播出,但是其音量并不会自动调大。经过我们的测试,我们发现当播放时拔出耳机会有两个问题(也许对你来说不是问题,但是会影响我们的app):
音乐播放自动停止
声音音量大小不会自动变大,系统仍然以较小的声音(在耳机上合适的声音)来进行外放
对于第一个问题,实际上就是需要能够检测到耳机拔出的事件;而第二个问题则是需要当耳机拔出时强制设置系统输出设备修改为系统外放。
强制修改系统声音输出设备:
-
(void)resetOutputTarget{
BOOLhasHeadset=[selfhasHeadset];
NSLog(@”WillSetoutputtargetis_headset=%@.”,hasHeadset?@”YES”:@”NO”);
UInt32audioRouteOverride=hasHeadset?
kAudioSessionOverrideAudioRoute_None:kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,sizeof(audioRouteOverride),&audioRouteOverride);
}
可以看到我们修改了AudioSession的属性“kAudioSessionProperty_OverrideAudioRoute”,该属性在iOS文档上的解释如下:
kAudioSessionProperty_OverrideAudioRoute
Specifieswhether
ornottooverridetheaudiosessioncategory’snormalaudioroute.Canbesetwithoneoftwovalues:
,whichkAudioSessionOverrideAudioRoute_None
specifiesthatyouwanttousethenormalaudioroute;and
,whenkAudioSessionOverrideAudioRoute_Speaker
sendsoutputaudiotothespeaker.Awrite-only
UInt32value.
Upon
anaudioroutechange(suchasbyplugginginorunpluggingaheadset),oruponinterruption,thispropertyrevertstoitsdefaultvalue.Thispropertycanbeusedonlywiththe
(orthekAudioSessionCategory_PlayAndRecord
equivalent
)category.AVAudioSessionCategoryRecord
可以看到,该属性只有当category为
4.
设置Audio工作模式(category,我当做工作模式理解的)
iOS系统中Audio支持多种工作模式(category),要实现某个功能,必须首先将AudioSession设置到支持该功能的工作模式下。所有支持的工作模式如下:AudioSessionCategories
Category
identifiersforaudiosessions,usedasvaluesforthe
method.setCategory:error:
NSString*constAVAudioSessionCategoryAmbient;
NSString*constAVAudioSessionCategorySoloAmbient;
NSString*constAVAudioSessionCategoryPlayback;
NSString*constAVAudioSessionCategoryRecord;
NSString*constAVAudioSessionCategoryPlayAndRecord;
NSString*constAVAudioSessionCategoryAudioProcessing;
具体每一个category的功能请参考iOS文档,其中
设置category:
-
(BOOL)checkAndPrepareCategoryForRecording{
recording=YES;
BOOLhasMicphone=[selfhasMicphone];
NSLog(@”WillSetcategoryforrecording!hasMicophone=%@”,hasMicphone?@”YES”:@”NO”);
if(hasMicphone){
[[AVAudioSessionsharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecord
error:nil];
}
[selfresetOutputTarget];
returnhasMicphone;
}
-
(void)resetCategory{
if(!recording){
NSLog(@”WillSetcategorytostaticvalue=AVAudioSessionCategoryPlayback!”);
[[AVAudioSessionsharedInstance]setCategory:AVAudioSessionCategoryPlayback
error:nil];
}
}
5.
检测耳机插入/拔出事件
耳机插入拔出事件是通过监听AudioSession的RouteChange事件然后判断耳机状态实现的。实现步骤分为两步,首先注册监听函数,然后再监听函数中判断耳机状态。
注册监听函数:
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self);
我们的需求是当耳机被插入或拔出时做出响应,而产生AouteChange事件的原因有多种,所以需要对各种类型进行处理并结合当前耳机状态进行判断。在iOS文档中,产生AouteChange事件的原因有如下几种:
AudioSessionRouteChangeReasons
Identifiers
forthevariousreasonsthatanaudioroutecanchangewhileyouriOSapplicationisrunning.
enum{kAudioSessionRouteChangeReason_Unknown=0, kAudioSessionRouteChangeReason_NewDeviceAvailable=1, kAudioSessionRouteChangeReason_OldDeviceUnavailable=2, kAudioSessionRouteChangeReason_CategoryChange=3, kAudioSessionRouteChangeReason_Override=4,
//thisenumhasnoconstantwithavalueof5kAudioSessionRouteChangeReason_WakeFromSleep=6, kAudioSessionRouteChangeReason_NoSuitableRouteForCategory=7
};
具体每个类型的含义请查阅iOS文档,其中我们关注的是
当有新设备接入时,如果检测到耳机,则判定为耳机插入事件;当原有设备移除时,如果无法检测到耳机,则判定为耳机拔出事件;当出现“当前工作模式缺少合适设备时”,直接判定为录音时拔出了麦克风。
很明显,这个判定逻辑实际上不准确,比如原来就有耳机但是插入了一个新的audio设备或者是原来就没有耳机但是拔出了一个原有的audio设备,我们的判定都会出错。但是对于我们的项目来说,其实关注的不是耳机是拔出还是插入,真正关注的是有audio设备插入/拔出时能够根据当前耳机/麦克风状态去调整设置,所以这个判定实现对我们来说是正确的。
监听函数的实现:
void
audioRouteChangeListenerCallback(
void*inUserData,
AudioSessionPropertyIDinPropertyID,
UInt32inPropertyValueSize,
constvoid*inPropertyValue
){
if(inPropertyID!=kAudioSessionProperty_AudioRouteChange)return;
//Determinesthereasonfortheroutechange,toensurethatitisnot
//becauseofacategorychange.
CFDictionaryRefrouteChangeDictionary=inPropertyValue;
CFNumberRefrouteChangeReasonRef=
CFDictionaryGetValue(routeChangeDictionary,
CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
SInt32routeChangeReason;
CFNumberGetValue(routeChangeReasonRef,kCFNumberSInt32Type,&routeChangeReason);
NSLog(@”=======================RouteChangeReason:%d”,routeChangeReason);
AudioHelper*_self=(AudioHelper*)inUserData;
if(routeChangeReason==kAudioSessionRouteChangeReason_OldDeviceUnavailable){
[_selfresetSettings];
if(![_selfhasHeadset]){
[[NSNotificationCenterdefaultCenter]postNotificationName:@”ununpluggingHeadse
object:nil];
}
}elseif(routeChangeReason==kAudioSessionRouteChangeReason_NewDeviceAvailable){
[_selfresetSettings];
if(![_selfhasMicphone]){
[[NSNotificationCenterdefaultCenter]postNotificationName:@”pluggInMicrophone”
object:nil];
}
}elseif(routeChangeReason==kAudioSessionRouteChangeReason_NoSuitableRouteForCategory){
[_selfresetSettings];
[[NSNotificationCenterdefaultCenter]postNotificationName:@”lostMicroPhone”
object:nil];
}
//elseif(routeChangeReason==kAudioSessionRouteChangeReason_CategoryChange){
//[[AVAudioSessionsharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecorderror:nil];
//}
[_selfprintCurrentCategory];
}
当检测到相关事件后,通过NSNotificationCenter通知observers耳机(有无麦克风)拔出/插入事件拔出事件,从而触发相关操作。
6.
事件处理
对于耳机(有无麦克风)拔出/插入事件,一般需要做如下处理:
强制重设系统声音输出设备(防止系统以较小声音在外放中播放)
如果拔出前正在播放,则启动已经暂停的播放(当耳机拔出时,系统会自动暂停播放)
当拔出前正在录音,则需要检查麦克风情况并决定是否停止录音(如果录音时从iTouch/iPad等设备上拔出了带麦克风的耳机)
相关文章推荐
- IOS NavigationController Toolbar学习笔记
- ios7彩信配置
- Xcode5 运行iOS 模拟器"未能安装此应用程序"
- 三句话解决IOS7下透明导航栏情况TableView向上偏移问题
- iOS精品视频课程(677个课时)--教你如何成为一名IOS开发高手!
- iOS精品视频课程(677个课时)--教你如何成为一名IOS开发高手!
- IOS成长之路-JSON解析
- IOS“多继承”
- 使用Xcode和Instruments调试解决iOS内存泄露
- IOS中输入框被软键盘遮挡的解决办法
- Xcode4.6创建和使用iOS的dylib动态库
- 使用CocoaPods来做iOS程序的包依赖管理
- IOS RGB颜色转换
- iOS程序初始化
- ios Lable 自动换行
- ios NSString 去除空格和回车
- ios中tableviewcell允许长按出现剪切板
- IOS开发笔记_4自定义TabBar
- iOS导航控制器
- ios的frame,bound和center的原理和区别