Android音頻PCM數(shù)據(jù)的采集和播放检痰,讀寫wav文件

2020年真的是非同一般的一年,全球疫情蔓延锨推。早上坐出租時和司機師傅聊天铅歼,深深的感受到當前經(jīng)濟的艱難,受疫情影響换可,司機師傅最近接單特別少椎椰,語氣中透露著無奈與擔憂,也給自己提了個醒锦担,一定要好好攢錢俭识,以備不時之需慨削,言歸正傳洞渔,伴隨5G的來臨,移動端音視頻肯定會火缚态,形勢所迫磁椒,必須學習點硬貨,未雨綢繆玫芦,因此在這里記錄一下音視頻開發(fā)的學習歷程~~~? ? ? ? ?

音頻開發(fā)經(jīng)常遇到的專業(yè)性詞語

(1)采樣率

"音頻采樣率“是指錄音設(shè)備在一秒鐘內(nèi)對聲音信號的采樣次數(shù)浆熔,采樣頻率越高聲音的還原就越真實越自然,常用的音頻采樣頻率:8kHz,11.025kHz,22.05kHz,16kHz,37.8kHz,44.1kHz,48kHz,96kHz,192kHz等桥帆,在當今的主流采集卡上医增,采樣頻率一般共分為22.05kHz,44.1kHz,48kHz三個等級慎皱,22.05kHz只能達到FM廣播的聲音品質(zhì),44.1kHz則是理論上的CD音質(zhì)界限叶骨,48kHz則更加精確一些茫多。

通俗理解:每秒錄取聲音的次數(shù)

(2)量化精度(采樣位數(shù))

”采樣位數(shù)“越大表示的值的范圍也就越大

”采樣位數(shù)“可以理解為采集卡處理聲音的解析度,這個數(shù)值越大忽刽,解析度越高天揖,錄制和回放的聲音就越真實,電腦中的聲音文件是用數(shù)字0和1來表示的跪帝。連續(xù)的模擬信號按一定的采樣頻率經(jīng)數(shù)碼脈取樣后今膊,每一個離散的脈沖信號被以一定的量化精度量化成一串二進制編碼流,這串編碼的位數(shù)即為采樣位數(shù)伞剑,也稱為”量化精度“斑唬。

常見的位數(shù)為:16bit和32bit

通俗理解:每秒錄取聲音的精度,就像畫面的分辨率黎泣,越高聲音越真實

(3)聲道數(shù)

聲道數(shù)分別有:單聲道的聲道數(shù)為1個聲道數(shù)赖钞;雙聲道的聲道數(shù)為2個聲道;立體聲道的聲道數(shù)默認為2個聲道聘裁;立體聲道(4聲道)的聲道數(shù)為4個聲道雪营。

常見使用的是:單聲道(MONO)和雙聲道(STEREO)

(4)PCM編碼與WAV格式

PCM(Pulse Code Modulation---脈碼調(diào)制錄音)。所謂PCM錄音就是將聲音等模擬信號編程符號化的脈沖列衡便,再予以記錄献起,PCM信號是由【1】,【0】等符號構(gòu)成的數(shù)字信號镣陕,而未經(jīng)過任何編碼和壓縮處理谴餐,與模擬信號比,它不易傳送系統(tǒng)的雜波及失真的影響呆抑,動態(tài)范圍寬岂嗓,可得到音質(zhì)相當好的影響效果,也就是說鹊碍,PCM就是沒有壓縮編碼方式厌殉,PCM文件就是采用PCM這種沒有壓縮的編碼方式編碼的音頻數(shù)據(jù)文件。

PCM約定俗成了無損編碼侈咕,因為PCM代表了數(shù)字音頻中最佳的保真水準公罕,并不意味著PCM就能夠確保信號絕對保真,PCM也只能做到最大程度的無限接近耀销。

WAV為微軟公司(Microsoft)開發(fā)的一種聲音文件格式楼眷,它復(fù)合RIFF(Resource InterChange File Format)文件規(guī)范,用于保存windows平臺的音頻信息資源,被windows平臺及其應(yīng)用程序所廣泛支持罐柳,該格式也支持MSADPCM,CCITT A LAW 等多種壓縮運算法掌腰,支持多種音頻數(shù)字,取樣頻率和聲道张吉,標準格式化的WAV文件和CD格式一樣辅斟,也是44.1K取樣頻率,16位量化數(shù)字芦拿,因此在聲音文件質(zhì)量和CD相差無幾士飒!

