实现语音数据实时采集 播放.docx

上传人:b****3 文档编号:27446585 上传时间:2023-07-01 格式:DOCX 页数:21 大小:148.43KB
下载 相关 举报
实现语音数据实时采集 播放.docx_第1页
第1页 / 共21页
实现语音数据实时采集 播放.docx_第2页
第2页 / 共21页
实现语音数据实时采集 播放.docx_第3页
第3页 / 共21页
实现语音数据实时采集 播放.docx_第4页
第4页 / 共21页
实现语音数据实时采集 播放.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

实现语音数据实时采集 播放.docx

《实现语音数据实时采集 播放.docx》由会员分享,可在线阅读,更多相关《实现语音数据实时采集 播放.docx(21页珍藏版)》请在冰豆网上搜索。

实现语音数据实时采集 播放.docx

实现语音数据实时采集播放

实现语音数据实时采集/播放

最近做的项目是和语音实时采集并发送,对方实时接收并播放相关,下面记录下实现的核心代码。

很多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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1