实现语音数据实时采集 播放_第1页
实现语音数据实时采集 播放_第2页
实现语音数据实时采集 播放_第3页
实现语音数据实时采集 播放_第4页
实现语音数据实时采集 播放_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、实现语音数据实时采集/播放最近做的项目是和语音实时采集并发送,对方实时接收并播放相关,下面记录下实现的核心代码。 很多Android开发者应该知道android有个MediaRecorder对象和MediaPlayer对象,用于录制和播放音频。这个弊端在于他们不能实时采集并发送出去,所以,我们只能使用AudioRecord和AudioTrack来实现。 记得申明权限:<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission andr

2、oid:name="android.permission.RECORD_AUDIO" >一、AudioRecord实现核心代码介绍如下: 1、先申明相关录制配置参数private AudioRecord audioRecord;/ 录音对象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;/ 录制的缓冲数组2、在开始录制前,我们需要初始化AudioRecord类。/ 根据定义好的几个配置,来获取合适的缓冲大小/ int bufferSize = 800;int bufferSize = AudioRecord.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 实例化AudioRecordaudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, fr

4、equence, channelInConfig, audioEncoding, bufferSize);/ 定义缓冲数组buffer = new bytebufferSize;3、准备开始录制,使用循环不断读取数据。audioRecord.startRecording();/ 开始录制isRecording = true;/ 设置录制标记为true/ 开始录制while (isRecording) / 录制的内容放置到了buffer中,result代表存储长度int result = audioRecord.read(buffer, 0, buffer.length);/*.result为b

5、uffer中录制数据的长度(貌似基本上都是640)。剩下就是处理buffer了,是发送出去还是直接播放,这个随便你。*/录制循环结束后,记得关闭录制!if (audioRecord != null) audioRecord.stop();二、AudioTrack代码实现介绍如下: 1、声明播放相关配置。private AudioTrack track = null;/ 录音文件播放对象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对象(初始化一次,该对象可重复使用)/ 获取缓冲 大小bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 实例AudioTracktrack = new AudioTrack(AudioManager.STREAM

7、_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);3、使用AudioTrack播放语音数据。/将语音数据写入即可。track.write(dataArray, buffer, len);问题一: 由于目前的项目是实时采集,实时发送,所以需要考虑到包的大小,经测试,我们使用160个byte作为一个包传递可以做到比较良好的播放效果(也就是将一份buffer拆分成四个发送)。处理代码如下:/ 将数据通过监听接口回调出去if (audioRecordingCallback !=

8、null) int offset = result % MAX_DATA_LENGTH > 0 ? 1 : 0; /将一个buffer拆分成几份小数据包 MAX_DATA_LENGTH 为包的最大byte数 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; /写到回调接口 audioRecordi

9、ngCallback.onRecording(buffer, i * MAX_DATA_LENGTH, length); 问题二: 有时候传输的过来播放声音会一卡一卡的,为了解决这样的问题,暂时使用了语音双缓冲机制来解决,问题优化很明显。代码和示意图如下: 双缓冲示意图【有朋友说要源码,那我就贴下】【声音采集的源码】/* * 实时音频录制处理类<br/> * 记得申明系统权限:MODIFY_AUDIO_SETTINGS、RECORD_AUDIO<br/> * 使用实例代码:<br/> * * <pre> * audioRecoderHandler

10、 = new AudioRecoderHandler(this); * audioRecoderHandler.startRecord(new AudioRecordingCallback() * &#064;Override * public void onStopRecord(String savedPath) * * * * &#064;Override * public void onRecording(byte data, int startIndex, int length) * / TODO 录制监听。处理data即可。立即播放or发送出去,随你。 * * );

11、* </pre> * * author 李长军 * */SuppressWarnings("deprecation")public class AudioRecoderHandler private Context context = null; /* * 录音数据单次回调数组最大为多少 */ private static int MAX_DATA_LENGTH = 160; private AudioRecord audioRecord;/ 录音对象 private boolean isRecording = false;/ 标记是否正在录音中 private

