最近的項(xiàng)目的內(nèi)容開(kāi)始涉及到一定的IM語(yǔ)音對(duì)講的內(nèi)容,而筆者從未接觸過(guò)此類(lèi)開(kāi)發(fā),也只是在摸索中一點(diǎn)點(diǎn)探索學(xué)習(xí),幾日下來(lái)略有了一點(diǎn)收獲,以博客的形式跟諸位看官分享
先說(shuō)明一下什么是IM語(yǔ)音聊天,IM全稱(chēng)Instant Messenger,即時(shí)通訊,簡(jiǎn)單的來(lái)說(shuō)就是MSN ,QQ一類(lèi)的聊天軟件。而IM語(yǔ)音聊天即是使用語(yǔ)音音頻來(lái)代替?zhèn)鹘y(tǒng)文字交流的方式進(jìn)行溝通交流,目前市面上的語(yǔ)音IM根據(jù)聊天的方式又幾種不同的方式,一種是即時(shí)發(fā)言猶如電話通信一樣的語(yǔ)音通信,其中比較具有代表的軟件有UCTalk,YY語(yǔ)音等,此類(lèi)語(yǔ)音軟件以PC平臺(tái)為主,另外一種則是先進(jìn)行錄音之后發(fā)送形式與傳統(tǒng)文字IM的形式略有相似之處的聊天方式,比較具有代表性的軟件有微信,陌陌等,此類(lèi)的語(yǔ)音軟件大多出現(xiàn)在移動(dòng)端,PC平臺(tái)上同時(shí)也具有少量的使用。還有一種則可以視為介于兩者中間的,模仿傳統(tǒng)對(duì)講機(jī)形式進(jìn)行輪流發(fā)言的聊天方式,這種方式只在PC臺(tái)上少量使用出現(xiàn)過(guò),較前兩者使用的范圍比較小。
本次我們做的是移動(dòng)端的項(xiàng)目,于是也難免于俗套使用先錄制后發(fā)送的方式實(shí)現(xiàn)語(yǔ)音聊天,鄙人才疏學(xué)淺了解不深,所以可能是主觀上的臆斷,感覺(jué)以錄制發(fā)送的形式實(shí)現(xiàn)的語(yǔ)音IM實(shí)現(xiàn)起來(lái)從技術(shù)將要簡(jiǎn)單很多,畢竟不會(huì)設(shè)計(jì)流媒體的問(wèn)題。
既然是語(yǔ)音聊天難免涉及到一些音頻相關(guān)的問(wèn)題,筆者是負(fù)責(zé)IOS端開(kāi)發(fā)的,所以大部分的內(nèi)容以IOS角度為主,IOS提供的AVFoundation框架可以實(shí)現(xiàn)大部分系統(tǒng)聲音服務(wù)不支持的超過(guò)30秒的音頻播放功能,同時(shí)還提供了錄音功能。而我們主要使用到的是AVAudioRecorder與AVAudioPlayer兩個(gè)類(lèi),通過(guò)名字我們就可以判斷出,前者是提供音頻錄制服務(wù)而后者則是提供播放服務(wù)。AVAudioRecorder以各種不同的格式將聲音錄制到內(nèi)存或設(shè)備本地文件中。錄音過(guò)程可再應(yīng)用程序執(zhí)行其他功能時(shí)持續(xù)進(jìn)行。而AVAudioPlayer能夠播放任意長(zhǎng)度的音頻。使用這個(gè)類(lèi)可以實(shí)現(xiàn)游戲配樂(lè)和其他復(fù)雜的音頻應(yīng)用程序??梢匀刂撇シ胚^(guò)程,包括同時(shí)播放多個(gè)音頻文件等。無(wú)疑IOS提供的音頻服務(wù)是強(qiáng)大以及便利的。再使用AVFoundation框架之前必須要將AVFoundation.framework與CoreAudio.framework加入到項(xiàng)目中,再導(dǎo)入兩個(gè)接口文件。
import
import
具體的使用實(shí)例代碼如下,首先是音頻錄制的使用方法:
現(xiàn)在我我們來(lái)詳細(xì)解讀一下者兩段代碼的含義,首先是音頻錄制的代碼,我們先后聲明并且定義了一下幾樣?xùn)|西,創(chuàng)建音頻的參數(shù)鍵值對(duì)MyRecordParam,一個(gè)路徑數(shù)組pathArray,一個(gè)Docment路徑字符串DocmentPath以及我們這一步的主角AVAudioRecorder對(duì)象MyRecorder。
我們先來(lái)解釋一下路徑的獲取,至于音頻參數(shù),重頭戲需要放在后面不是么~
NSSearchPathForDirectoriesInDomains是IOS中一個(gè)搜索路徑的方法,它三個(gè)參數(shù)前兩個(gè)為枚舉,而最后一個(gè)參數(shù)為BOOL類(lèi)型,第一個(gè)參數(shù)的枚舉列表如下:
enum {
NSApplicationDirectory = 1,//Supported applications (/Applications)
NSDemoApplicationDirectory,//Unsupported applications and demonstration versions
NSDeveloperApplicationDirectory,//Developer applications (/Developer/Applications)
NSAdminApplicationDirectory,//System and network administration applications
NSLibraryDirectory,//Various user-visible documentation, support, and configuration files (/Library)
NSDeveloperDirectory,//Developer resources (/Developer)
NSUserDirectory,//User home directories (/Users)
NSDocumentationDirectory,//
NSDocumentDirectory,//
NSCoreServiceDirectory,//Location of core services (System/Library/CoreServices)
NSAutosavedInformationDirectory = 11,//Location of user’s autosaved documents Library/Autosave Information
NSDesktopDirectory = 12,//
NSCachesDirectory = 13,//Location of discardable cache files (Library/Caches)
NSApplicationSupportDirectory = 14,//Location of application support files (Library/Application Support)
NSDownloadsDirectory = 15,//
NSInputMethodsDirectory = 16,//
NSMoviesDirectory = 17,//
NSMusicDirectory = 18,//
NSPicturesDirectory = 19,//
NSPrinterDescriptionDirectory = 20,//
NSSharedPublicDirectory = 21,//
NSPreferencePanesDirectory = 22,//
NSItemReplacementDirectory = 99,//
NSAllApplicationsDirectory = 100,//
NSAllLibrariesDirectory = 101//
};
其每一項(xiàng)代表一種希望獲取到的目錄類(lèi)型,這其中不只是IOS中的目錄類(lèi)型,也有MAC下的路徑類(lèi)型,沒(méi)錯(cuò),就跟你想的一樣,這個(gè)函數(shù)并非IOS下專(zhuān)用。
第二個(gè)參數(shù)的枚舉列表如下
enum {
NSUserDomainMask = 1,//用戶(hù)主目錄中
NSLocalDomainMask = 2,//當(dāng)前機(jī)器中
NSNetworkDomainMask = 4,//網(wǎng)絡(luò)中可見(jiàn)的主機(jī)
NSSystemDomainMask = 8,//系統(tǒng)目錄,不可修改(/System)
NSAllDomainsMask = 0x0ffff,//全部
};
第二個(gè)參數(shù)代表要搜索路徑的位置,本機(jī)?亦或是當(dāng)前程序,還是局域網(wǎng)連接到的其他電腦。
第三個(gè)參數(shù)是一個(gè)BOOL值他代表是否將返回完整路徑
而返回的路徑中并不包含文件名,我們一定要記住再路徑結(jié)尾處加上我們想要的文件名,別忘了我們是要?jiǎng)?chuàng)建一個(gè)音頻文件。
當(dāng)然此函數(shù)搜索的結(jié)果可能又很多條路徑,因?yàn)楦鶕?jù)你的參數(shù)不同他返回的路徑甚至可能包含其他電腦上的(具體本人未測(cè),有心人可進(jìn)一步測(cè)試,也希望其講結(jié)果與大家分享)所以他的結(jié)果是一個(gè)數(shù)組,而我們要取得的路徑目的極為明確,就是程序的Docment路徑,并且可以更加肯定是我們的程序只有一個(gè)Docment路徑,所以我們直接取得了第一條返回記錄。
不得不說(shuō)的是AVAudioRecorder的設(shè)計(jì)者是個(gè)好人,沒(méi)錯(cuò),他沒(méi)有將構(gòu)造函數(shù)的參數(shù)設(shè)置成一大堆參數(shù),那讓人看起來(lái)頭疼,但其實(shí)他用了一個(gè)更加讓人頭疼的方法,沒(méi)錯(cuò)他讓你去手動(dòng)設(shè)置一個(gè)參數(shù)鍵值對(duì),你甚至不知道建值對(duì)中該填什么參數(shù),哪些參數(shù)…這對(duì)于習(xí)慣看參數(shù)列表直接調(diào)用方法的人無(wú)疑是個(gè)噩夢(mèng)(尤其是當(dāng)他們英文文檔閱讀能力低下時(shí)- -),目前我所掌握的參數(shù)鍵的相關(guān)資料如下:
AVSampleRateKey, //采樣率
AVFormatIDKey,//音頻編碼格式
AVLinearPCMBitDepthKey,//采樣位數(shù) 默認(rèn) 16
AVNumberOfChannelsKey,//通道的數(shù)目
AVLinearPCMIsBigEndianKey,//大端還是小端 是內(nèi)存的組織方式
AVLinearPCMIsFloatKey,//采樣信號(hào)是整數(shù)還是浮點(diǎn)數(shù)
AVEncoderAudioQualityKey,//音頻編碼質(zhì)量
鑒于考慮到可能各位看官對(duì)于我們所要給出的參數(shù)并不了解,所以在此我們來(lái)依次解釋一下每一個(gè)參數(shù)的含義
首先是采樣率,簡(jiǎn)單地說(shuō)就是通過(guò)波形采樣的方法記錄1秒鐘長(zhǎng)度的聲音,需要多少個(gè)數(shù)據(jù)。44KHz采樣率的聲音就是要花費(fèi)44000個(gè)數(shù)據(jù)來(lái)描述1秒鐘的聲音波形。原則上采樣率越高,聲音的質(zhì)量越好。
編碼格式可以理解為每種音頻格式不同的編解碼方式,鄙人對(duì)于此了解的也不是非常多(能熟知所有編解碼的人一定是偶像級(jí)的超人!)而IOS下這些編碼方式被集中到一個(gè)枚舉中,而我們本次代碼中所使用的編碼格式是WAV文件的格式,想要使用其他的編碼格式就在成功導(dǎo)入AVFouncation框架之后即可通過(guò)Xcode的自動(dòng)提示找到以kAudioFormat開(kāi)頭的各種枚舉的名稱(chēng)。
采樣位數(shù)即采樣值或取樣值,是用來(lái)衡量聲音波動(dòng)變化的參數(shù),是指聲卡在采集和播放聲音文件時(shí)所使用數(shù)字聲音信號(hào)的二進(jìn)制位數(shù)。聲卡的位客觀地反映了數(shù)字聲音信號(hào)對(duì)輸入聲音信號(hào)描述的準(zhǔn)確程度。
通道數(shù)目應(yīng)該很好理解了,1意味著單聲道聲音,2指立體聲,4是指四個(gè)聲道等等。
接下來(lái)的AVLinearPCMIsBigEndianKey是指再內(nèi)存中音頻的存儲(chǔ)模式,在計(jì)算機(jī)中,通常采用的字節(jié)存儲(chǔ)機(jī)制主要有兩種:big-endian和little-endian,即大端模式和小端模式。這個(gè)參數(shù)為BOOL值,YES為大端,NO為小端。關(guān)于大端和小端相關(guān)到兩個(gè)關(guān)鍵詞,MSB以及LSB。MSB:Most Significant Bit( 最高有效位),LSB:Least Significant Bit (最低有效位)你可以理解為一段數(shù)據(jù)再內(nèi)存中的起始位置以及終止位置,大端模式就是MSB存放在最低端的地址上。而小端口模式就是LSB存放在最低端的地址上。
在Big-Endian中,對(duì)于bit序列中的序號(hào)編排方式如下(以雙字節(jié)數(shù)0x8B8A為例):bit | casino
0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15——MSB———————————-LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |——————————————–
在Little-Endian中,對(duì)于bit序列中的序號(hào)編排和Big-Endian剛好相反,其方式如下(以雙字節(jié)數(shù)0x8B8A為例):
bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0——MSB———————————–LSBval | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |———————————————
總之可以理解為在內(nèi)存中正反兩種存儲(chǔ)順序。
對(duì)于采樣信號(hào)是整數(shù)還是浮點(diǎn)數(shù)也是一個(gè)BOOL類(lèi)型的參數(shù),本人并未理解會(huì)影響到的含義,或許是設(shè)計(jì)到音頻信號(hào)再解析時(shí)候的精準(zhǔn)度但并不敢確定還有待設(shè)定,所以也沒(méi)有設(shè)置這個(gè)參數(shù)。
最后一個(gè)參數(shù)音頻編碼質(zhì)量比較好理解了,這個(gè)參數(shù)又是一個(gè)枚舉,我們可以找到以AVAudioQuality開(kāi)頭的High、Low、Medium、Max、Min五種設(shè)置。介于參數(shù)鍵值對(duì)的解釋暫時(shí)就是這些了,如果又看官得到了一些其他參數(shù)的資料或者對(duì)于我的解釋有糾正補(bǔ)充的歡迎指教。
AVAudioRecorder構(gòu)造函數(shù)中的最后一個(gè)參數(shù)為一個(gè)出參,用以保存音頻錄制的錯(cuò)誤信息,如果不想保存錯(cuò)誤的信息直接設(shè)置為空即可
接下來(lái)我們的AVAudioRecorder對(duì)象好像看起來(lái)成功創(chuàng)建完成了,慢著,不對(duì),為什么會(huì)提示錯(cuò)誤?仔細(xì)看一下,原來(lái)是路徑的問(wèn)題,別擔(dān)心,其實(shí)只是AVAudioRecorder跟你開(kāi)了個(gè)小玩笑,因?yàn)樗麑?shí)在太懶了,都懶得把字符串路徑轉(zhuǎn)換成URL了,所以我們得手動(dòng)轉(zhuǎn)換一下~沒(méi)錯(cuò)使用NSURL的靜態(tài)方法 urlWithString即可解決~接下來(lái)只要調(diào)用record方法即可開(kāi)始錄音。而以上代碼只是實(shí)例,建議在編寫(xiě)程序的時(shí)候,我們的AVAudioRecorder對(duì)象要聲名在類(lèi)內(nèi)屬性中。否則需要結(jié)束錄音時(shí)無(wú)法調(diào)用到AVAudioRecorder的stop方法。
AVAudioRecorder對(duì)象創(chuàng)建起來(lái)或許會(huì)比較麻煩,但是使用起來(lái)確很方便,只要再錄音開(kāi)始時(shí)調(diào)用record,暫停的時(shí)候調(diào)用pause方法,而結(jié)束的時(shí)候調(diào)用stop方法就可以了。
相對(duì)于錄音,播放音頻可以說(shuō)簡(jiǎn)單的可以,我們?cè)趧?chuàng)建AVAudioPlayer對(duì)象的時(shí)候只需要將之前錄音的文件路徑給它并且給一個(gè)空的錯(cuò)誤出參便可輕松的創(chuàng)建出一個(gè)AVAudioPlayer對(duì)象,使用起來(lái)也是那么的方便只要調(diào)用一下play函數(shù)即可~~他和AVAudioRecorder一樣也可以提供暫停和停止的功能,那它是否可以支持進(jìn)度調(diào)節(jié)呢?想知道的話不如用自動(dòng)提示打開(kāi)他的方法列表看看呢一切看起來(lái)好像都很簡(jiǎn)單,我們就要輕松愉快的大功告成了,可當(dāng)你運(yùn)行程序之后卻驚奇的發(fā)現(xiàn)為什么播放不出聲音!使用iTools一類(lèi)的軟件檢查一下程序的Docment目錄,沒(méi)錯(cuò)啊!文件在啊,莫非我錄制錯(cuò)了?等等等等上萬(wàn)種可能就這樣出現(xiàn)在你的腦中!好吧我真不忍心看你像我一樣沒(méi)頭沒(méi)腦的研究幾個(gè)小時(shí)只是因?yàn)槟銢](méi)有把實(shí)例代碼中AVAudioPlayer的聲明放到類(lèi)屬性中。。。沒(méi)錯(cuò),如果你再某個(gè)方法中聲明了它并且調(diào)用播放函數(shù)你就會(huì)發(fā)現(xiàn)怎么樣也播放不出聲音,如果你在調(diào)用play方法的位置設(shè)置下斷點(diǎn)再仔細(xì)聽(tīng)的話可能會(huì)聽(tīng)到一小段聲音,為什么呢?因?yàn)槟銊傉{(diào)用了play的方法你的AVAudioPlayer對(duì)象就被釋放了,當(dāng)然什么也沒(méi)有了也就播放不出聲音了使用AVAudioPlayer就把它的對(duì)象放入類(lèi)內(nèi)屬性吧!
wav沒(méi)錯(cuò)我們的成功的錄制并播放了一個(gè)wav,就在你開(kāi)心的時(shí)候你會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,納尼???安卓不支持wav!!!這可怎么辦?難道要讓本來(lái)就少的可憐的用戶(hù)還要永隔安卓與IOS的大洋兩岸么!一切問(wèn)題都會(huì)有解決方法的!但是,預(yù)知后事如何,請(qǐng)聽(tīng)下回分解