在windows平臺下,基于PCM編碼的WAV是被支持的最好的音頻格式蔗崎,所有音頻軟件都完美支持酵幕,由于本身可以達到較高的音質(zhì)的要求,因此缓苛,WAV也是音樂編輯創(chuàng)作的首選格式芳撒,適合保存音樂素材,因此未桥,基于PCM編碼的WAV被作為了一種中介的格式笔刹,常常使用在其它編碼的相互轉(zhuǎn)換中,例如MP3轉(zhuǎn)換WMA冬耿。

通俗理解:PCM是一種沒有壓縮且無損的編碼方式舌菜,WAV是微軟開發(fā)的一種無損的音頻文件格式,而WAV是通過PCM數(shù)據(jù)的基礎(chǔ)上添加頭部信息而生成的一種音頻格式亦镶,當然也可以基于其它如ADPCM編碼添加頭部信息生成WAV日月。

WAV文件格式

在文件的前44字節(jié)放置標頭(head),使播放器或編輯器能夠簡單掌握文件的基本信息缤骨,其內(nèi)容以區(qū)塊(chunk)為最小單位爱咬,每一區(qū)塊長度為4字節(jié)。

aa
bb

代碼實現(xiàn):


// 音頻數(shù)據(jù)的大小longtotalAudioLen=fileInputStream.getChannel().size();// wav總區(qū)塊大小longtotalDataLen=totalAudioLen+36;// 聲道數(shù)量intchannels;// 采樣率longlongSampleRate;// 位元率longbyteRate=16*longSampleRate*channels/8;byte[]header=newbyte[44];// RIFF/WAVE headerheader[0]='R';header[1]='I';header[2]='F';header[3]='F';header[4]=(byte)(totalDataLen&0xff);header[5]=(byte)((totalDataLen>>8)&0xff);header[6]=(byte)((totalDataLen>>16)&0xff);header[7]=(byte)((totalDataLen>>24)&0xff);//WAVEheader[8]='W';header[9]='A';header[10]='V';header[11]='E';// 'fmt ' chunkheader[12]='f';header[13]='m';header[14]='t';header[15]=' ';// 4 bytes: size of 'fmt ' chunkheader[16]=16;header[17]=0;header[18]=0;header[19]=0;// format = 1header[20]=1;header[21]=0;header[22]=(byte)channels;header[23]=0;header[24]=(byte)(longSampleRate&0xff);header[25]=(byte)((longSampleRate>>8)&0xff);header[26]=(byte)((longSampleRate>>16)&0xff);header[27]=(byte)((longSampleRate>>24)&0xff);header[28]=(byte)(byteRate&0xff);header[29]=(byte)((byteRate>>8)&0xff);header[30]=(byte)((byteRate>>16)&0xff);header[31]=(byte)((byteRate>>24)&0xff);// block alignheader[32]=(byte)(2*16/8);header[33]=0;// bits per sampleheader[34]=16;header[35]=0;//dataheader[36]='d';header[37]='a';header[38]='t';header[39]='a';header[40]=(byte)(totalAudioLen&0xff);header[41]=(byte)((totalAudioLen>>8)&0xff);header[42]=(byte)((totalAudioLen>>16)&0xff);header[43]=(byte)((totalAudioLen>>24)&0xff);

Android中MediaRecord和AudioRecord與MediaPlay和AudioTrack的介紹

官方提供兩種API用于音頻開發(fā)绊起,分別是MediaRecorder和AudioRecord用與音頻的采集精拟,MediaPalyer和AudioTrack用于音頻的播放

CC

小知識點:

1.在用MediaRecorder進行錄制音視頻時,最終還是會創(chuàng)建AudioRecord用來與AudioFliger進行交互虱歪。

2.MediaPlay在framework層還是創(chuàng)建AudioTrack,把解碼后的PCM數(shù)流傳遞給AudioTrack蜂绎,AudioTrack再傳遞給AudioFlinger進行混音,然后才傳遞給硬件播放实蔽,所以MediaPlayer包含了AudioTRack.

使用AudioRecord錄制pcm音頻

