实现语音数据实时采集 播放资料Word格式.docx
《实现语音数据实时采集 播放资料Word格式.docx》由会员分享,可在线阅读,更多相关《实现语音数据实时采集 播放资料Word格式.docx(17页珍藏版)》请在冰豆网上搜索。
/开始录制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;
/定义音频编码(16位)privateintbufferSize=-1;
/播放缓冲大小2、初始化AudioTrack对象(初始化一次,该对象可重复使用)/获取缓冲大小bufferSize=AudioTrack.getMinBufferSize(frequence,channelInConfig,audioEncoding);
/实例AudioTracktrack=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_LENGTH0?
1:
0;
/将一个buffer拆分成几份小数据包MAX_DATA_LENGTH为包的最大byte数for(inti=0;
iresult)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()*&
#064;
Override*publicvoidonStopRecord(StringsavedPath)*&
Override*publicvoidonRecording(bytedata,intstartIndex,intlength)*/TODO录制监听。
处理data即可。
立即播放or发送出去,随你。
*);
*author李长军*/SuppressWarnings(deprecation)publicclassAudioRecoderHandlerprivateContextcontext=null;
/*录音数据单次回调数组最大为多少*/privatestaticintMAX_DATA_LENGTH=160;
privateAudioRecordaudioRecord;
/录音对象privatebooleanisRecording=false;
/标记是否正在录音中privateintfrequence=8000;
/定义采样通道(过时,但是使用其他的又不行privateintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;
/录制的缓冲数组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()booleansuccess=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();
elsepath=cacheFile.getAbsolutePath()+/+commonSharedpreferenceHelper.getCurrentUserID()+/record;
/创建文件夹newFile(path).mkdirs();
returnpath;
/*录制音频的任务类*author李长军*/privateclassRecordTaskextendsAsyncTaskprivateAudioRecordingCallbackaudioRecordingCallback=null;
publicRecordTask(AudioRecordingCallbackaudioRecordingCallback)this.audioRecordingCallback=oRecordingCallback;
OverrideprotectedvoidonPreExecute()/根据定义好的几个配置,来获取合适的缓冲大小/intbufferSize=800;
/设置录制标记为trueOverrideprotectedvoidonPostExecute(Stringresult)audioRecord=null;
if(result=null)lastCacheFile=null;
elselastCacheFile=newFile(result);
if(audioRecordingCallback!
=null)audioRecordingCallback.onStopRecord(result);
OverrideprotectedStringdoInBackground(String.params)StringcacheDir=getOutputDir();
StringtempFileName=null;
/输出的文件流DataOutputStreamdataOutputStream=null;
/如果设置了要保存历史录音文件,则创建临时文件if(shouldSaveAudio&
cacheDir!
=null)tempFileName=cacheDir+/+System.currentTimeMillis();
cacheFile=newFile(pFileName);
trydataOutputStream=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;
i0?
for(inti=0;
audioRecordingCallback.onRecording(buffer,i*MAX_DATA_LENGTH,length);
if(dataOutputStream!
=null)trydataOutputStream.close();
catch(IOExceptione)e.printStackTrace();
returntempFileName;
/*监听录制过程,用于实时获取录音数据*author李长军*/publicstaticinterfaceAudioRecordingCallback/*录音数据获取回调*paramdata*数据数组对象*paramstartIndex*数据其开始*paramlength*数据的结尾*/publicvoidonRecording(bytedata,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)publicclassAudioPlayerHandlerimplementsRunnableprivateAudioTracktrack=null;
/录音文件播放对象privatebooleanisPlaying=false;
/播放缓冲大小/使用双缓冲机制privateByteArrayOutputStreambufferStream0=newByteArrayOutputStream();
privateByteArrayOutputStreambufferStream1=newByteArrayOutputStream();
privateintcurrentBuffer=-1;
/记录当前哪个buffer填充完毕,并正在播放中。
-1表示都没有。
0表示第一个,1表示第二个/互斥信号量privateSemaphoresemaphore=newSemaphore
(1);
/是否释放资源的标志位privatebooleanrelease=false;
publicAudioPlayerHandler()/获取缓冲大小bufferSize=AudioTrack.getMinBufferSize(frequence,channelInConfig,audioEncoding);
try/默认需要抢占一个信号量。
防止播放进程执行semaphore.acquire();
catch(InterruptedExceptione)e.printStackTrace();
/开启播放线程newThread(this).start();
/*播放,当有新数据传入时,*paramdata*语音byte数组*paramstartIndex*开始的偏移量*paramlength*数据长度*/publicsynchronizedvoidonPlaying(bytedata,intstartIndex,intlength)if(AudioTrack.ERROR_BAD_VALUE=bufferSize)/初始化错误return;
switch(currentBuffer)case0:
bufferStream1.write(data,startIndex,length);
/如果缓冲区不够大,暂时不往下执行if(bufferStream1.size()bufferSize)if(bufferStream0.size()bufferSize)if(bufferStream1.size()=0)currentBuffer=0;
semaphore.release();
break;
default:
/*准备播放*/publicvoidprepare()if(track!
!
isPlaying)track.play();
isPlaying=true;
/*停止播放*/publicvoidstop()if(track!
=null)track.stop();
isPlaying=false;
/*释放资源*/publicvoidrelease()release=true;
if(track!
=null)track.release();
track=null;
trybufferStream0.close();
bufferStream0=null;
Overridepublicvoidrun()while(true)trysemaphore.acquire();
/如果资源释放了,则不再执行if(release)return;
if(currentBuffer=-1)continue;
/如果当前是第一个buff填充完毕if(currentBuffer=0)runPlay(bufferStream0);
/如果当前是第二个buff填充完毕elseif(currentBuffer=1)runPlay(bufferStream1);
/*执行播放语音*parambufferStream*/privatevoidrunPlay(ByteArrayOutputStreambufferStream)intdataSize=bufferStream.size();
bytedataArray=bufferStream.toByteArray();
intcount=dataSize/bufferSize;
/将数据按照buffer的大小可以切分的份数intoffset=dataSize%bufferSize=0?
0:
1;
/如果有余数,则还得循环一次for(inti=0;
idataSize)len=dataSize-i*bufferSize;
elselen=bufferSize;
track.write(dataArray,i*bufferSize,len);
bufferStream.reset();
/重置Stream