实现语音数据实时采集 播放.docx
- 文档编号:27446585
- 上传时间:2023-07-01
- 格式:DOCX
- 页数:21
- 大小:148.43KB
实现语音数据实时采集 播放.docx
《实现语音数据实时采集 播放.docx》由会员分享,可在线阅读,更多相关《实现语音数据实时采集 播放.docx(21页珍藏版)》请在冰豆网上搜索。
实现语音数据实时采集播放
实现语音数据实时采集/播放
最近做的项目是和语音实时采集并发送,对方实时接收并播放相关,下面记录下实现的核心代码。
很多Android开发者应该知道android有个MediaRecorder对象和MediaPlayer对象,用于录制和播放音频。
这个弊端在于他们不能实时采集并发送出去,所以,我们只能使用AudioRecord和AudioTrack来实现。
记得申明权限:
name="android.permission.MODIFY_AUDIO_SETTINGS"/> name="android.permission.RECORD_AUDIO"> 一、AudioRecord实现核心代码介绍如下: 1、先申明相关录制配置参数 privateAudioRecordaudioRecord;//录音对象 privateintfrequence=8000;//采样率8000 privateintchannelInConfig=AudioFormat.CHANNEL_CONFIGURATION_MONO;//定义采样通道 privateintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;//定义音频编码(16位) privatebyte[]buffer=null;//录制的缓冲数组 2、在开始录制前,我们需要初始化AudioRecord类。 //根据定义好的几个配置,来获取合适的缓冲大小 //intbufferSize=800; intbufferSize=AudioRecord.getMinBufferSize(frequence, channelInConfig,audioEncoding); //实例化AudioRecord audioRecord=newAudioRecord(MediaRecorder.AudioSource.MIC, frequence,channelInConfig,audioEncoding,bufferSize); //定义缓冲数组 buffer=newbyte[bufferSize]; 3、准备开始录制,使用循环不断读取数据。 audioRecord.startRecording();//开始录制 isRecording=true;//设置录制标记为true //开始录制 while(isRecording){ //录制的内容放置到了buffer中,result代表存储长度 intresult=audioRecord.read(buffer,0,buffer.length); /*.....result为buffer中录制数据的长度(貌似基本上都是640)。 剩下就是处理buffer了,是发送出去还是直接播放,这个随便你。 */ } //录制循环结束后,记得关闭录制! ! if(audioRecord! =null){ audioRecord.stop(); } 二、AudioTrack代码实现介绍如下: 1、声明播放相关配置。 privateAudioTracktrack=null;//录音文件播放对象 privateintfrequence=8000;//采样率8000 privateintchannelInConfig=AudioFormat.CHANNEL_CONFIGURATION_MONO;//定义采样通道 privateintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;//定义音频编码(16位) privateintbufferSize=-1;//播放缓冲大小 2、初始化AudioTrack对象(初始化一次,该对象可重复使用) //获取缓冲大小 bufferSize=AudioTrack.getMinBufferSize(frequence,channelInConfig, audioEncoding); //实例AudioTrack track=newAudioTrack(AudioManager.STREAM_MUSIC,frequence, channelInConfig,audioEncoding,bufferSize, AudioTrack.MODE_STREAM); 3、使用AudioTrack播放语音数据。 //将语音数据写入即可。 track.write(dataArray,buffer,len); 问题一: 由于目前的项目是实时采集,实时发送,所以需要考虑到包的大小,经测试,我们使用160个byte作为一个包传递可以做到比较良好的播放效果(也就是将一份buffer拆分成四个发送)。 处理代码如下: //将数据通过监听接口回调出去 if(audioRecordingCallback! =null){ intoffset=result%MAX_DATA_LENGTH>0? 1: 0; //将一个buffer拆分成几份小数据包MAX_DATA_LENGTH为包的最大byte数 for(inti=0;i intlength=MAX_DATA_LENGTH; if((i+1)*MAX_DATA_LENGTH>result){ length=result-i*MAX_DATA_LENGTH; } //写到回调接口 audioRecordingCallback.onRecording(buffer,i *MAX_DATA_LENGTH,length); } } 问题二: 有时候传输的过来播放声音会一卡一卡的,为了解决这样的问题,暂时使用了语音双缓冲机制来解决,问题优化很明显。 代码和示意图如下: 【声音采集的源码】 /** *实时音频录制处理类 *记得申明系统权限: MODIFY_AUDIO_SETTINGS、RECORD_AUDIO *使用实例代码: * * *audioRecoderHandler=newAudioRecoderHandler(this); *audioRecoderHandler.startRecord(newAudioRecordingCallback(){ *@Override *publicvoidonStopRecord(StringsavedPath){ * *} * *@Override *publicvoidonRecording(byte[]data,intstartIndex,intlength){ *//TODO录制监听。 处理data即可。 立即播放or发送出去,随你。 *} *}); * * *@author李长军 * */ @SuppressWarnings("deprecation") publicclassAudioRecoderHandler{ privateContextcontext=null; /** *录音数据单次回调数组最大为多少 */ privatestaticintMAX_DATA_LENGTH=160; privateAudioRecordaudioRecord;//录音对象 privatebooleanisRecording=false;//标记是否正在录音中 privateintfrequence=8000;//采样率8000 privateintchannelInConfig=AudioFormat.CHANNEL_CONFIGURATION_MONO;//定义采样通道(过时,但是使用其他的又不行 privateintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;//定义音频编码(16位) privatebyte[]buffer=null;//录制的缓冲数组 privateFilelastCacheFile=null;//记录上次录制的文件名 privateCommonSharedpreferenceHelpercommonSharedpreferenceHelper; privatebooleanshouldSaveAudio=false;//标记是否保存录音历史记录 publicAudioRecoderHandler(Contextcontext){ if(context==null){ thrownewRuntimeException("Contextcouldnotbenull! "); } this.context=context; commonSharedpreferenceHelper=CommonSharedpreferenceHelper .getInstance(context); } /** *设置处理对象是否保存录音历史记录(如果设置为false表示不保存) * *@paramshouldSaveAudio *true表示保存(默认),false不保存,onStopRecord回调将会返回null */ publicvoidsetShouldSaveAudio(booleanshouldSaveAudio){ this.shouldSaveAudio=shouldSaveAudio; } /** *开始录制音频 * *@paramcallBackListener *录制过程中的回调函数 */ publicvoidstartRecord(AudioRecordingCallbackaudioRecordingCallback){ RecordTasktask=newRecordTask(audioRecordingCallback); task.execute();//开始执行 } /** *停止录制 */ publicvoidstoppRecord(){ isRecording=false; } /** *删除上次录制的文件(一般是用户取消发送导致删除上次录制的内容) * *@returntrue表示删除成功,false表示删除失败,一般是没有上次录制的文件,或者文件已经被删除了 */ publicbooleandeleteLastRecordFile(){ booleaness=false; if(lastCacheFile! =null&&lastCacheFile.exists()){ success=lastCacheFile.delete(); } returnsuccess; } /** *获取音频文件的缓存地址(获取的地址都是可以直接存储的,也就是文件夹已建立好),需要注意的是,缓存地址和用户的ID有关 * *@return音频文件的缓存地址路径,如果获取失败,返回null */ privateStringgetOutputDir(){ Stringpath=null; FilecacheFile=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.getAbsolutePath()+"/" +commonSharedpreferenceHelper.getCurrentUserID() +"/record"; //创建文件夹 newFile(path).mkdirs(); } } returnpath; } /** *录制音频的任务类 * *@author李长军 * */ privateclassRecordTaskextendsAsyncTask privateAudioRecordingCallbackaudioRecordingCallback=null; publicRecordTask(AudioRecordingCallbackaudioRecordingCallback){ this.audioRecordingCallback=audioRecordingCallback; } @Override protectedvoidonPreExecute(){ //根据定义好的几个配置,来获取合适的缓冲大小 //intbufferSize=800; intbufferSize=AudioRecord.getMinBufferSize(frequence, channelInConfig,audioEncoding); //实例化AudioRecord audioRecord=newAudioRecord(MediaRecorder.AudioSource.MIC, frequence,channelInConfig,audioEncoding,bufferSize); //定义缓冲数组 buffer=newbyte[bufferSize]; audioRecord.startRecording();//开始录制 isRecording=true;//设置录制标记为true } @Override protectedvoidonPostExecute(Stringresult){ audioRecord=null; if(result==null){ lastCacheFile=null; }else{ lastCacheFile=newFile(result); } if(audioRecordingCallback! =null){ audioRecordingCallback.onStopRecord(result); } } @Override protectedStringdoInBackground(String...params){ StringcacheDir=getOutputDir(); StringtempFileName=null; FilecacheFile=null; //输出的文件流 DataOutputStreamdataOutputStream=null; //如果设置了要保存历史录音文件,则创建临时文件 if(shouldSaveAudio&&cacheDir! =null){ tempFileName=cacheDir+"/"+System.currentTimeMillis(); cacheFile=newFile(tempFileName); try{ dataOutputStream=newDataOutputStream( newBufferedOutputStream(newFileOutputStream( cacheFile))); }catch(FileNotFoundExceptione){ e.printStackTrace(); } } //开始录制 while(isRecording){ //录制的内容放置到了buffer中,result代表存储长度 intresult=audioRecord.read(buffer,0,buffer.length); //如果设置需要保存录音文件 if(shouldSaveAudio&&dataOutputStream! =null){ for(inti=0;i try{ //将录制到的内容放置到文件中 dataOutputStream.write(buffer[i]); }catch(IOExceptione){ e.printStackTrace(); } } } //将数据回调出去 if(audioRecordingCallback! =null){ intoffset=result%MAX_DATA_LENGTH>0? 1: 0; for(inti=0;i intlength=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(IOExceptione){ e.printStackTrace(); } } returntempFileName; } } /** *监听录制过程,用于实时获取录音数据 * *@author李长军 * */ publicstaticinterfaceAudioRecordingCallback{ /** *录音数据获取回调 * *@paramdata *数据数组对象 *@paramstartIndex *数据其开始 *@paramlength *数据的结尾 */ publicvoidonRecording(byte[]data,intstartIndex,intlength); /** *录音结束后的回调 * *@paramsavedPath *录音文件存储的路径 */ publicvoidonStopRecord(StringsavedPath); } /** *释放资源 */ publicvoidrelease(){ if(audioRecord! =null){ audioRecord.release(); audioRecord=null; } } } 【声音播放的源码】 /** *实时音频播放处理类 *使用示例代码如下: * * *audioPlayerHandler=newAudioPlayerHandler(); *audioPlayerHandler.prepare();//播放前需要prepare。 可以重复prepare *//直接将需要播放的数据传入即可 *audioPlayerHandler.onPlaying(data,0,data.length); * * *@author李长军 * */ @SuppressWarnings("deprecation") publicclassAudioPlayerHandlerimplementsRunnable{ privateAudioTracktrack=null;//录音文件播放对象 privatebooleanisPlaying=false;//标记是否正在录音中 privateintfrequence=8000;//采样率8000 privateintchannelInConfig=AudioFormat.CHANNEL_CONFIGURATION_MONO;//定义采样通道(过时,但是使用其他的又不行 privateintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;//定义音频编码(16位) privateintbufferSize=-1;//播放缓冲大小 //使用双缓冲机制 privateByteArrayOutputStreambufferStream0=newByteArrayOutputStream(); privateByteArrayOutputStreambufferStream1=newByteArrayOutputStream(); privateintcurrentBuffer=-1;//记录当前哪个buffer填充完毕,并正在播放中。 -1表示都没有。 0表示第一个,1表示第二个 //互斥信号量 privateSemaphoresemaphore=newSemaphore (1); //是否释放资源的标志位 privatebooleanrelease=false; publicAudioPlayerHandler(){ //获取缓冲大小 bufferSize=AudioTrack.getMinBufferSize(frequence,channelInConfig, audioEncoding); //实例AudioTrack t
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实现语音数据实时采集 播放 实现 语音 数据 实时 采集