/**

* 采樣率荡碾,現(xiàn)在能夠保證在所有設(shè)備上使用的采樣率是44100Hz, 但是其他的采樣率(22050, 16000, 11025)在一些設(shè)備上也可以使用。

*/privatestaticfinalintSAMPLE_RATE_INHZ=44100;/**

* 聲道數(shù)局装。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是可以保證在所有設(shè)備能夠使用的。

*/privatestaticfinalintCHANNEL_CONFIG=AudioFormat.CHANNEL_IN_MONO;/**

* 返回的音頻數(shù)據(jù)的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.

*/privatestaticfinalintAUDIO_FORMAT=AudioFormat.ENCODING_PCM_16BIT;finalintminBufferSize=AudioRecord.getMinBufferSize(SAMPLE_RATE_INHZ,CHANNEL_CONFIG,AUDIO_FORMAT);audioRecord=newAudioRecord(MediaRecorder.AudioSource.MIC,SAMPLE_RATE_INHZ,CHANNEL_CONFIG,AUDIO_FORMAT,minBufferSize);finalbytedata[]=newbyte[minBufferSize];finalFilefile=newFile(getExternalFilesDir(Environment.DIRECTORY_MUSIC),"test.pcm");if(!file.mkdirs()){Log.e(TAG,"Directory not created");}if(file.exists()){file.delete();}audioRecord.startRecording();isRecording=true;newThread(newRunnable(){@Overridepublicvoidrun(){FileOutputStreamos=null;try{os=newFileOutputStream(file);}catch(FileNotFoundExceptione){e.printStackTrace();}if(null!=os){while(isRecording){intread=audioRecord.read(data,0,minBufferSize);// 如果讀取音頻數(shù)據(jù)沒有出現(xiàn)錯誤铐尚,就將數(shù)據(jù)寫入到文件if(AudioRecord.ERROR_INVALID_OPERATION!=read){try{os.write(data);}catch(IOExceptione){e.printStackTrace();}}}try{Log.i(TAG,"run: close file output stream !");os.close();}catch(IOExceptione){e.printStackTrace();}}}}).start();

PCM轉(zhuǎn)WAV

只要加上wav頭文件即可拨脉。

使用AudioTrack播放pcm音頻

AudioTrack 類為Java程序?qū)崿F(xiàn)了控制和播放簡單的音頻,它允許將PCM音頻流傳輸?shù)揭纛l接收器進行播放宣增,這是通過將音頻數(shù)據(jù)推給AudioTrack對象實現(xiàn)的玫膀,可以使用write(byte[],int,int),write(short[],int,int)或write(float[],int,int,int)方法

AudioTrack可以在兩種模式下運行:static或streaming.

在Streaming模式下,應(yīng)用程序使用其中一種write()方法將連續(xù)的數(shù)據(jù)流寫入AudioTrack爹脾,當數(shù)據(jù)從Java層傳輸?shù)絥ative層并排隊等待播放時帖旨,它們會阻塞并返回,在播放音頻數(shù)據(jù)塊時灵妨,流模式非常有用解阅,例如:

由于聲音播放的持續(xù)時間太長而不能裝入內(nèi)存,由于音頻數(shù)據(jù)的特性(高采樣率泌霍,每個樣本的位數(shù)……)而不能裝入內(nèi)存货抄,在先前排隊的音頻正在播放時接收或生成。

在處理能夠裝入內(nèi)存的短音時朱转,應(yīng)選擇靜態(tài)模式蟹地,并且需要盡可能以最小的延遲播放,因此藤为,對于經(jīng)常播放的UI和游戲聲音而言怪与,靜態(tài)模式將是優(yōu)選的,并且可能具有最小的開銷缅疟。

一旦創(chuàng)建琼梆,AudioTrack對象將初始化其關(guān)聯(lián)的音頻緩沖區(qū),在構(gòu)建過程中指定的這個緩沖區(qū)的大小決定了AudioTrack在耗盡數(shù)據(jù)之前可以播放多長時間窿吩。

對于使用靜態(tài)模式的AudioTrack茎杂,此大小是可以從中播放的最大聲音大小。

對于流模式纫雁,數(shù)據(jù)將以小于或等于總緩沖區(qū)大小的塊形式寫入音頻接收器煌往,AudioTrack不是final,因此允許使用子類轧邪,但不建議使用這種類型的子類刽脖。

使用 AudioTrack 播放音頻

/** 播放,使用stream模? ?*/

privatevoidplayInModeStream(){

/** SAMPLE_RATE_INHZ 對應(yīng)pcm音頻的采樣率* channelConfig 對應(yīng)pcm音頻的聲道* AUDIO_FORMAT 對應(yīng)pcm音頻的格式?* */

intchannelConfig=AudioFormat.CHANNEL_OUT_MONO;finalintminBufferSize=AudioTrack.getMinBufferSize(SAMPLE_RATE_INHZ,channelConfig,AUDIO_FORMAT);audioTrack=newAudioTrack(newAudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build(),newAudioFormat.Builder().setSampleRate(SAMPLE_RATE_INHZ).setEncoding(AUDIO_FORMAT).setChannelMask(channelConfig).build(),minBufferSize,AudioTrack.MODE_STREAM,AudioManager.AUDIO_SESSION_ID_GENERATE);audioTrack.play();Filefile=newFile(getExternalFilesDir(Environment.DIRECTORY_MUSIC),"test.pcm");try{fileInputStream=newFileInputStream(file);newThread(newRunnable(){@Overridepublicvoidrun(){try{byte[]tempBuffer=newbyte[minBufferSize];while(fileInputStream.available()>0){intreadCount=fileInputStream.read(tempBuffer);if(readCount==AudioTrack.ERROR_INVALID_OPERATION||readCount==AudioTrack.ERROR_BAD_VALUE){continue;}if(readCount!=0&&readCount!=-1){audioTrack.write(tempBuffer,0,readCount);}}}catch(IOExceptione){e.printStackTrace();}}}).start();}catch(IOExceptione){e.printStackTrace();}}/**

? ? * 播放忌愚,使用static模式

? ? */privatevoidplayInModeStatic(){// static模式曲管,需要將音頻數(shù)據(jù)一次性write到AudioTrack的內(nèi)部緩沖區(qū)newAsyncTask<Void,Void,Void>(){@OverrideprotectedVoiddoInBackground(Void...params){try{InputStreamin=getResources().openRawResource(R.raw.ding);try{ByteArrayOutputStreamout=newByteArrayOutputStream();for(intb;(b=in.read())!=-1;){out.write(b);}Log.d(TAG,"Got the data");audioData=out.toByteArray();}finally{in.close();}}catch(IOExceptione){Log.wtf(TAG,"Failed to read",e);}returnnull;}@OverrideprotectedvoidonPostExecute(Voidv){Log.i(TAG,"Creating track...audioData.length = "+audioData.length);// R.raw.ding鈴聲文件的相關(guān)屬性為 22050Hz, 8-bit, MonoaudioTrack=newAudioTrack(newAudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build(),newAudioFormat.Builder().setSampleRate(22050).setEncoding(AudioFormat.ENCODING_PCM_8BIT).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build(),audioData.length,AudioTrack.MODE_STATIC,AudioManager.AUDIO_SESSION_ID_GENERATE);Log.d(TAG,"Writing audio data...");audioTrack.write(audioData,0,audioData.length);Log.d(TAG,"Starting playback");audioTrack.play();Log.d(TAG,"Playing");}}.execute();}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市硕糊,隨后出現(xiàn)的幾起案子院水,更是在濱河造成了極大的恐慌腊徙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檬某,死亡現(xiàn)場離奇詭異撬腾,居然都是意外死亡,警方通過查閱死者的電腦和手機恢恼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門民傻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人场斑,你說我怎么就攤上這事漓踢。” “怎么了漏隐?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵喧半,是天一觀的道長。 經(jīng)常有香客問我锁保,道長薯酝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任爽柒,我火速辦了婚禮吴菠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浩村。我一直安慰自己做葵,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布心墅。 她就那樣靜靜地躺著酿矢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怎燥。 梳的紋絲不亂的頭發(fā)上瘫筐,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音铐姚,去河邊找鬼策肝。 笑死,一個胖子當著我的面吹牛隐绵,可吹牛的內(nèi)容都是我干的之众。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼依许,長吁一口氣:“原來是場噩夢啊……” “哼棺禾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起峭跳,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤膘婶,失蹤者是張志新(化名)和其女友劉穎缺前,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竣付,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡诡延,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年滞欠,在試婚紗的時候發(fā)現(xiàn)自己被綠了古胆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡筛璧,死狀恐怖逸绎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情夭谤,我是刑警寧澤棺牧,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站朗儒,受9級特大地震影響颊乘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜醉锄,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一乏悄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恳不,春花似錦檩小、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卵惦,卻和暖如春阻肿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沮尿。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工丛塌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛹找。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓姨伤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庸疾。 傳聞我的和親對象是個殘疾皇子乍楚,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容