![實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集 播放_(tái)第1頁(yè)](http://file3.renrendoc.com/fileroot_temp3/2021-12/20/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b1.gif)
![實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集 播放_(tái)第2頁(yè)](http://file3.renrendoc.com/fileroot_temp3/2021-12/20/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b2.gif)
![實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集 播放_(tái)第3頁(yè)](http://file3.renrendoc.com/fileroot_temp3/2021-12/20/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b3.gif)
![實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集 播放_(tái)第4頁(yè)](http://file3.renrendoc.com/fileroot_temp3/2021-12/20/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b4.gif)
![實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集 播放_(tái)第5頁(yè)](http://file3.renrendoc.com/fileroot_temp3/2021-12/20/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b/d3bfc4c2-2ce0-49f2-b377-d86fa64eda6b5.gif)
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、實(shí)現(xiàn)語(yǔ)音數(shù)據(jù)實(shí)時(shí)采集/播放最近做的項(xiàng)目是和語(yǔ)音實(shí)時(shí)采集并發(fā)送,對(duì)方實(shí)時(shí)接收并播放相關(guān),下面記錄下實(shí)現(xiàn)的核心代碼。 很多Android開(kāi)發(fā)者應(yīng)該知道android有個(gè)MediaRecorder對(duì)象和MediaPlayer對(duì)象,用于錄制和播放音頻。這個(gè)弊端在于他們不能實(shí)時(shí)采集并發(fā)送出去,所以,我們只能使用AudioRecord和AudioTrack來(lái)實(shí)現(xiàn)。 記得申明權(quán)限:<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission andr
2、oid:name="android.permission.RECORD_AUDIO" >一、AudioRecord實(shí)現(xiàn)核心代碼介紹如下: 1、先申明相關(guān)錄制配置參數(shù)private AudioRecord audioRecord;/ 錄音對(duì)象private int frequence = 8000;/ 采樣率 8000private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定義采樣通道private int audioEncoding = AudioFormat.ENCODING_PC
3、M_16BIT;/ 定義音頻編碼(16位)private byte buffer = null;/ 錄制的緩沖數(shù)組2、在開(kāi)始錄制前,我們需要初始化AudioRecord類。/ 根據(jù)定義好的幾個(gè)配置,來(lái)獲取合適的緩沖大小/ int bufferSize = 800;int bufferSize = AudioRecord.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 實(shí)例化AudioRecordaudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, fr
4、equence, channelInConfig, audioEncoding, bufferSize);/ 定義緩沖數(shù)組buffer = new bytebufferSize;3、準(zhǔn)備開(kāi)始錄制,使用循環(huán)不斷讀取數(shù)據(jù)。audioRecord.startRecording();/ 開(kāi)始錄制isRecording = true;/ 設(shè)置錄制標(biāo)記為true/ 開(kāi)始錄制while (isRecording) / 錄制的內(nèi)容放置到了buffer中,result代表存儲(chǔ)長(zhǎng)度int result = audioRecord.read(buffer, 0, buffer.length);/*.result為b
5、uffer中錄制數(shù)據(jù)的長(zhǎng)度(貌似基本上都是640)。剩下就是處理buffer了,是發(fā)送出去還是直接播放,這個(gè)隨便你。*/錄制循環(huán)結(jié)束后,記得關(guān)閉錄制!if (audioRecord != null) audioRecord.stop();二、AudioTrack代碼實(shí)現(xiàn)介紹如下: 1、聲明播放相關(guān)配置。private AudioTrack track = null;/ 錄音文件播放對(duì)象private int frequence = 8000;/ 采樣率 8000private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MON
6、O;/ 定義采樣通道private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定義音頻編碼(16位)private int bufferSize = -1;/ 播放緩沖大小2、初始化AudioTrack對(duì)象(初始化一次,該對(duì)象可重復(fù)使用)/ 獲取緩沖 大小bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 實(shí)例AudioTracktrack = new AudioTrack(AudioManager.STREAM
7、_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);3、使用AudioTrack播放語(yǔ)音數(shù)據(jù)。/將語(yǔ)音數(shù)據(jù)寫(xiě)入即可。track.write(dataArray, buffer, len);問(wèn)題一: 由于目前的項(xiàng)目是實(shí)時(shí)采集,實(shí)時(shí)發(fā)送,所以需要考慮到包的大小,經(jīng)測(cè)試,我們使用160個(gè)byte作為一個(gè)包傳遞可以做到比較良好的播放效果(也就是將一份buffer拆分成四個(gè)發(fā)送)。處理代碼如下:/ 將數(shù)據(jù)通過(guò)監(jiān)聽(tīng)接口回調(diào)出去if (audioRecordingCallback !=
8、null) int offset = result % MAX_DATA_LENGTH > 0 ? 1 : 0; /將一個(gè)buffer拆分成幾份小數(shù)據(jù)包 MAX_DATA_LENGTH 為包的最大byte數(shù) for (int i = 0; i < result / MAX_DATA_LENGTH + offset; i+) int length = MAX_DATA_LENGTH; if (i + 1) * MAX_DATA_LENGTH > result) length = result - i * MAX_DATA_LENGTH; /寫(xiě)到回調(diào)接口 audioRecordi
9、ngCallback.onRecording(buffer, i * MAX_DATA_LENGTH, length); 問(wèn)題二: 有時(shí)候傳輸?shù)倪^(guò)來(lái)播放聲音會(huì)一卡一卡的,為了解決這樣的問(wèn)題,暫時(shí)使用了語(yǔ)音雙緩沖機(jī)制來(lái)解決,問(wèn)題優(yōu)化很明顯。代碼和示意圖如下: 雙緩沖示意圖【有朋友說(shuō)要源碼,那我就貼下】【聲音采集的源碼】/* * 實(shí)時(shí)音頻錄制處理類<br/> * 記得申明系統(tǒng)權(quán)限:MODIFY_AUDIO_SETTINGS、RECORD_AUDIO<br/> * 使用實(shí)例代碼:<br/> * * <pre> * audioRecoderHandler
10、 = new AudioRecoderHandler(this); * audioRecoderHandler.startRecord(new AudioRecordingCallback() * @Override * public void onStopRecord(String savedPath) * * * * @Override * public void onRecording(byte data, int startIndex, int length) * / TODO 錄制監(jiān)聽(tīng)。處理data即可。立即播放or發(fā)送出去,隨你。 * * );
11、* </pre> * * author 李長(zhǎng)軍 * */SuppressWarnings("deprecation")public class AudioRecoderHandler private Context context = null; /* * 錄音數(shù)據(jù)單次回調(diào)數(shù)組最大為多少 */ private static int MAX_DATA_LENGTH = 160; private AudioRecord audioRecord;/ 錄音對(duì)象 private boolean isRecording = false;/ 標(biāo)記是否正在錄音中 private
12、 int frequence = 8000;/ 采樣率 8000 private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定義采樣通道(過(guò)時(shí),但是使用其他的又不行 private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定義音頻編碼(16位) private byte buffer = null;/ 錄制的緩沖數(shù)組 private File lastCacheFile = null;/ 記錄上次錄制的文件名 private CommonShar
13、edpreferenceHelper commonSharedpreferenceHelper; private boolean shouldSaveAudio = false;/ 標(biāo)記是否保存錄音歷史記錄 public AudioRecoderHandler(Context context) if (context = null) throw new RuntimeException("Context could not be null!"); this.context = context; commonSharedpreferenceHelper = CommonSha
14、redpreferenceHelper .getInstance(context); /* * 設(shè)置處理對(duì)象是否保存錄音歷史記錄(如果設(shè)置為false表示不保存) * * param shouldSaveAudio * true表示保存(默認(rèn)),false不保存,onStopRecord回調(diào)將會(huì)返回null */ public void setShouldSaveAudio(boolean shouldSaveAudio) this.shouldSaveAudio = shouldSaveAudio; /* * 開(kāi)始錄制音頻 * * param callBackListener * 錄制過(guò)程中
15、的回調(diào)函數(shù) */ public void startRecord(AudioRecordingCallback audioRecordingCallback) RecordTask task = new RecordTask(audioRecordingCallback); task.execute();/ 開(kāi)始執(zhí)行 /* * 停止錄制 */ public void stoppRecord() isRecording = false; /* * 刪除上次錄制的文件(一般是用戶取消發(fā)送導(dǎo)致刪除上次錄制的內(nèi)容) * * return true表示刪除成功,false表示刪除失敗,一般是沒(méi)有上次錄制
16、的文件,或者文件已經(jīng)被刪除了 */ public boolean deleteLastRecordFile() boolean success = false; if (lastCacheFile != null && lastCacheFile.exists() success = lastCacheFile.delete(); return success; /* * 獲取音頻文件的緩存地址(獲取的地址都是可以直接存儲(chǔ)的,也就是文件夾已建立好),需要注意的是,緩存地址和用戶的ID有關(guān) * * return 音頻文件的緩存地址路徑,如果獲取失敗,返回null */ priva
17、te String getOutputDir() String path = null; File cacheFile = null; if (context != null) cacheFile = context .getExternalFilesDir(android.os.Environment.DIRECTORY_MUSIC); if (cacheFile = null) Toast.makeText(context, "您的SD卡不可用", Toast.LENGTH_SHORT).show(); else path = cacheFile.getAbsolute
18、Path() + "/" + commonSharedpreferenceHelper.getCurrentUserID() + "/record" / 創(chuàng)建文件夾 new File(path).mkdirs(); return path; /* * 錄制音頻的任務(wù)類 * * author 李長(zhǎng)軍 * */ private class RecordTask extends AsyncTask<String, Integer, String> private AudioRecordingCallback audioRecordingCallba
19、ck = null; public RecordTask(AudioRecordingCallback audioRecordingCallback) this.audioRecordingCallback = oRecordingCallback; Override protected void onPreExecute() / 根據(jù)定義好的幾個(gè)配置,來(lái)獲取合適的緩沖大小 / int bufferSize = 800; int bufferSize = AudioRecord.getMinBufferSize(frequence, channelInConfig, audioEncoding
20、); / 實(shí)例化AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelInConfig, audioEncoding, bufferSize); / 定義緩沖數(shù)組 buffer = new bytebufferSize; audioRecord.startRecording();/ 開(kāi)始錄制 isRecording = true;/ 設(shè)置錄制標(biāo)記為true Override protected void onPostExecute(String result) aud
21、ioRecord = null; if (result = null) lastCacheFile = null; else lastCacheFile = new File(result); if (audioRecordingCallback != null) audioRecordingCallback.onStopRecord(result); Override protected String doInBackground(String. params) String cacheDir = getOutputDir(); String tempFileName = null; Fil
22、e cacheFile = null; / 輸出的文件流 DataOutputStream dataOutputStream = null; / 如果設(shè)置了要保存歷史錄音文件,則 創(chuàng)建臨時(shí)文件 if (shouldSaveAudio && cacheDir != null) tempFileName = cacheDir + "/" + System.currentTimeMillis(); cacheFile = new File(pFileName); try dataOutputStream = new DataOutputStream( new Bu
23、fferedOutputStream(new FileOutputStream( cacheFile); catch (FileNotFoundException e) e.printStackTrace(); / 開(kāi)始錄制 while (isRecording) / 錄制的內(nèi)容放置到了buffer中,result代表存儲(chǔ)長(zhǎng)度 int result = audioRecord.read(buffer, 0, buffer.length); / 如果設(shè)置需要保存錄音文件 if (shouldSaveAudio && dataOutputStream != null) for (i
24、nt i = 0; i < result; i+) try / 將錄制到的內(nèi)容放置到文件中 dataOutputStream.write(bufferi); catch (IOException e) e.printStackTrace(); / 將數(shù)據(jù)回調(diào)出去 if (audioRecordingCallback != null) int offset = result % MAX_DATA_LENGTH > 0 ? 1 : 0; for (int i = 0; i < result / MAX_DATA_LENGTH + offset; i+) int length =
25、MAX_DATA_LENGTH; if (i + 1) * MAX_DATA_LENGTH > result) length = result - i * MAX_DATA_LENGTH; audioRecordingCallback.onRecording(buffer, i * MAX_DATA_LENGTH, length); if (audioRecord != null) audioRecord.stop(); if (dataOutputStream != null) try dataOutputStream.close(); catch (IOException e) e.
26、printStackTrace(); return tempFileName; /* * 監(jiān)聽(tīng)錄制過(guò)程,用于實(shí)時(shí)獲取錄音數(shù)據(jù) * * author 李長(zhǎng)軍 * */ public static interface AudioRecordingCallback /* * 錄音數(shù)據(jù)獲取回調(diào) * * param data * 數(shù)據(jù)數(shù)組對(duì)象 * param startIndex * 數(shù)據(jù)其開(kāi)始 * param length * 數(shù)據(jù)的結(jié)尾 */ public void onRecording(byte data, int startIndex, int length); /* * 錄音結(jié)束后的回調(diào) *
27、 * param savedPath * 錄音文件存儲(chǔ)的路徑 */ public void onStopRecord(String savedPath); /* * 釋放資源 */ public void release() if (audioRecord != null) audioRecord.release(); audioRecord = null; 【聲音播放的源碼】/* * 實(shí)時(shí)音頻播放處理類<br/> * 使用示例代碼如下:<br/> * * <pre> * audioPlayerHandler = new AudioPlayerHandler
28、(); * audioPlayerHandler.prepare();/ 播放前需要prepare??梢灾貜?fù)prepare * / 直接將需要播放的數(shù)據(jù)傳入即可 * audioPlayerHandler.onPlaying(data, 0, data.length); * </pre> * * author 李長(zhǎng)軍 * */SuppressWarnings("deprecation")public class AudioPlayerHandler implements Runnable private AudioTrack track = null;/ 錄音文件
29、播放對(duì)象 private boolean isPlaying = false;/ 標(biāo)記是否正在錄音中 private int frequence = 8000;/ 采樣率 8000 private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定義采樣通道(過(guò)時(shí),但是使用其他的又不行 private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定義音頻編碼(16位) private int bufferSize = -1;/ 播放緩沖大小 / 使用雙緩沖
30、機(jī)制 private ByteArrayOutputStream bufferStream0 = new ByteArrayOutputStream(); private ByteArrayOutputStream bufferStream1 = new ByteArrayOutputStream(); private int currentBuffer = -1;/ 記錄當(dāng)前哪個(gè)buffer填充完畢,并正在播放中。-1表示都沒(méi)有。0表示第一個(gè),1表示第二個(gè) / 互斥信號(hào)量 private Semaphore semaphore = new Semaphore(1); / 是否釋放資源的標(biāo)志位
31、 private boolean release = false; public AudioPlayerHandler() / 獲取緩沖 大小 bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding); / 實(shí)例AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM)
32、; try / 默認(rèn)需要搶占一個(gè)信號(hào)量。防止播放進(jìn)程執(zhí)行 semaphore.acquire(); catch (InterruptedException e) e.printStackTrace(); / 開(kāi)啟播放線程 new Thread(this).start(); /* * 播放,當(dāng)有新數(shù)據(jù)傳入時(shí), * * param data * 語(yǔ)音byte數(shù)組 * param startIndex * 開(kāi)始的偏移量 * param length * 數(shù)據(jù)長(zhǎng)度 */ public synchronized void onPlaying(byte data, int startIndex, int
33、length) if (AudioTrack.ERROR_BAD_VALUE = bufferSize) / 初始化錯(cuò)誤 return; switch (currentBuffer) case 0: bufferStream1.write(data, startIndex, length); / 如果緩沖區(qū)不夠大,暫時(shí)不往下執(zhí)行 if (bufferStream1.size() > bufferSize) if (bufferStream0.size() <= 0) currentBuffer = 1; semaphore.release(); break; case -1: ca
34、se 1: bufferStream0.write(data, startIndex, length); / 如果緩沖區(qū)不夠大,暫時(shí)不往下執(zhí)行 if (bufferStream0.size() > bufferSize) if (bufferStream1.size() <= 0) currentBuffer = 0; semaphore.release(); break; default: break; /* * 準(zhǔn)備播放 */ public void prepare() if (track != null && !isPlaying) track.play(); isPlaying = true; /* * 停止播
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 人教版數(shù)學(xué)八年級(jí)上冊(cè)15.4.1《提公因式法因式分解》聽(tīng)評(píng)課記錄
- 人教版七年級(jí)地理上冊(cè):4.2《世界的語(yǔ)言和宗教》聽(tīng)課評(píng)課記錄1
- 八年級(jí)歷史上聽(tīng)課評(píng)課記錄《第一單元第1課鴉片戰(zhàn)爭(zhēng)》聽(tīng)課評(píng)課記錄
- 用維修基金維修電梯主機(jī)軸承合同
- 生態(tài)項(xiàng)目投資合作協(xié)議書(shū)(2篇)
- 人教版數(shù)學(xué)八年級(jí)上冊(cè)聽(tīng)評(píng)課記錄15.2.1《分式的乘除》
- 部編版八年級(jí)道德與法治下冊(cè)第五課《我國(guó)基本制度》第3課時(shí)《基本政治制度》聽(tīng)課評(píng)課記錄
- 北師大版數(shù)學(xué)一年級(jí)上冊(cè)第一單元《生活中的數(shù) 第3課時(shí) 玩具》聽(tīng)評(píng)課記錄
- 北師大版數(shù)學(xué)五年級(jí)上冊(cè)《軸對(duì)稱再認(rèn)識(shí)(一)》聽(tīng)評(píng)課記錄2
- 冀教版數(shù)學(xué)七年級(jí)下冊(cè)《數(shù)學(xué)活動(dòng) 拼圖與分解因式》聽(tīng)評(píng)課記錄
- 初中數(shù)學(xué)教學(xué)“教-學(xué)-評(píng)”一體化研究
- 2012年安徽高考理綜試卷及答案-文檔
- 《游戲界面設(shè)計(jì)專題實(shí)踐》課件-知識(shí)點(diǎn)5:圖標(biāo)繪制準(zhǔn)備與繪制步驟
- 自動(dòng)扶梯安裝過(guò)程記錄
- MOOC 材料科學(xué)基礎(chǔ)-西安交通大學(xué) 中國(guó)大學(xué)慕課答案
- 智慧供熱管理系統(tǒng)方案可行性研究報(bào)告
- 帕金森病的言語(yǔ)康復(fù)治療
- 中國(guó)城市居民的健康意識(shí)和生活方式調(diào)研分析報(bào)告
- 上海星巴克員工手冊(cè)
- 貓狗創(chuàng)業(yè)計(jì)劃書(shū)
- 復(fù)產(chǎn)復(fù)工試題含答案
評(píng)論
0/150
提交評(píng)論