一、音頻會(huì)話 AVAudioSession
音頻會(huì)話在應(yīng)用程序和操作系統(tǒng)之間扮演著中間人的角色,提供一種簡單實(shí)用的方法是OS得知應(yīng)用程序應(yīng)該如何與iOS音頻環(huán)境進(jìn)行交互。
AVAudioSession有AVFOundation框架引入。每個(gè)iOS應(yīng)用程序都有自己的一個(gè)音頻會(huì)話,這個(gè)會(huì)話可以被AVAudioSession的類方法sharedInstance訪問。
音頻會(huì)話是一個(gè)單例對象,可以使用它來設(shè)置應(yīng)用程序的音頻上下文環(huán)境,并向系統(tǒng)表達(dá)您的應(yīng)用程序音頻行為的意圖。
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
使用它可以實(shí)現(xiàn):
- 啟用或停用應(yīng)用程序中的音頻工作
- 設(shè)置音頻會(huì)話類別和模式,
- 配置音頻設(shè)置,如采樣率,I/O緩沖區(qū)持續(xù)時(shí)間和通道數(shù)
- 處理音頻輸出更改
- 相應(yīng)重要的音頻時(shí)間,如更改底層Media Services守護(hù)程序的可用性。
1、音頻會(huì)話分類/類別
Ambient 游戲、效率應(yīng)用程序
AVAudioSessionCategoryAmbient/kAudioSessionCategory_AmbientSound使用這個(gè)分類應(yīng)用會(huì)隨著靜音鍵和屏幕關(guān)閉而靜音,且不會(huì)終止其他應(yīng)用播放的聲音,可以和其他自帶應(yīng)用如iPod、Safari同時(shí)播放聲音。該類別無法在后臺(tái)播放聲音Solo Ambient(默認(rèn)) 游戲、效率應(yīng)用程序
AVAudioSessionCategorySoloAmbient/kAudioSessionCategory_SoloAmbientSound類似Ambient不同之處在于它會(huì)終止其它應(yīng)用播放聲音。該類別無法在后臺(tái)播放聲音Playback 音頻和視頻播放
AVAudioSessionCategoryPlayback/kAudioSessionCategory_MediaPlayback用于以音頻為主的應(yīng)用,不會(huì)隨著靜音鍵和平不關(guān)閉而靜音。可在后臺(tái)播放聲音。Record 錄音機(jī)、視頻捕捉
AVAudioSessionCategoryRecord/kAudioSessionCategory_RecordAudio錄音應(yīng)用,除了來電鈴聲、鬧鐘、日歷提醒之外的其他系統(tǒng)聲音不會(huì)被播放。只提供單純錄音功能。Play and Record VoIP、語音聊天
AVAudioSessionCategoryPlayAndRecord/kAudioSessionCategory_PlayAndRecord提供錄音和播放功能,如果應(yīng)用需要用到iPhone上的聽筒,這個(gè)類別是你唯一的選擇,在這個(gè)類別下,聲音的默認(rèn)出口為聽筒或者耳機(jī)。Audio Processing 離線會(huì)話和處理
AVAudioSessionCategoryAudioProcessing/kAudioSessionCategory_AudioProcessing在不播放或錄制音頻時(shí)使用音頻硬件編解碼器或信號(hào)處理器的類別。例如在執(zhí)行離線音頻格式轉(zhuǎn)換時(shí),此類別禁用播放和禁用錄音。應(yīng)用處于后臺(tái)時(shí),音頻處理通常不會(huì)繼續(xù),但是可以在應(yīng)用移至后臺(tái)時(shí),請求更多時(shí)間來完成處理。Multi-Route 使用外部硬件的高級A/V應(yīng)用程序
AVAudioSessionCategoryMultiRoute通過可以用的音頻輔助設(shè)備和內(nèi)置音頻硬件設(shè)備,我們可以自定義使用類型
并不是一個(gè)應(yīng)用只能使用一個(gè)category,可以根據(jù)實(shí)際需求來切換設(shè)置不同的category。
通過音頻會(huì)話單例對象的setCategory: error:設(shè)置iOS應(yīng)用音頻會(huì)話類別和模式。
NSError *error;
if (![_audioSession setCategory:AVAudioSessionCategoryPlayback error:&error]) { //設(shè)置類別
NSLog(@"Category error :%@",[error localizedDescription]);
}
2、配置音頻會(huì)話
音頻會(huì)話在應(yīng)用程序的生命周期中是可以修改的,一般在應(yīng)用程序啟動(dòng)時(shí),對其進(jìn)行配置。配置音頻會(huì)話的最佳位置就是應(yīng)用程序委托的application: didFinishLaunchingWithOptions:方法。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error;
if (![audioSession setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category error :%@",[error localizedDescription]);
}
if (![audioSession setActive:YES error:&error]) {
NSLog(@"Activation Error :%@",[error localizedDescription]);
}
return YES;
}
AVAudioSession 提供了與應(yīng)用程序音頻會(huì)話交互的接口,通過設(shè)置合適的的分類,可以音頻的播放指定需要的音頻會(huì)話,定制一些行為。最后告知該音頻會(huì)話激活該配置setActive:YES error:。
- 配置可以在后臺(tái)運(yùn)行:
info.plist文件天劍一個(gè)新的Required background modes類型的數(shù)組,在其中添加名為App plays audio or streams audio/video using AirPlay選項(xiàng)。或者右擊info.plist文件,在相應(yīng)的XML部分編輯plist,以及選擇Open as Source Code,添加下面標(biāo)簽到文件底部的</dict>前:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
兩種方式效果一樣,只是添加的方式不同。
三、使用AVAudioPlayer播放音頻
AVAudioPlayer提供了簡單地從文本或內(nèi)存中播放音頻的方法。
AVAudioPlayer構(gòu)建于Core Audio中的C-based Audio Queue Services的最頂層。它可以提供在Audio Queue Service中所能找到的核心功能。除非需要從網(wǎng)絡(luò)流中播放音頻、需要訪問原始音頻樣本或者需要非常低的時(shí)延,否則它都能勝任。
1、創(chuàng)建AVAudioPlayer
兩種方法創(chuàng)建AVAudioPlayer:initWithData: error:nil和initWithContentsOfURL: error:nil
使用包含要播放音頻的內(nèi)存的NSData,或者本地音頻文件的NSURL。
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"崔健-假行僧" withExtension:@"mp3"];
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:nil];
if (self.audioPlayer) {
[self.audioPlayer prepareToPlay];
}
如果返回一個(gè)有效的播放實(shí)例,建議調(diào)用prepareToPlay方法。這樣會(huì)取得需要音頻硬件并預(yù)加載Audio Queue的緩沖區(qū)。調(diào)用這個(gè)方法是可選的,在調(diào)用play方法時(shí)會(huì)隱性激活,不過在創(chuàng)建時(shí)準(zhǔn)備播放器可以降低調(diào)用paly方法和聽到聲音輸出之間的延時(shí)。
2、對播放進(jìn)行控制
常規(guī)方法:
-
play-- 立即播放音頻 -
pause-- 暫停播放 -
stop-- 停止播放
pause和stop都能停止播放,并且再次播放的時(shí)候繼續(xù)播放。區(qū)別是stop方法會(huì)撤銷調(diào)用prepareToPlay是所做的設(shè)置,而調(diào)用pause不會(huì)。
其他方法:
- 修改播放器音量:播放器的音量獨(dú)立于系統(tǒng)的音量,可以通過對播放器音量的處理實(shí)現(xiàn)一些效果,比如聲音漸隱效果。音量或播放增益定義為0.0(靜音)到1.0之間的浮點(diǎn)值。
- 修改播放器pan值:允許使用立體聲播放聲音,pan值范圍
-1.0(極左)-1.0(極右),默認(rèn)是為1.0(居中) - 調(diào)整播放率:允許用戶在不改變音調(diào)的情況下調(diào)整播放率,范圍從
0.5(半速)-2.0(2倍速)。 - 通過設(shè)置
numberOfLoops實(shí)現(xiàn)音頻無縫循環(huán):給這個(gè)屬性設(shè)置一個(gè)大于0的數(shù),可以實(shí)現(xiàn)播放器n次循環(huán)播放。相反如果為-1導(dǎo)致播放器無限循環(huán)。音頻循環(huán)可以是未壓縮的線性PCM音頻,也可以是AAC之類的壓縮格式音頻。MP3格式片段可以實(shí)現(xiàn)無縫循環(huán),但是MP3格式用作循環(huán)格式不被推崇。MP3格式的音頻要實(shí)現(xiàn)循環(huán)的目的通常需要使用特殊工具進(jìn)行處理。如果希望使用壓縮格式的資源,建議使用AAC或者AppleLossless格式的內(nèi)容。 - 進(jìn)行音頻計(jì)量:播放發(fā)生時(shí)從播放器讀取播放力度的平均值和峰值。將這些數(shù)據(jù)提供給VU計(jì)量器或其他可視化元件。向用戶提供可視化的反饋效果。
三、創(chuàng)建Audio Looper
四、處理中斷事件
音頻會(huì)話通知
添加通知監(jiān)聽,監(jiān)聽是否發(fā)生中斷事件。通知名稱為AVAudioSessionInterruptionNotification。
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
推送的消息會(huì)包含許多重要信息的userInfo字典,通過關(guān)鍵字AVAudioSessionInterruptionTypeKey獲取中斷類型AVAudioSessionInterruptionType,根據(jù)中斷狀態(tài)執(zhí)行不同操作
- (void)handleInterruption:(NSNotification *)notification {
NSDictionary *infoDict = notification.userInfo;
AVAudioSessionInterruptionType type = [infoDict[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
[self stop];//開始中斷,停止播放
} else {
AVAudioSessionInterruptionOptions options = [infoDict[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
[self play];
}
}
}
如果中斷類型為AVAudioSessionInterruptionTypeEnded,userInfo字典里會(huì)包含一個(gè)通過keyAVAudioSessionInterruptionOptionKey取的AVAudioSessionInterruptionOptions類型值,表示音頻會(huì)話是否已經(jīng)重新激活以及是否可以再次播放。上例中,如果options為AVAudioSessionInterruptionOptionShouldResume,可以調(diào)用播放的方法繼續(xù)播放音頻。
五、對線路改變的響應(yīng)
在iOS設(shè)備上添加或移除音頻輸入、輸出線路時(shí),會(huì)發(fā)生線路改變,有多重原因會(huì)導(dǎo)致線路的變化,比如插入耳機(jī)或斷開USB麥克風(fēng)。當(dāng)這些時(shí)間發(fā)生時(shí),音頻會(huì)根據(jù)情況改變輸入或輸出線路,同時(shí)AVAudioSession會(huì)廣播一個(gè)描述該變化的通知給所有相關(guān)的監(jiān)聽者。
添加監(jiān)聽的通知名稱:AVAudioSessionRouteChangeNotification。該通知同樣包含一個(gè)userInfo字典,帶有相應(yīng)通知發(fā)送的原因一前一個(gè)線路的描述,以此可以確定線路變化的情況。
判斷線路變更發(fā)生的原因,取keyAVAudioSessionRouteChangeReasonKey對應(yīng)的AVAudioSessionRouteChangeReason類型值。根據(jù)變更原因,作相應(yīng)處理。
typedef NS_ENUM(NSUInteger, AVAudioSessionRouteChangeReason)
{
AVAudioSessionRouteChangeReasonUnknown = 0,
原因不明;
AVAudioSessionRouteChangeReasonNewDeviceAvailable = 1,
有新設(shè)備可用,如耳機(jī)插入
AVAudioSessionRouteChangeReasonOldDeviceUnavailable = 2,
一個(gè)舊設(shè)備不可用,如耳機(jī)拔出
AVAudioSessionRouteChangeReasonCategoryChange = 3,
音頻類別被改變,如Audio從Play back 變成Play And Record
AVAudioSessionRouteChangeReasonOverride = 4,
音頻線路(route)改變,如類別是Play and Record,輸出社誒已經(jīng)從默認(rèn)的接收器改變成為揚(yáng)聲器
AVAudioSessionRouteChangeReasonWakeFromSleep = 6,
設(shè)備從休眠中醒來
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory = 7,
沒有路徑返回當(dāng)前的類別,如Record雷彪當(dāng)前沒有輸入設(shè)備
AVAudioSessionRouteChangeReasonRouteConfigurationChange NS_ENUM_AVAILABLE_IOS(7_0) = 8
當(dāng)前輸入/輸出口沒變,但設(shè)置修改,如一個(gè)端口的數(shù)據(jù)選擇已經(jīng)改變。
}
知道有設(shè)備斷開連接后,需要向userInfo字典提出請求,一會(huì)的其中用于描述前一個(gè)線路的AVAudioSessionRouteDescription,其對應(yīng)的key為AVAudioSessionRouteChangePreviousRouteKey。線路的描述信息整合在一個(gè)輸入NSArray和一個(gè)輸出NSArray中。數(shù)組中的元素都是AVAudioSessionPortDescription的實(shí)例。用于描述不同的I/O接口屬性??梢詮木€路描述中找到第一個(gè)輸出接口,即前一次的接口。
輸入口不同類型,input port type
AVAudioSessionPortLineIn
AVAudioSessionPortBuiltInMic :內(nèi)置麥克風(fēng)
AVAudioSessionPortHeadsetMic :耳機(jī)線中的麥克風(fēng)
輸出口不同類型,output port type
AVAudioSessionPortLineOut
AVAudioSessionPortHeadphones :耳機(jī)或者耳機(jī)式輸出設(shè)備
AVAudioSessionPortBuiltInReceiver :帖耳朵時(shí)候內(nèi)置揚(yáng)聲器(打電話的時(shí)候的聽筒)
AVAudioSessionPortBuiltInSpeaker :iOS設(shè)備的揚(yáng)聲器
AVAudioSessionPortBluetoothA2DP :A2DP協(xié)議式的藍(lán)牙設(shè)備
AVAudioSessionPortHDMI :高保真多媒體接口設(shè)備
AVAudioSessionPortAirPlay :遠(yuǎn)程AirPlay設(shè)備
AVAudioSessionPortBluetoothLE :藍(lán)牙低電量輸出設(shè)備
一個(gè)簡單實(shí)例,拔出耳機(jī)之后,默認(rèn)停止播放:
- (void)handleRouteChange:(NSNotification *)notification {
NSDictionary *infoDict = notification.userInfo;
AVAudioSessionRouteChangeReason reason = [infoDict[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
// AVAudioSessionRouteDescription
// AVAudioSessionPortDescription
AVAudioSessionRouteDescription *previousRoute = infoDict[AVAudioSessionRouteChangePreviousRouteKey];
//取出所有線路描述
NSLog(@"count :%zd",previousRoute.outputs.count);
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
//取出前一次線路描述
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
[self stop];
}
}
}
六、使用AVAudioRecorder錄制音頻
AVAudioRecorder構(gòu)建于Audio Queue Services之上,可以再iOS設(shè)備上使用這個(gè)類從內(nèi)置的麥克風(fēng)錄制音頻,也可以從外部音頻設(shè)備進(jìn)行錄制,比如數(shù)字音頻接口或USB麥克風(fēng)等。
1、創(chuàng)建AVAudioRecorder
- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;
第一個(gè)參數(shù):音頻流寫入文件的本地文件URL,第二個(gè)參數(shù):包含用于配置錄音會(huì)話信息,第三個(gè)參數(shù):捕捉初始化階段錯(cuò)誤。
成功創(chuàng)建AVAudioRecorder實(shí)例,建議調(diào)用prepareToRecord。與AVAudioPlayer的prepareToPlay方法類似,執(zhí)行底層Audio Queue初始化的必要過程。在URL參數(shù)指定位置創(chuàng)建一個(gè)文件,將錄制啟動(dòng)時(shí)的延時(shí)降到最小。
NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"1.m4a"];
NSURL *fileUrl = [NSURL fileURLWithPath:path];
NSDictionary *settings = @{
AVFormatIDKey:@(kAudioFormatMPEG4AAC),
AVSampleRateKey:@22050.0f,
AVNumberOfChannelsKey:@1,
};
NSError *error;
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:fileUrl settings:settings error:&error];
if (_audioRecorder) {
[_audioRecorder prepareToRecord];
} else {
NSLog(@"init error :%@",[error localizedDescription]);
}
配置錄音會(huì)話參數(shù):
- AVFormatIDKey --寫入內(nèi)容的音頻格式,常用的音頻格式支持的值:
kAudioFormatLinearPCM
kAudioFormatMPEG4AAC
kAudioFormatAppleLossless
kAudioFormatAppleIMA4
kAudioFormatiLBC
kAudioFormatULaw
kAudioFormatLinearPCM -會(huì)將未壓縮的音頻流寫入文件中,
這種格式的保真度最高,相應(yīng)的文件也最大。
AAC或Apple IMA4的壓縮格式會(huì)顯著縮小文件,還能保證高質(zhì)量的音頻內(nèi)容。
- AVSampleRateKey --定義錄音器采樣率。采樣率定義了對輸入的模擬音頻信號(hào)每一秒內(nèi)的采樣數(shù)。采樣率決定音頻的質(zhì)量及最終文件大小。一般標(biāo)準(zhǔn)的采樣率:8k、16k、22.5k、44.1k。
- AVNumbeOfChannelsKey -- 定義記錄音頻通道數(shù)。默認(rèn)值1,單聲道錄制。設(shè)置2-立體聲錄制。除非使用外部硬件進(jìn)行錄制,一般應(yīng)該創(chuàng)建單聲道錄音。
2、控制錄音過程
record --開始或繼續(xù)錄音
stop --停止錄音,并關(guān)閉文件
pause --暫停錄音
七、使用Audio Metering
AVAudioPlayer和AVAudioRecorder中最強(qiáng)大和最實(shí)用的功能是對音頻進(jìn)行測量,Audio Metering可讓開發(fā)者讀取音頻的平均分貝和峰值分貝數(shù)據(jù),并使這些數(shù)據(jù)以可視化方式將聲音大小呈獻(xiàn)給用戶。
通過averagePowerForChannel:和peakPowerForChannel:獲取平均分貝和峰值分貝,返回一個(gè)用于表示聲音分貝(dB)等級的浮點(diǎn)值,這個(gè)值的范圍是從表示最大分貝的0dB(full scale)到最小分貝或靜音的-160dB。獲取這兩個(gè)值之前,要先設(shè)置屬性meteringEnabled為YES,才能對音頻進(jìn)行測量。另,每當(dāng)需要讀取值時(shí),需要先調(diào)用updateMeters方法才能獲取最新的值。