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é)。
代碼實現(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用于音頻的播放
小知識點:
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();}