




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、实现语音数据实时采集 /播放最近做的项目是和语音实时采集并发送, 对方实时接收并播放相关, 下面记录下实现的核心 代码。很多 Android 开发者应该知道 android 有个 MediaRecorder 对象和 MediaPlayer 对象, 用于录 制和播放音频。这个弊端在于他们不能实时采集并发送出去,所以,我们只能使用 AudioRecord 和 AudioTrack 来实现。记得申明权限:一、 AudioRecord 实现核心代码介绍如下:1、先申明相关录制配置参数private AudioRecord audioRecord;/ 录音对象private int frequence
2、= 8000;/ 采样率 8000private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定义采 样通道private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/定义音频编码( 16 位)private byte buffer = null;/ 录制的缓冲数组2、在开始录制前,我们需要初始化AudioRecord 类。/ 根据定义好的几个配置,来获取合适的缓冲大小/ int bufferSize = 800;int bufferSize = Aud
3、ioRecord.getMinBufferSize(frequence,channelInConfig, audioEncoding);/ 实例化 AudioRecordaudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequence, channelInConfig, audioEncoding, bufferSize);/ 定义缓冲数组buffer = new bytebufferSize;3、准备开始录制,使用循环不断读取数据。audioRecord.startRecording();/ 开始录制isRecordi
4、ng = true;/ 设置录制标记为 true/ 开始录制while (isRecording) / 录制的内容放置到了 buffer 中, result 代表存储长度int result = audioRecord.read(buffer, 0, buffer.length);/*result 为 buffer 中录制数据的长度 (貌似基本上都是 640)。剩下就是处理 buffer 了,是发送出去还是直接播放,这个随便你。 */录制循环结束后,记得关闭录制! !if (audioRecord != null) audioRecord.stop();二、 AudioTrack 代码实现介绍
5、如下:1、声明播放相关配置。private AudioTrack track = null;/ 录音文件播放对象private int frequence = 8000;/ 采样率 8000private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定义采 样通道private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/定义音频编码( 16 位)private int bufferSize = -1;/ 播放缓冲大小2、初始化 AudioTrack 对
6、象(初始化一次,该对象可重复使用)/ 获取缓冲 大小bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 实例 AudioTracktrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);3、使用 AudioTrack 播放语音数据。/将语音数据写入即可。track.write(
7、dataArray, buffer, len);问题一: 由于目前的项目是实时采集,实时发送,所以需要考虑到包的大小,经测试,我们使用 160 个 byte 作为一个包传递可以做到比较良好的播放效果(也就是将一份 buffer 拆分成四个发 送)。处理代码如下:/ 将数据通过监听接口回调出去if (audioRecordingCallback != null) int offset = result % MAX_DA TA_LENGTH 0 ? 1 : 0;/将一个 buffer 拆分成几份小数据包 MAX_DATA_LENGTH 为包的最大 byte 数for (int i = 0; i r
8、esult) length = result - i * MAX_DA TA_LENGTH;/写到回调接口 audioRecordingCallback.onRecording(buffer, i* MAX_DATA_LENGTH, length);问题二:有时候传输的过来播放声音会一卡一卡的,为了解决这样的问题,暂时使用了语音双缓冲机buHcrl制来解决,问题优化很明显。代码和示意图如下:|JlLIf,4*7=:#I EiTfvrI|.XB-| i- 5 11 .总彳人憚云rwjwn.niBMfiriBwFrtEllmtiij. cAII44U .R-arliMirii ;v*WIf rhT
9、KfrlliwtfliVhlMrd DMirvrJLjvlrvrvhrMf*r - I a MEME:b=n . TLtri* -K4*MIAHM rrp-i.TpMi Ft FV -I T tfFwrtrri Ili iSuffliKfiUtl.LU4t.id Clf uBdiHvKwfcTrftn | p jif ImuEmRiAuftvr4-t iiumj匕斡匕書快ir iwrrvE 時 i i rwi-f a-3T打ittHft* - ZMMWI! hr |jrMfVMMEV-r rrstf 1當 IMMtK twJ p jr IW.HE r , n klfcj-1 Si, : At
10、r !:-jE Jl r j t ffrt 3=1 e r1*1 Mift FlC rU- tlJ I: f| dflTaJzTaij |?Zf tSxxvaZ _ =3rtJLrxH.7 |- j1MI. geU 电 4JW / feMflVEJirt J 苗計鞘裤加VAR mc 4BE-4iii- 1 r* S ? fll ihV I LltaL 1 - -Sj 1 4- C ff Hl 3*1B Ivuhf-FEei 3Ie*14t4i3-hE4-h! Hn tMW Z * 皿 t 114I4 4【声音采集的源码】*实时音频录制处理类* 记得申明系统权限:MODIFY_AUDIO_SET
11、TINGS 、RECORD_AUDIO*使用实例代码:* * audioRecoderHa ndler = new AudioRecoderHa ndler(this);* audioRecoderHandler.startRecord(new AudioRecordingCallback() * Override* public void onStopRecord(String savedPath) * * Override* public void onRecording(byte data, int startIndex, int length) * / TODO 录制监听。处理 dat
12、a 即可。立即播放 or 发送出去,随你。* * );* * author 李长军*/ SuppressWarnings(deprecation) public class AudioRecoderHandler private Context context = null;/* 录音数据单次回调数组最大为多少*/private static int MAX_DA TA_LENGTH = 160;private AudioRecord audioRecord;/ 录音对象private boolean isRecording = false;/ 标记是否正在录音中private int fre
13、quence = 8000;/ 采样率 8000定16private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 义采样通道(过时,但是使用其他的又不行private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定义音频编码( 位)private byte buffer = null;/ 录制的缓冲数组private File lastCacheFile = null;/ 记录上次录制的文件名private CommonSharedpreference
14、Helper commonSharedpreferenceHelper;private boolean shouldSaveAudio = false;/ 标记是否保存录音历史记录public AudioRecoderHandler(Context context) if (context = null) throw new RuntimeException(Context could not be null!); this.context = context; commonSharedpreferenceHelper = CommonSharedpreferenceHelper.getIns
15、tance(context);* 设置处理对象是否保存录音历史记录(如果设置为 false 表示不保存)* param shouldSaveAudionull*true 表示保存(默认) , false 不保存, onStopRecord 回调将会返回*/public void setShouldSaveAudio(boolean shouldSaveAudio) this.shouldSaveAudio = shouldSaveAudio;* 开始录制音频* param callBackListener* 录制过程中的回调函数*/public void startRecord(AudioRe
16、cordingCallback audioRecordingCallback) RecordTask task = new RecordTask(audioRecordingCallback); task.execute();/ 开始执行* 停止录制*/public void stoppRecord() isRecording = false;* 删除上次录制的文件(一般是用户取消发送导致删除上次录制的内容)* return true 表示删除成功, false 表示删除失败,一般是没有上次录制的文件,或者 文件已经被删除了*/public boolean deleteLastRecordFi
17、le() boolean ess = false;if (lastCacheFile != null & lastCacheFile.exists() success = lastCacheFile.delete();return success;* 获取音频文件的缓存地址 ( 获取的地址都是可以直接存储的,也就是文件夹已建立 好), 需要注意的是,缓存地址和用户的ID 有关* return 音频文件的缓存地址路径,如果获取失败,返回null*/private String getOutputDir() String path = null;File cacheFile = null; if
18、(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;/ 创建文件夹new
19、 File(path).mkdirs(); return path;* 录制音频的任务类* author 李长军*/private class RecordTask extends AsyncTask private AudioRecordingCallback audioRecordingCallback = null;public RecordTask(AudioRecordingCallback audioRecordingCallback) this.audioRecordingCallback = audioRecordingCallback;Overrideprotected vo
20、id onPreExecute() / 根据定义好的几个配置,来获取合适的缓冲大小/ int bufferSize = 800;int bufferSize = AudioRecord.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 实例化 AudioRecordaudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelInConfig, audioEncoding, bufferSize);/ 定义缓冲数组buffer
21、 = new bytebufferSize;audioRecord.startRecording();/ 开始录制isRecording = true;/ 设置录制标记为 trueOverrideprotected void onPostExecute(String result) audioRecord = null;if (result = null) lastCacheFile = null; else lastCacheFile = new File(result);if (audioRecordingCallback != null) audioRecordingCallback.o
22、nStopRecord(result);Overrideprotected String doInBackground(String. params) String cacheDir = getOutputDir();String tempFileName = null;File cacheFile = null;/ 输出的文件流DataOutputStream dataOutputStream = null;/ 如果设置了要保存历史录音文件,则 创建临时文件if (shouldSaveAudio & cacheDir != null) tempFileName = cacheDir + /
23、+ System.currentTimeMillis(); cacheFile = new File(tempFileName);try dataOutputStream = new DataOutputStream( new BufferedOutputStream(new FileOutputStream( cacheFile); catch (FileNotFoundException e) e.printStackTrace();/ 开始录制while (isRecording) / 录制的内容放置到了 buffer 中, result 代表存储长度 int result = audi
24、oRecord.read(buffer, 0, buffer.length);/ 如果设置需要保存录音文件if (shouldSaveAudio & dataOutputStream != null) for (int i = 0; i 0 ? 1 : 0;for (int i = 0; i result) length = result - i * MAX_DATA_LENGTH; audioRecordingCallback.onRecording(buffer, i* MAX_DA TA_LENGTH, length);if (audioRecord != null) audioReco
25、rd.stop();if (dataOutputStream != null) try dataOutputStream.close(); catch (IOException e) e.printStackTrace();return tempFileName;* 监听录制过程,用于实时获取录音数据* author 李长军*/ public static interface AudioRecordingCallback /* 录音数据获取回调* param data* 数据数组对象* param startIndex*数据其开始* param length*数据的结尾*/public voi
26、d onRecording(byte data, int startIndex, int length);* 录音结束后的回调* param savedPath* 录音文件存储的路径*/public void onStopRecord(String savedPath);* 释放资源*/public void release() if (audioRecord != null) audioRecord.release(); audioRecord = null;【声音播放的源码】/* 实时音频播放处理类 * 使用示例代码如下 :* * audioPlayerHandler = new Audi
27、oPlayerHandler();* audioPlayerHandler.prepare(); 播放前需要 prepare。可以重复 prepare* / 直接将需要播放的数据传入即可* audioPlayerHandler.onPlaying(data, 0, data.length);* * author 李长军*/SuppressWarnings(deprecation)public class AudioPlayerHandler implements Runnable private AudioTrack track = null;/ 录音文件播放对象private boolean
28、 isPlaying = false;/ 标记是否正在录音中private int frequence = 8000;/ 采样率 8000private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;/ 定 义采样通道(过时,但是使用其他的又不行private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;/ 定义音频编码( 16 位)private int bufferSize = -1;/ 播放缓冲大小/ 使用双缓冲机制private ByteArrayOut
29、putStream bufferStream0 = new ByteArrayOutputStream();private ByteArrayOutputStream bufferStream1 = new ByteArrayOutputStream();private int currentBuffer = -1;/ 记录当前哪个 buffer 填充完毕, 并正在播放中。 -1 表示都 没有。 0 表示第一个, 1 表示第二个/ 互斥信号量private Semaphore semaphore = new Semaphore(1);/ 是否释放资源的标志位private boolean re
30、lease = false;public AudioPlayerHandler() / 获取缓冲 大小bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig, audioEncoding);/ 实例 AudioTracktrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelInConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);try / 默认需要抢占一个信号量。防止播
31、放进程执行 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 length) if (AudioTrack.ERROR_BAD_V ALUE
32、 = bufferSize) / 初始化错误 return;switch (currentBuffer) case 0:bufferStream1.write(data, startIndex, length);/ 如果缓冲区不够大,暂时不往下执行if (bufferStream1.size() bufferSize) if (bufferStream0.size() bufferSize) if (bufferStream1.size() = 0) currentBuffer = 0; semaphore.release();break; default:break; * 准备播放*/public void prepare() if (track != null & !isPlaying) track.play(); i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 甘肃省陇南市文县三校联考2024~2025学年 高三下册三月联考数学试卷附解析
- 新外研版英语7年级上册全册教学课件
- 黑色金属冶炼市场趋势分析-洞察阐释
- 2024年昭通市消防救援支队招录政府专职消防员真题
- 2024年和田地区大学生乡村医生专项计划招聘真题
- 成都天府新区顾连禾泰康复医院招聘笔试真题2024
- 2024年广西英华国际职业学院辅导员考试真题
- 历史地理角色设计师基础知识点归纳
- 2025年二级建造师理论试题
- 参数化贝叶斯推断在生物学和医学中的应用-洞察阐释
- 2025年通信工程与技术考试试卷及答案
- 2024-2025学年下学期初中道德与法治七年级期末复习试卷(含答案)
- 2025年中国1,2-环氧丁烷行业市场规模调研及投资前景研究分析报告
- 2025年江苏南通市通州区八年级生物二模试卷
- 【变电站高压断路器和隔离开关的选择计算过程案例】2100字
- 2025年行政职业能力测验试卷及答案
- 道观庙宇托管协议书
- 2025年中国天然云母市场调查研究报告
- 2024北京朝阳区六年级毕业考英语试题及答案
- 关爱眼健康远离近视眼科普呵护眼睛让视界更精彩课件
- 【课件】跨学科实践:探索厨房中的物态变化问题(教学课件)初中物理人教版(2024)八年级上册
评论
0/150
提交评论