12、 int frequence = 8000;/ 采样率 8000 private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定义采样通道(过时,但是使用其他的又不行 private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定义音频编码(16位) private byte buffer = null;/ 录制的缓冲数组 private File lastCacheFile = null;/ 记录上次录制的文件名 private CommonShar

13、edpreferenceHelper commonSharedpreferenceHelper; private boolean shouldSaveAudio = false;/ 标记是否保存录音历史记录 public AudioRecoderHandler(Context context) if (context = null) throw new RuntimeException("Context could not be null!"); this.context = context; commonSharedpreferenceHelper = CommonSha

14、redpreferenceHelper .getInstance(context); /* * 设置处理对象是否保存录音历史记录(如果设置为false表示不保存) * * param shouldSaveAudio * true表示保存(默认),false不保存,onStopRecord回调将会返回null */ public void setShouldSaveAudio(boolean shouldSaveAudio) this.shouldSaveAudio = shouldSaveAudio; /* * 开始录制音频 * * param callBackListener * 录制过程中

15、的回调函数 */ public void startRecord(AudioRecordingCallback audioRecordingCallback) RecordTask task = new RecordTask(audioRecordingCallback); task.execute();/ 开始执行 /* * 停止录制 */ public void stoppRecord() isRecording = false; /* * 删除上次录制的文件(一般是用户取消发送导致删除上次录制的内容) * * return true表示删除成功,false表示删除失败,一般是没有上次录制

16、的文件,或者文件已经被删除了 */ public boolean deleteLastRecordFile() boolean success = false; if (lastCacheFile != null && lastCacheFile.exists() success = lastCacheFile.delete(); return success; /* * 获取音频文件的缓存地址(获取的地址都是可以直接存储的,也就是文件夹已建立好),需要注意的是,缓存地址和用户的ID有关 * * 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" / 创建文件夹 new File(path).mkdirs(); return path; /* * 录制音频的任务类 * * author 李长军 * */ 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() / 根据定义好的几个配置,来获取合适的缓冲大小 / int bufferSize = 800; int bufferSize = AudioRecord.getMinBufferSize(frequence, channelInConfig, audioEncoding

20、); / 实例化AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelInConfig, audioEncoding, bufferSize); / 定义缓冲数组 buffer = new bytebufferSize; audioRecord.startRecording();/ 开始录制 isRecording = true;/ 设置录制标记为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; / 如果设置了要保存历史录音文件,则 创建临时文件 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(); / 开始录制 while (isRecording) / 录制的内容放置到了buffer中,result代表存储长度 int result = audioRecord.read(buffer, 0, buffer.length); / 如果设置需要保存录音文件 if (shouldSaveAudio && dataOutputStream != null) for (i

24、nt i = 0; i < result; i+) try / 将录制到的内容放置到文件中 dataOutputStream.write(bufferi); catch (IOException e) e.printStackTrace(); / 将数据回调出去 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; /* * 监听录制过程,用于实时获取录音数据 * * author 李长军 * */ public static interface AudioRecordingCallback /* * 录音数据获取回调 * * param data * 数据数组对象 * param startIndex * 数据其开始 * param length * 数据的结尾 */ public void onRecording(byte data, int startIndex, int length); /* * 录音结束后的回调 *

27、 * param savedPath * 录音文件存储的路径 */ public void onStopRecord(String savedPath); /* * 释放资源 */ public void release() if (audioRecord != null) audioRecord.release(); audioRecord = null; 【声音播放的源码】/* * 实时音频播放处理类<br/> * 使用示例代码如下:<br/> * * <pre> * audioPlayerHandler = new AudioPlayerHandler

28、(); * audioPlayerHandler.prepare();/ 播放前需要prepare。可以重复prepare * / 直接将需要播放的数据传入即可 * audioPlayerHandler.onPlaying(data, 0, data.length); * </pre> * * author 李长军 * */SuppressWarnings("deprecation")public class AudioPlayerHandler implements Runnable private AudioTrack track = null;/ 录音文件

29、播放对象 private boolean isPlaying = false;/ 标记是否正在录音中 private int frequence = 8000;/ 采样率 8000 private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定义采样通道(过时,但是使用其他的又不行 private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定义音频编码(16位) private int bufferSize = -1;/ 播放缓冲大小 / 使用双缓冲

30、机制 private ByteArrayOutputStream bufferStream0 = new ByteArrayOutputStream(); private ByteArrayOutputStream bufferStream1 = new ByteArrayOutputStream(); private int currentBuffer = -1;/ 记录当前哪个buffer填充完毕,并正在播放中。-1表示都没有。0表示第一个,1表示第二个 / 互斥信号量 private Semaphore semaphore = new Semaphore(1); / 是否释放资源的标志位

31、 private boolean release = false; public AudioPlayerHandler() / 获取缓冲 大小 bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding); / 实例AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM)

32、; try / 默认需要抢占一个信号量。防止播放进程执行 semaphore.acquire(); catch (InterruptedException e) e.printStackTrace(); / 开启播放线程 new Thread(this).start(); /* * 播放,当有新数据传入时, * * param data * 语音byte数组 * param startIndex * 开始的偏移量 * param length * 数据长度 */ public synchronized void onPlaying(byte data, int startIndex, int

33、length) if (AudioTrack.ERROR_BAD_VALUE = bufferSize) / 初始化错误 return; switch (currentBuffer) case 0: bufferStream1.write(data, startIndex, length); / 如果缓冲区不够大,暂时不往下执行 if (bufferStream1.size() > bufferSize) if (bufferStream0.size() <= 0) currentBuffer = 1; semaphore.release(); break; case -1: ca

34、se 1: bufferStream0.write(data, startIndex, length); / 如果缓冲区不够大,暂时不往下执行 if (bufferStream0.size() > bufferSize) if (bufferStream1.size() <= 0) currentBuffer = 0; semaphore.release(); break; default: break; /* * 准备播放 */ public void prepare() if (track != null && !isPlaying) track.play(); isPlaying = true; /* * 停止播

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论